Merge V8 at r7668: Initial merge by Git.
Change-Id: I1703c8b4f5c63052451a22cf3fb878abc9a0ec75
diff --git a/ChangeLog b/ChangeLog
index b5b4423..cf9c96c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,60 @@
+2011-04-18: Version 3.3.0
+
+ Fixed bug in floating point rounding in Crankshaft on ARM
+ (issue 958)
+
+ Fixed a number of issues with running without VFPv3 support on ARM
+ (issue 1315)
+
+ Introduced v8Locale.Collator, a partial implementation of Collator
+ per last ECMAScript meeting + mailing list.
+
+ Minor performance improvements and bug fixes.
+
+
+2011-04-13: Version 3.2.10
+
+ Fixed bug in external float arrays on ARM (issue 1323).
+
+ Minor performance improvements and bug fixes.
+
+
+2011-04-11: Version 3.2.9
+
+ Removed support for ABI prior to EABI on ARM.
+
+ Fixed multiple crash bugs.
+
+ Added GCMole to the repository, a simple static analysis tool that
+ searches for GC-unsafe evaluation order dependent callsites.
+
+ Made preparser API be exported in shared libraries.
+
+ Fixed multiple issues in EcmaScript 5 strict mode implementation.
+
+ Fixed mutable __proto__ property if object is not extensible
+ (Issue 1309).
+
+ Fixed auto suspension of the sampler thread.
+
+
+2011-04-06: Version 3.2.8
+
+ Exposed WebGL typed array constructors in the shell sample.
+
+ Performance improvements on all platforms.
+
+
+2011-04-04: Version 3.2.7
+
+ Disabled the original 'classic' V8 code generator. Crankshaft is
+ now the default on all platforms.
+
+ Changed the heap profiler to use more descriptive names.
+
+ Performance and stability improvements to isolates on all platforms.
+
+
2011-03-30: Version 3.2.6
Fixed xcode build warning in shell.cc (out of order initialization).
diff --git a/SConstruct b/SConstruct
index 2287c80..d92dd02 100644
--- a/SConstruct
+++ b/SConstruct
@@ -27,6 +27,7 @@
import platform
import re
+import subprocess
import sys
import os
from os.path import join, dirname, abspath
@@ -145,6 +146,9 @@
# Use visibility=default to disable this.
'CXXFLAGS': ['-fvisibility=hidden']
},
+ 'strictaliasing:off': {
+ 'CCFLAGS': ['-fno-strict-aliasing']
+ },
'mode:debug': {
'CCFLAGS': ['-g', '-O0'],
'CPPDEFINES': ['ENABLE_DISASSEMBLER', 'DEBUG'],
@@ -826,8 +830,16 @@
sys.exit(1)
-def GuessToolchain(os):
- tools = Environment()['TOOLS']
+def GuessOS(env):
+ return utils.GuessOS()
+
+
+def GuessArch(env):
+ return utils.GuessArchitecture()
+
+
+def GuessToolchain(env):
+ tools = env['TOOLS']
if 'gcc' in tools:
return 'gcc'
elif 'msvc' in tools:
@@ -836,7 +848,9 @@
return None
-def GuessVisibility(os, toolchain):
+def GuessVisibility(env):
+ os = env['os']
+ toolchain = env['toolchain'];
if (os == 'win32' or os == 'cygwin') and toolchain == 'gcc':
# MinGW / Cygwin can't do it.
return 'default'
@@ -846,28 +860,41 @@
return 'hidden'
-OS_GUESS = utils.GuessOS()
-TOOLCHAIN_GUESS = GuessToolchain(OS_GUESS)
-ARCH_GUESS = utils.GuessArchitecture()
-VISIBILITY_GUESS = GuessVisibility(OS_GUESS, TOOLCHAIN_GUESS)
+def GuessStrictAliasing(env):
+ # There seems to be a problem with gcc 4.5.x.
+ # See http://code.google.com/p/v8/issues/detail?id=884
+ # It can be worked around by disabling strict aliasing.
+ toolchain = env['toolchain'];
+ if toolchain == 'gcc':
+ env = Environment(tools=['gcc'])
+ # The gcc version should be available in env['CCVERSION'],
+ # but when scons detects msvc this value is not set.
+ version = subprocess.Popen([env['CC'], '-dumpversion'],
+ stdout=subprocess.PIPE).communicate()[0]
+ if version.find('4.5') == 0:
+ return 'off'
+ return 'default'
-SIMPLE_OPTIONS = {
- 'toolchain': {
- 'values': ['gcc', 'msvc'],
- 'default': TOOLCHAIN_GUESS,
- 'help': 'the toolchain to use (%s)' % TOOLCHAIN_GUESS
+PLATFORM_OPTIONS = {
+ 'arch': {
+ 'values': ['arm', 'ia32', 'x64', 'mips'],
+ 'guess': GuessArch,
+ 'help': 'the architecture to build for'
},
'os': {
'values': ['freebsd', 'linux', 'macos', 'win32', 'android', 'openbsd', 'solaris', 'cygwin'],
- 'default': OS_GUESS,
- 'help': 'the os to build for (%s)' % OS_GUESS
+ 'guess': GuessOS,
+ 'help': 'the os to build for'
},
- 'arch': {
- 'values':['arm', 'ia32', 'x64', 'mips'],
- 'default': ARCH_GUESS,
- 'help': 'the architecture to build for (%s)' % ARCH_GUESS
- },
+ 'toolchain': {
+ 'values': ['gcc', 'msvc'],
+ 'guess': GuessToolchain,
+ 'help': 'the toolchain to use'
+ }
+}
+
+SIMPLE_OPTIONS = {
'regexp': {
'values': ['native', 'interpreted'],
'default': 'native',
@@ -981,8 +1008,13 @@
},
'visibility': {
'values': ['default', 'hidden'],
- 'default': VISIBILITY_GUESS,
- 'help': 'shared library symbol visibility (%s)' % VISIBILITY_GUESS
+ 'guess': GuessVisibility,
+ 'help': 'shared library symbol visibility'
+ },
+ 'strictaliasing': {
+ 'values': ['default', 'off'],
+ 'guess': GuessStrictAliasing,
+ 'help': 'assume strict aliasing while optimizing'
},
'pgo': {
'values': ['off', 'instrument', 'optimize'],
@@ -1001,6 +1033,22 @@
}
}
+ALL_OPTIONS = dict(PLATFORM_OPTIONS, **SIMPLE_OPTIONS)
+
+
+def AddOptions(options, result):
+ guess_env = Environment(options=result)
+ for (name, option) in options.iteritems():
+ if 'guess' in option:
+ # Option has a guess function
+ guess = option.get('guess')
+ default = guess(guess_env)
+ else:
+ # Option has a fixed default
+ default = option.get('default')
+ help = '%s (%s)' % (option.get('help'), ", ".join(option['values']))
+ result.Add(name, help, default)
+
def GetOptions():
result = Options()
@@ -1009,12 +1057,23 @@
result.Add('cache', 'directory to use for scons build cache', '')
result.Add('env', 'override environment settings (NAME0:value0,NAME1:value1,...)', '')
result.Add('importenv', 'import environment settings (NAME0,NAME1,...)', '')
- for (name, option) in SIMPLE_OPTIONS.iteritems():
- help = '%s (%s)' % (name, ", ".join(option['values']))
- result.Add(name, help, option.get('default'))
+ AddOptions(PLATFORM_OPTIONS, result)
+ AddOptions(SIMPLE_OPTIONS, result)
return result
+def GetTools(opts):
+ env = Environment(options=opts)
+ os = env['os']
+ toolchain = env['toolchain']
+ if os == 'win32' and toolchain == 'gcc':
+ return ['mingw']
+ elif os == 'win32' and toolchain == 'msvc':
+ return ['msvc', 'mslink', 'mslib', 'msvs']
+ else:
+ return ['default']
+
+
def GetVersionComponents():
MAJOR_VERSION_PATTERN = re.compile(r"#define\s+MAJOR_VERSION\s+(.*)")
MINOR_VERSION_PATTERN = re.compile(r"#define\s+MINOR_VERSION\s+(.*)")
@@ -1094,8 +1153,8 @@
print env['arch']
print env['simulator']
Abort("Option unalignedaccesses only supported for the ARM architecture.")
- for (name, option) in SIMPLE_OPTIONS.iteritems():
- if (not option.get('default')) and (name not in ARGUMENTS):
+ for (name, option) in ALL_OPTIONS.iteritems():
+ if (not name in env):
message = ("A value for option %s must be specified (%s)." %
(name, ", ".join(option['values'])))
Abort(message)
@@ -1225,9 +1284,9 @@
return overrides
-def BuildSpecific(env, mode, env_overrides):
+def BuildSpecific(env, mode, env_overrides, tools):
options = {'mode': mode}
- for option in SIMPLE_OPTIONS:
+ for option in ALL_OPTIONS:
options[option] = env[option]
PostprocessOptions(options, env['os'])
@@ -1281,7 +1340,7 @@
(object_files, shell_files, mksnapshot, preparser_files) = env.SConscript(
join('src', 'SConscript'),
build_dir=join('obj', target_id),
- exports='context',
+ exports='context tools',
duplicate=False
)
@@ -1308,21 +1367,21 @@
context.library_targets.append(library)
context.library_targets.append(preparser_library)
- d8_env = Environment()
+ d8_env = Environment(tools=tools)
d8_env.Replace(**context.flags['d8'])
context.ApplyEnvOverrides(d8_env)
shell = d8_env.Program('d8' + suffix, object_files + shell_files)
context.d8_targets.append(shell)
for sample in context.samples:
- sample_env = Environment()
+ sample_env = Environment(tools=tools)
sample_env.Replace(**context.flags['sample'])
sample_env.Prepend(LIBS=[library_name])
context.ApplyEnvOverrides(sample_env)
sample_object = sample_env.SConscript(
join('samples', 'SConscript'),
build_dir=join('obj', 'sample', sample, target_id),
- exports='sample context',
+ exports='sample context tools',
duplicate=False
)
sample_name = sample + suffix
@@ -1335,7 +1394,7 @@
cctest_program = cctest_env.SConscript(
join('test', 'cctest', 'SConscript'),
build_dir=join('obj', 'test', target_id),
- exports='context object_files',
+ exports='context object_files tools',
duplicate=False
)
context.cctest_targets.append(cctest_program)
@@ -1350,7 +1409,7 @@
exports='context',
duplicate=False
)
- preparser_name = join('obj', 'preparser', target_id, 'preparser' + suffix)
+ preparser_name = join('obj', 'preparser', target_id, 'preparser')
preparser_program = preparser_env.Program(preparser_name, preparser_object);
preparser_env.Depends(preparser_program, preparser_library)
context.preparser_targets.append(preparser_program)
@@ -1360,7 +1419,9 @@
def Build():
opts = GetOptions()
- env = Environment(options=opts)
+ tools = GetTools(opts)
+ env = Environment(options=opts, tools=tools)
+
Help(opts.GenerateHelpText(env))
VerifyOptions(env)
env_overrides = ParseEnvOverrides(env['env'], env['importenv'])
@@ -1375,7 +1436,7 @@
d8s = []
modes = SplitList(env['mode'])
for mode in modes:
- context = BuildSpecific(env.Copy(), mode, env_overrides)
+ context = BuildSpecific(env.Copy(), mode, env_overrides, tools)
libraries += context.library_targets
mksnapshots += context.mksnapshot_targets
cctests += context.cctest_targets
diff --git a/include/v8-preparser.h b/include/v8-preparser.h
index 9425f7d..4d46bad 100644
--- a/include/v8-preparser.h
+++ b/include/v8-preparser.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -67,16 +67,16 @@
namespace v8 {
-class PreParserData {
+class V8EXPORT PreParserData {
public:
PreParserData(size_t size, const uint8_t* data)
: data_(data), size_(size) { }
// Create a PreParserData value where stack_overflow reports true.
- static PreParserData StackOverflow() { return PreParserData(NULL, 0); }
+ static PreParserData StackOverflow() { return PreParserData(0, NULL); }
+
// Whether the pre-parser stopped due to a stack overflow.
// If this is the case, size() and data() should not be used.
-
bool stack_overflow() { return size_ == 0u; }
// The size of the data in bytes.
@@ -92,7 +92,7 @@
// Interface for a stream of Unicode characters.
-class UnicodeInputStream {
+class V8EXPORT UnicodeInputStream { // NOLINT - Thinks V8EXPORT is class name.
public:
virtual ~UnicodeInputStream();
diff --git a/include/v8.h b/include/v8.h
index 62d1085..d15d024 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -1653,6 +1653,11 @@
V8EXPORT Local<Object> Clone();
/**
+ * Returns the context in which the object was created.
+ */
+ V8EXPORT Local<Context> CreationContext();
+
+ /**
* Set the backing store of the indexed properties to be managed by the
* embedding layer. Access to the indexed properties will follow the rules
* spelled out in CanvasPixelArray.
@@ -3291,7 +3296,7 @@
*/
class Scope {
public:
- inline Scope(Handle<Context> context) : context_(context) {
+ explicit inline Scope(Handle<Context> context) : context_(context) {
context_->Enter();
}
inline ~Scope() { context_->Exit(); }
diff --git a/preparser/preparser-process.cc b/preparser/preparser-process.cc
index 26dfc42..fb6e386 100644
--- a/preparser/preparser-process.cc
+++ b/preparser/preparser-process.cc
@@ -27,105 +27,64 @@
#include <stdlib.h>
#include <stdarg.h>
+#include <stdio.h>
+
#include "../include/v8stdint.h"
#include "../include/v8-preparser.h"
-#include "unicode-inl.h"
-enum ResultCode { kSuccess = 0, kErrorReading = 1, kErrorWriting = 2 };
-
-namespace v8 {
-namespace internal {
-
-// THIS FILE IS PROOF-OF-CONCEPT ONLY.
-// The final goal is a stand-alone preparser library.
+// This file is only used for testing the stand-alone preparser
+// library.
+// The first (and only) argument must be the path of a JavaScript file.
+// This file is preparsed and the resulting preparser data is written
+// to stdout. Diagnostic output is output on stderr.
+// The file must contain only ASCII characters (UTF-8 isn't supported).
+// The file is read into memory, so it should have a reasonable size.
-class UTF8InputStream : public v8::UnicodeInputStream {
+// Adapts an ASCII string to the UnicodeInputStream interface.
+class AsciiInputStream : public v8::UnicodeInputStream {
public:
- UTF8InputStream(uint8_t* buffer, size_t length)
+ AsciiInputStream(uint8_t* buffer, size_t length)
: buffer_(buffer),
- offset_(0),
- pos_(0),
- end_offset_(static_cast<int>(length)) { }
+ end_offset_(static_cast<int>(length)),
+ offset_(0) { }
- virtual ~UTF8InputStream() { }
+ virtual ~AsciiInputStream() { }
virtual void PushBack(int32_t ch) {
- // Pushback assumes that the character pushed back is the
- // one that was most recently read, and jumps back in the
- // UTF-8 stream by the length of that character's encoding.
- offset_ -= unibrow::Utf8::Length(ch);
- pos_--;
+ offset_--;
#ifdef DEBUG
- if (static_cast<unsigned>(ch) <= unibrow::Utf8::kMaxOneByteChar) {
- if (ch != buffer_[offset_]) {
- fprintf(stderr, "Invalid pushback: '%c'.", ch);
- exit(1);
- }
- } else {
- unsigned tmp = 0;
- if (static_cast<unibrow::uchar>(ch) !=
- unibrow::Utf8::CalculateValue(buffer_ + offset_,
- end_offset_ - offset_,
- &tmp)) {
- fprintf(stderr, "Invalid pushback: 0x%x.", ch);
- exit(1);
- }
+ if (offset_ < 0 ||
+ (ch != ((offset_ >= end_offset_) ? -1 : buffer_[offset_]))) {
+ fprintf(stderr, "Invalid pushback: '%c' at offset %d.", ch, offset_);
+ exit(1);
}
#endif
}
virtual int32_t Next() {
- if (offset_ == end_offset_) return -1;
- uint8_t first_char = buffer_[offset_];
- if (first_char <= unibrow::Utf8::kMaxOneByteChar) {
- pos_++;
- offset_++;
- return static_cast<int32_t>(first_char);
+ if (offset_ >= end_offset_) {
+ offset_++; // Increment anyway to allow symmetric pushbacks.
+ return -1;
}
- unibrow::uchar codepoint =
- unibrow::Utf8::CalculateValue(buffer_ + offset_,
- end_offset_ - offset_,
- &offset_);
- pos_++;
- return static_cast<int32_t>(codepoint);
+ uint8_t next_char = buffer_[offset_];
+#ifdef DEBUG
+ if (next_char > 0x7fu) {
+ fprintf(stderr, "Non-ASCII character in input: '%c'.", next_char);
+ exit(1);
+ }
+#endif
+ offset_++;
+ return static_cast<int32_t>(next_char);
}
private:
const uint8_t* buffer_;
- unsigned offset_;
- unsigned pos_;
- unsigned end_offset_;
+ const int end_offset_;
+ int offset_;
};
-// Write a number to dest in network byte order.
-void WriteUInt32(FILE* dest, uint32_t value, bool* ok) {
- for (int i = 3; i >= 0; i--) {
- uint8_t byte = static_cast<uint8_t>(value >> (i << 3));
- int result = fputc(byte, dest);
- if (result == EOF) {
- *ok = false;
- return;
- }
- }
-}
-
-// Read number from FILE* in network byte order.
-uint32_t ReadUInt32(FILE* source, bool* ok) {
- uint32_t n = 0;
- for (int i = 0; i < 4; i++) {
- int c = fgetc(source);
- if (c == EOF) {
- *ok = false;
- return 0;
- }
- n = (n << 8) + static_cast<uint32_t>(c);
- }
- return n;
-}
-
-
bool ReadBuffer(FILE* source, void* buffer, size_t length) {
size_t actually_read = fread(buffer, 1, length, source);
return (actually_read == length);
@@ -150,57 +109,61 @@
};
-// Preparse input and output result on stdout.
-int PreParseIO(FILE* input) {
- fprintf(stderr, "LOG: Enter parsing loop\n");
- bool ok = true;
- uint32_t length = ReadUInt32(input, &ok);
- fprintf(stderr, "LOG: Input length: %d\n", length);
- if (!ok) return kErrorReading;
- ScopedPointer<uint8_t> buffer(new uint8_t[length]);
-
- if (!ReadBuffer(input, *buffer, length)) {
- return kErrorReading;
- }
- UTF8InputStream input_buffer(*buffer, static_cast<size_t>(length));
-
- v8::PreParserData data =
- v8::Preparse(&input_buffer, 64 * 1024 * sizeof(void*)); // NOLINT
- if (data.stack_overflow()) {
- fprintf(stderr, "LOG: Stack overflow\n");
+int main(int argc, char* argv[]) {
+ // Check for filename argument.
+ if (argc < 2) {
+ fprintf(stderr, "ERROR: No filename on command line.\n");
fflush(stderr);
- // Report stack overflow error/no-preparser-data.
- WriteUInt32(stdout, 0, &ok);
- if (!ok) return kErrorWriting;
- return 0;
+ return EXIT_FAILURE;
+ }
+ const char* filename = argv[1];
+
+ // Open JS file.
+ FILE* input = fopen(filename, "rb");
+ if (input == NULL) {
+ perror("ERROR: Error opening file");
+ fflush(stderr);
+ return EXIT_FAILURE;
}
+ // Find length of JS file.
+ if (fseek(input, 0, SEEK_END) != 0) {
+ perror("ERROR: Error during seek");
+ fflush(stderr);
+ return EXIT_FAILURE;
+ }
+ size_t length = static_cast<size_t>(ftell(input));
+ rewind(input);
+
+ // Read JS file into memory buffer.
+ ScopedPointer<uint8_t> buffer(new uint8_t[length]);
+ if (!ReadBuffer(input, *buffer, length)) {
+ perror("ERROR: Reading file");
+ fflush(stderr);
+ return EXIT_FAILURE;
+ }
+ fclose(input);
+
+ // Preparse input file.
+ AsciiInputStream input_buffer(*buffer, length);
+ size_t kMaxStackSize = 64 * 1024 * sizeof(void*); // NOLINT
+ v8::PreParserData data = v8::Preparse(&input_buffer, kMaxStackSize);
+
+ // Fail if stack overflow.
+ if (data.stack_overflow()) {
+ fprintf(stderr, "ERROR: Stack overflow\n");
+ fflush(stderr);
+ return EXIT_FAILURE;
+ }
+
+ // Print preparser data to stdout.
uint32_t size = data.size();
fprintf(stderr, "LOG: Success, data size: %u\n", size);
fflush(stderr);
- WriteUInt32(stdout, size, &ok);
- if (!ok) return kErrorWriting;
if (!WriteBuffer(stdout, data.data(), size)) {
- return kErrorWriting;
+ perror("ERROR: Writing data");
+ return EXIT_FAILURE;
}
- return 0;
-}
-} } // namespace v8::internal
-
-
-int main(int argc, char* argv[]) {
- FILE* input = stdin;
- if (argc > 1) {
- char* arg = argv[1];
- input = fopen(arg, "rb");
- if (input == NULL) return EXIT_FAILURE;
- }
- int status = 0;
- do {
- status = v8::internal::PreParseIO(input);
- } while (status == 0);
- fprintf(stderr, "EXIT: Failure %d\n", status);
- fflush(stderr);
- return EXIT_FAILURE;
+ return EXIT_SUCCESS;
}
diff --git a/samples/shell.cc b/samples/shell.cc
index 0710d46..222eeda 100644
--- a/samples/shell.cc
+++ b/samples/shell.cc
@@ -1,4 +1,4 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -65,6 +65,14 @@
v8::Handle<v8::Value> Load(const v8::Arguments& args);
v8::Handle<v8::Value> Quit(const v8::Arguments& args);
v8::Handle<v8::Value> Version(const v8::Arguments& args);
+v8::Handle<v8::Value> Int8Array(const v8::Arguments& args);
+v8::Handle<v8::Value> Uint8Array(const v8::Arguments& args);
+v8::Handle<v8::Value> Int16Array(const v8::Arguments& args);
+v8::Handle<v8::Value> Uint16Array(const v8::Arguments& args);
+v8::Handle<v8::Value> Int32Array(const v8::Arguments& args);
+v8::Handle<v8::Value> Uint32Array(const v8::Arguments& args);
+v8::Handle<v8::Value> Float32Array(const v8::Arguments& args);
+v8::Handle<v8::Value> PixelArray(const v8::Arguments& args);
v8::Handle<v8::String> ReadFile(const char* name);
void ReportException(v8::TryCatch* handler);
@@ -112,6 +120,7 @@
v8::Handle<v8::String> source = ReadFile(arg);
if (source.IsEmpty()) {
printf("Error reading '%s'\n", arg);
+ continue;
}
if (!ExecuteString(source, file_name, false, true)) {
ExitShell(1);
@@ -334,6 +343,25 @@
global->Set(v8::String::New("quit"), v8::FunctionTemplate::New(Quit));
// Bind the 'version' function
global->Set(v8::String::New("version"), v8::FunctionTemplate::New(Version));
+
+ // Bind the handlers for external arrays.
+ global->Set(v8::String::New("Int8Array"),
+ v8::FunctionTemplate::New(Int8Array));
+ global->Set(v8::String::New("Uint8Array"),
+ v8::FunctionTemplate::New(Uint8Array));
+ global->Set(v8::String::New("Int16Array"),
+ v8::FunctionTemplate::New(Int16Array));
+ global->Set(v8::String::New("Uint16Array"),
+ v8::FunctionTemplate::New(Uint16Array));
+ global->Set(v8::String::New("Int32Array"),
+ v8::FunctionTemplate::New(Int32Array));
+ global->Set(v8::String::New("Uint32Array"),
+ v8::FunctionTemplate::New(Uint32Array));
+ global->Set(v8::String::New("Float32Array"),
+ v8::FunctionTemplate::New(Float32Array));
+ global->Set(v8::String::New("PixelArray"),
+ v8::FunctionTemplate::New(PixelArray));
+
return v8::Context::New(NULL, global);
}
@@ -417,6 +445,78 @@
}
+void ExternalArrayWeakCallback(v8::Persistent<v8::Value> object, void* data) {
+ free(data);
+ object.Dispose();
+}
+
+
+v8::Handle<v8::Value> CreateExternalArray(const v8::Arguments& args,
+ v8::ExternalArrayType type,
+ int element_size) {
+ if (args.Length() != 1) {
+ return v8::ThrowException(
+ v8::String::New("Array constructor needs one parameter."));
+ }
+ int length = args[0]->Int32Value();
+ void* data = malloc(length * element_size);
+ memset(data, 0, length * element_size);
+ v8::Handle<v8::Object> array = v8::Object::New();
+ v8::Persistent<v8::Object> persistent_array =
+ v8::Persistent<v8::Object>::New(array);
+ persistent_array.MakeWeak(data, ExternalArrayWeakCallback);
+ array->SetIndexedPropertiesToExternalArrayData(data, type, length);
+ array->Set(v8::String::New("length"), v8::Int32::New(length),
+ v8::ReadOnly);
+ array->Set(v8::String::New("BYTES_PER_ELEMENT"),
+ v8::Int32::New(element_size));
+ return array;
+}
+
+
+v8::Handle<v8::Value> Int8Array(const v8::Arguments& args) {
+ return CreateExternalArray(args, v8::kExternalByteArray, sizeof(int8_t));
+}
+
+
+v8::Handle<v8::Value> Uint8Array(const v8::Arguments& args) {
+ return CreateExternalArray(args, v8::kExternalUnsignedByteArray,
+ sizeof(uint8_t));
+}
+
+
+v8::Handle<v8::Value> Int16Array(const v8::Arguments& args) {
+ return CreateExternalArray(args, v8::kExternalShortArray, sizeof(int16_t));
+}
+
+
+v8::Handle<v8::Value> Uint16Array(const v8::Arguments& args) {
+ return CreateExternalArray(args, v8::kExternalUnsignedShortArray,
+ sizeof(uint16_t));
+}
+
+v8::Handle<v8::Value> Int32Array(const v8::Arguments& args) {
+ return CreateExternalArray(args, v8::kExternalIntArray, sizeof(int32_t));
+}
+
+
+v8::Handle<v8::Value> Uint32Array(const v8::Arguments& args) {
+ return CreateExternalArray(args, v8::kExternalUnsignedIntArray,
+ sizeof(uint32_t));
+}
+
+
+v8::Handle<v8::Value> Float32Array(const v8::Arguments& args) {
+ return CreateExternalArray(args, v8::kExternalFloatArray,
+ sizeof(float)); // NOLINT
+}
+
+
+v8::Handle<v8::Value> PixelArray(const v8::Arguments& args) {
+ return CreateExternalArray(args, v8::kExternalPixelArray, sizeof(uint8_t));
+}
+
+
// Reads a file into a v8 string.
v8::Handle<v8::String> ReadFile(const char* name) {
FILE* file = fopen(name, "rb");
diff --git a/src/SConscript b/src/SConscript
index a740584..417e283 100755
--- a/src/SConscript
+++ b/src/SConscript
@@ -31,6 +31,7 @@
sys.path.append(join(root_dir, 'tools'))
import js2c
Import('context')
+Import('tools')
SOURCES = {
@@ -86,7 +87,6 @@
interpreter-irregexp.cc
isolate.cc
jsregexp.cc
- jump-target.cc
lithium-allocator.cc
lithium.cc
liveedit.cc
@@ -106,7 +106,6 @@
regexp-macro-assembler-irregexp.cc
regexp-macro-assembler.cc
regexp-stack.cc
- register-allocator.cc
rewriter.cc
runtime.cc
runtime-profiler.cc
@@ -132,14 +131,11 @@
v8threads.cc
variables.cc
version.cc
- virtual-frame.cc
zone.cc
extensions/gc-extension.cc
extensions/externalize-string-extension.cc
"""),
'arch:arm': Split("""
- jump-target-light.cc
- virtual-frame-light.cc
arm/builtins-arm.cc
arm/code-stubs-arm.cc
arm/codegen-arm.cc
@@ -151,20 +147,15 @@
arm/frames-arm.cc
arm/full-codegen-arm.cc
arm/ic-arm.cc
- arm/jump-target-arm.cc
arm/lithium-arm.cc
arm/lithium-codegen-arm.cc
arm/lithium-gap-resolver-arm.cc
arm/macro-assembler-arm.cc
arm/regexp-macro-assembler-arm.cc
- arm/register-allocator-arm.cc
arm/stub-cache-arm.cc
- arm/virtual-frame-arm.cc
arm/assembler-arm.cc
"""),
'arch:mips': Split("""
- jump-target-light.cc
- virtual-frame-light.cc
mips/assembler-mips.cc
mips/builtins-mips.cc
mips/code-stubs-mips.cc
@@ -177,16 +168,11 @@
mips/frames-mips.cc
mips/full-codegen-mips.cc
mips/ic-mips.cc
- mips/jump-target-mips.cc
mips/macro-assembler-mips.cc
mips/regexp-macro-assembler-mips.cc
- mips/register-allocator-mips.cc
mips/stub-cache-mips.cc
- mips/virtual-frame-mips.cc
"""),
'arch:ia32': Split("""
- jump-target-heavy.cc
- virtual-frame-heavy.cc
ia32/assembler-ia32.cc
ia32/builtins-ia32.cc
ia32/code-stubs-ia32.cc
@@ -198,19 +184,14 @@
ia32/frames-ia32.cc
ia32/full-codegen-ia32.cc
ia32/ic-ia32.cc
- ia32/jump-target-ia32.cc
ia32/lithium-codegen-ia32.cc
ia32/lithium-gap-resolver-ia32.cc
ia32/lithium-ia32.cc
ia32/macro-assembler-ia32.cc
ia32/regexp-macro-assembler-ia32.cc
- ia32/register-allocator-ia32.cc
ia32/stub-cache-ia32.cc
- ia32/virtual-frame-ia32.cc
"""),
'arch:x64': Split("""
- jump-target-heavy.cc
- virtual-frame-heavy.cc
x64/assembler-x64.cc
x64/builtins-x64.cc
x64/code-stubs-x64.cc
@@ -222,15 +203,12 @@
x64/frames-x64.cc
x64/full-codegen-x64.cc
x64/ic-x64.cc
- x64/jump-target-x64.cc
x64/lithium-codegen-x64.cc
x64/lithium-gap-resolver-x64.cc
x64/lithium-x64.cc
x64/macro-assembler-x64.cc
x64/regexp-macro-assembler-x64.cc
- x64/register-allocator-x64.cc
x64/stub-cache-x64.cc
- x64/virtual-frame-x64.cc
"""),
'simulator:arm': ['arm/simulator-arm.cc'],
'simulator:mips': ['mips/simulator-mips.cc'],
@@ -319,13 +297,18 @@
'''.split()
+EXPERIMENTAL_LIBRARY_FILES = '''
+proxy.js
+'''.split()
+
+
def Abort(message):
print message
sys.exit(1)
def ConfigureObjectFiles():
- env = Environment()
+ env = Environment(tools=tools)
env.Replace(**context.flags['v8'])
context.ApplyEnvOverrides(env)
env['BUILDERS']['JS2C'] = Builder(action=js2c.JS2C)
@@ -346,9 +329,16 @@
# compile it.
library_files = [s for s in LIBRARY_FILES]
library_files.append('macros.py')
- libraries_src, libraries_empty_src = env.JS2C(['libraries.cc', 'libraries-empty.cc'], library_files, TYPE='CORE')
+ libraries_src = env.JS2C(['libraries.cc'], library_files, TYPE='CORE')
libraries_obj = context.ConfigureObject(env, libraries_src, CPPPATH=['.'])
+ # Combine the experimental JavaScript library files into a C++ file
+ # and compile it.
+ experimental_library_files = [ s for s in EXPERIMENTAL_LIBRARY_FILES ]
+ experimental_library_files.append('macros.py')
+ experimental_libraries_src = env.JS2C(['experimental-libraries.cc'], experimental_library_files, TYPE='EXPERIMENTAL')
+ experimental_libraries_obj = context.ConfigureObject(env, experimental_libraries_src, CPPPATH=['.'])
+
source_objs = context.ConfigureObject(env, source_files)
non_snapshot_files = [source_objs]
@@ -365,7 +355,7 @@
mksnapshot_env = env.Copy()
mksnapshot_env.Replace(**context.flags['mksnapshot'])
mksnapshot_src = 'mksnapshot.cc'
- mksnapshot = mksnapshot_env.Program('mksnapshot', [mksnapshot_src, libraries_obj, non_snapshot_files, empty_snapshot_obj], PDB='mksnapshot.exe.pdb')
+ mksnapshot = mksnapshot_env.Program('mksnapshot', [mksnapshot_src, libraries_obj, experimental_libraries_obj, non_snapshot_files, empty_snapshot_obj], PDB='mksnapshot.exe.pdb')
if context.use_snapshot:
if context.build_snapshot:
snapshot_cc = env.Snapshot('snapshot.cc', mksnapshot, LOGFILE=File('snapshot.log').abspath)
@@ -374,7 +364,7 @@
snapshot_obj = context.ConfigureObject(env, snapshot_cc, CPPPATH=['.'])
else:
snapshot_obj = empty_snapshot_obj
- library_objs = [non_snapshot_files, libraries_obj, snapshot_obj]
+ library_objs = [non_snapshot_files, libraries_obj, experimental_libraries_obj, snapshot_obj]
return (library_objs, d8_objs, [mksnapshot], preparser_objs)
diff --git a/src/accessors.cc b/src/accessors.cc
index e33b4d7..5f9bf74 100644
--- a/src/accessors.cc
+++ b/src/accessors.cc
@@ -568,172 +568,6 @@
// Accessors::FunctionArguments
//
-static Address SlotAddress(JavaScriptFrame* frame, int slot_index) {
- if (slot_index >= 0) {
- const int offset = JavaScriptFrameConstants::kLocal0Offset;
- return frame->fp() + offset - (slot_index * kPointerSize);
- } else {
- const int offset = JavaScriptFrameConstants::kReceiverOffset;
- return frame->caller_sp() + offset + (slot_index * kPointerSize);
- }
-}
-
-
-// We can't intermix stack decoding and allocations because
-// deoptimization infrastracture is not GC safe.
-// Thus we build a temporary structure in malloced space.
-class SlotRef BASE_EMBEDDED {
- public:
- enum SlotRepresentation {
- UNKNOWN,
- TAGGED,
- INT32,
- DOUBLE,
- LITERAL
- };
-
- SlotRef()
- : addr_(NULL), representation_(UNKNOWN) { }
-
- SlotRef(Address addr, SlotRepresentation representation)
- : addr_(addr), representation_(representation) { }
-
- explicit SlotRef(Object* literal)
- : literal_(literal), representation_(LITERAL) { }
-
- Handle<Object> GetValue() {
- switch (representation_) {
- case TAGGED:
- return Handle<Object>(Memory::Object_at(addr_));
-
- case INT32: {
- int value = Memory::int32_at(addr_);
- if (Smi::IsValid(value)) {
- return Handle<Object>(Smi::FromInt(value));
- } else {
- return Isolate::Current()->factory()->NewNumberFromInt(value);
- }
- }
-
- case DOUBLE: {
- double value = Memory::double_at(addr_);
- return Isolate::Current()->factory()->NewNumber(value);
- }
-
- case LITERAL:
- return literal_;
-
- default:
- UNREACHABLE();
- return Handle<Object>::null();
- }
- }
-
- private:
- Address addr_;
- Handle<Object> literal_;
- SlotRepresentation representation_;
-};
-
-
-static SlotRef ComputeSlotForNextArgument(TranslationIterator* iterator,
- DeoptimizationInputData* data,
- JavaScriptFrame* frame) {
- Translation::Opcode opcode =
- static_cast<Translation::Opcode>(iterator->Next());
-
- switch (opcode) {
- case Translation::BEGIN:
- case Translation::FRAME:
- // Peeled off before getting here.
- break;
-
- case Translation::ARGUMENTS_OBJECT:
- // This can be only emitted for local slots not for argument slots.
- break;
-
- case Translation::REGISTER:
- case Translation::INT32_REGISTER:
- case Translation::DOUBLE_REGISTER:
- case Translation::DUPLICATE:
- // We are at safepoint which corresponds to call. All registers are
- // saved by caller so there would be no live registers at this
- // point. Thus these translation commands should not be used.
- break;
-
- case Translation::STACK_SLOT: {
- int slot_index = iterator->Next();
- Address slot_addr = SlotAddress(frame, slot_index);
- return SlotRef(slot_addr, SlotRef::TAGGED);
- }
-
- case Translation::INT32_STACK_SLOT: {
- int slot_index = iterator->Next();
- Address slot_addr = SlotAddress(frame, slot_index);
- return SlotRef(slot_addr, SlotRef::INT32);
- }
-
- case Translation::DOUBLE_STACK_SLOT: {
- int slot_index = iterator->Next();
- Address slot_addr = SlotAddress(frame, slot_index);
- return SlotRef(slot_addr, SlotRef::DOUBLE);
- }
-
- case Translation::LITERAL: {
- int literal_index = iterator->Next();
- return SlotRef(data->LiteralArray()->get(literal_index));
- }
- }
-
- UNREACHABLE();
- return SlotRef();
-}
-
-
-
-
-
-static void ComputeSlotMappingForArguments(JavaScriptFrame* frame,
- int inlined_frame_index,
- Vector<SlotRef>* args_slots) {
- AssertNoAllocation no_gc;
- int deopt_index = AstNode::kNoNumber;
- DeoptimizationInputData* data =
- static_cast<OptimizedFrame*>(frame)->GetDeoptimizationData(&deopt_index);
- TranslationIterator it(data->TranslationByteArray(),
- data->TranslationIndex(deopt_index)->value());
- Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
- ASSERT(opcode == Translation::BEGIN);
- int frame_count = it.Next();
- USE(frame_count);
- ASSERT(frame_count > inlined_frame_index);
- int frames_to_skip = inlined_frame_index;
- while (true) {
- opcode = static_cast<Translation::Opcode>(it.Next());
- // Skip over operands to advance to the next opcode.
- it.Skip(Translation::NumberOfOperandsFor(opcode));
- if (opcode == Translation::FRAME) {
- if (frames_to_skip == 0) {
- // We reached the frame corresponding to the inlined function
- // in question. Process the translation commands for the
- // arguments.
- //
- // Skip the translation command for the receiver.
- it.Skip(Translation::NumberOfOperandsFor(
- static_cast<Translation::Opcode>(it.Next())));
- // Compute slots for arguments.
- for (int i = 0; i < args_slots->length(); ++i) {
- (*args_slots)[i] = ComputeSlotForNextArgument(&it, data, frame);
- }
- return;
- }
- frames_to_skip--;
- }
- }
-
- UNREACHABLE();
-}
-
static MaybeObject* ConstructArgumentsObjectForInlinedFunction(
JavaScriptFrame* frame,
@@ -742,7 +576,9 @@
Factory* factory = Isolate::Current()->factory();
int args_count = inlined_function->shared()->formal_parameter_count();
ScopedVector<SlotRef> args_slots(args_count);
- ComputeSlotMappingForArguments(frame, inlined_frame_index, &args_slots);
+ SlotRef::ComputeSlotMappingForArguments(frame,
+ inlined_frame_index,
+ &args_slots);
Handle<JSObject> arguments =
factory->NewArgumentsObject(inlined_function, args_count);
Handle<FixedArray> array = factory->NewFixedArray(args_count);
@@ -767,7 +603,7 @@
// Find the top invocation of the function by traversing frames.
List<JSFunction*> functions(2);
- for (JavaScriptFrameIterator it; !it.done(); it.Advance()) {
+ for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) {
JavaScriptFrame* frame = it.frame();
frame->GetFunctions(&functions);
for (int i = functions.length() - 1; i >= 0; i--) {
@@ -856,7 +692,7 @@
Handle<JSFunction> function(holder, isolate);
List<JSFunction*> functions(2);
- for (JavaScriptFrameIterator it; !it.done(); it.Advance()) {
+ for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) {
JavaScriptFrame* frame = it.frame();
frame->GetFunctions(&functions);
for (int i = functions.length() - 1; i >= 0; i--) {
diff --git a/src/api.cc b/src/api.cc
index 2bfa598..c3684f7 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -1303,7 +1303,7 @@
}
// Copy the data to align it.
unsigned* deserialized_data = i::NewArray<unsigned>(deserialized_data_length);
- i::MemCopy(deserialized_data, data, length);
+ i::OS::MemCopy(deserialized_data, data, length);
return new i::ScriptDataImpl(
i::Vector<unsigned>(deserialized_data, deserialized_data_length));
@@ -2581,6 +2581,9 @@
ENTER_V8(isolate);
i::Handle<i::JSObject> self = Utils::OpenHandle(this);
i::Handle<i::Object> value_obj = Utils::OpenHandle(*value);
+ // We do not allow exceptions thrown while setting the prototype
+ // to propagate outside.
+ TryCatch try_catch;
EXCEPTION_PREAMBLE(isolate);
i::Handle<i::Object> result = i::SetPrototype(self, value_obj);
has_pending_exception = result.is_null();
@@ -2793,6 +2796,26 @@
}
+static Local<Value> GetPropertyByLookup(i::Isolate* isolate,
+ i::Handle<i::JSObject> receiver,
+ i::Handle<i::String> name,
+ i::LookupResult* lookup) {
+ if (!lookup->IsProperty()) {
+ // No real property was found.
+ return Local<Value>();
+ }
+
+ // If the property being looked up is a callback, it can throw
+ // an exception.
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> result = i::GetProperty(receiver, name, lookup);
+ has_pending_exception = result.is_null();
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>());
+
+ return Utils::ToLocal(result);
+}
+
+
Local<Value> v8::Object::GetRealNamedPropertyInPrototypeChain(
Handle<String> key) {
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
@@ -2804,17 +2827,7 @@
i::Handle<i::String> key_obj = Utils::OpenHandle(*key);
i::LookupResult lookup;
self_obj->LookupRealNamedPropertyInPrototypes(*key_obj, &lookup);
- if (lookup.IsProperty()) {
- PropertyAttributes attributes;
- i::Object* property =
- self_obj->GetProperty(*self_obj,
- &lookup,
- *key_obj,
- &attributes)->ToObjectUnchecked();
- i::Handle<i::Object> result(property);
- return Utils::ToLocal(result);
- }
- return Local<Value>(); // No real property was found in prototype chain.
+ return GetPropertyByLookup(isolate, self_obj, key_obj, &lookup);
}
@@ -2827,17 +2840,7 @@
i::Handle<i::String> key_obj = Utils::OpenHandle(*key);
i::LookupResult lookup;
self_obj->LookupRealNamedProperty(*key_obj, &lookup);
- if (lookup.IsProperty()) {
- PropertyAttributes attributes;
- i::Object* property =
- self_obj->GetProperty(*self_obj,
- &lookup,
- *key_obj,
- &attributes)->ToObjectUnchecked();
- i::Handle<i::Object> result(property);
- return Utils::ToLocal(result);
- }
- return Local<Value>(); // No real property was found in prototype chain.
+ return GetPropertyByLookup(isolate, self_obj, key_obj, &lookup);
}
@@ -2880,6 +2883,33 @@
}
+static i::Context* GetCreationContext(i::JSObject* object) {
+ i::Object* constructor = object->map()->constructor();
+ i::JSFunction* function;
+ if (!constructor->IsJSFunction()) {
+ // API functions have null as a constructor,
+ // but any JSFunction knows its context immediately.
+ ASSERT(object->IsJSFunction() &&
+ i::JSFunction::cast(object)->shared()->IsApiFunction());
+ function = i::JSFunction::cast(object);
+ } else {
+ function = i::JSFunction::cast(constructor);
+ }
+ return function->context()->global_context();
+}
+
+
+Local<v8::Context> v8::Object::CreationContext() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate,
+ "v8::Object::CreationContext()", return Local<v8::Context>());
+ ENTER_V8(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Context* context = GetCreationContext(*self);
+ return Utils::ToLocal(i::Handle<i::Context>(context));
+}
+
+
int v8::Object::GetIdentityHash() {
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
ON_BAILOUT(isolate, "v8::Object::GetIdentityHash()", return 0);
@@ -3679,6 +3709,7 @@
// Create the environment.
env = isolate->bootstrapper()->CreateEnvironment(
+ isolate,
Utils::OpenHandle(*global_object),
proxy_template,
extensions);
@@ -4177,9 +4208,11 @@
// Call ResetDateCache(0 but expect no exceptions:
bool caught_exception = false;
- i::Handle<i::Object> result =
- i::Execution::TryCall(func, isolate->js_builtins_object(), 0, NULL,
- &caught_exception);
+ i::Execution::TryCall(func,
+ isolate->js_builtins_object(),
+ 0,
+ NULL,
+ &caught_exception);
}
}
@@ -4248,7 +4281,9 @@
ENTER_V8(isolate);
int real_length = length > 0 ? length : 0;
i::Handle<i::JSArray> obj = isolate->factory()->NewJSArray(real_length);
- obj->set_length(*isolate->factory()->NewNumberFromInt(real_length));
+ i::Handle<i::Object> length_obj =
+ isolate->factory()->NewNumberFromInt(real_length);
+ obj->set_length(*length_obj);
return Utils::ToLocal(obj);
}
@@ -4444,7 +4479,7 @@
if (IsDeadCheck(isolate, "v8::V8::AddImplicitReferences()")) return;
STATIC_ASSERT(sizeof(Persistent<Value>) == sizeof(i::Object**));
isolate->global_handles()->AddImplicitReferences(
- *Utils::OpenHandle(*parent),
+ i::Handle<i::HeapObject>::cast(Utils::OpenHandle(*parent)).location(),
reinterpret_cast<i::Object***>(children), length);
}
@@ -4593,7 +4628,7 @@
int V8::GetCurrentThreadId() {
i::Isolate* isolate = i::Isolate::Current();
EnsureInitializedForIsolate(isolate, "V8::GetCurrentThreadId()");
- return isolate->thread_id();
+ return isolate->thread_id().ToInteger();
}
@@ -4604,10 +4639,11 @@
// If the thread_id identifies the current thread just terminate
// execution right away. Otherwise, ask the thread manager to
// terminate the thread with the given id if any.
- if (thread_id == isolate->thread_id()) {
+ i::ThreadId internal_tid = i::ThreadId::FromInteger(thread_id);
+ if (isolate->thread_id().Equals(internal_tid)) {
isolate->stack_guard()->TerminateExecution();
} else {
- isolate->thread_manager()->TerminateExecution(thread_id);
+ isolate->thread_manager()->TerminateExecution(internal_tid);
}
}
diff --git a/src/api.h b/src/api.h
index 6d46713..7423d28 100644
--- a/src/api.h
+++ b/src/api.h
@@ -53,8 +53,8 @@
class NeanderObject {
public:
explicit NeanderObject(int size);
- inline NeanderObject(v8::internal::Handle<v8::internal::Object> obj);
- inline NeanderObject(v8::internal::Object* obj);
+ explicit inline NeanderObject(v8::internal::Handle<v8::internal::Object> obj);
+ explicit inline NeanderObject(v8::internal::Object* obj);
inline v8::internal::Object* get(int index);
inline void set(int index, v8::internal::Object* value);
inline v8::internal::Handle<v8::internal::JSObject> value() { return value_; }
@@ -69,7 +69,7 @@
class NeanderArray {
public:
NeanderArray();
- inline NeanderArray(v8::internal::Handle<v8::internal::Object> obj);
+ explicit inline NeanderArray(v8::internal::Handle<v8::internal::Object> obj);
inline v8::internal::Handle<v8::internal::JSObject> value() {
return obj_.value();
}
diff --git a/src/arguments.h b/src/arguments.h
index c80548f..a7a30e2 100644
--- a/src/arguments.h
+++ b/src/arguments.h
@@ -99,8 +99,17 @@
Object* values_[3];
};
-#define RUNTIME_CALLING_CONVENTION Arguments args, Isolate* isolate
-#define RUNTIME_GET_ISOLATE ASSERT(isolate == Isolate::Current())
+
+#define DECLARE_RUNTIME_FUNCTION(Type, Name) \
+Type Name(Arguments args, Isolate* isolate)
+
+
+#define RUNTIME_FUNCTION(Type, Name) \
+Type Name(Arguments args, Isolate* isolate)
+
+
+#define RUNTIME_ARGUMENTS(isolate, args) args, isolate
+
} } // namespace v8::internal
diff --git a/src/arm/assembler-arm-inl.h b/src/arm/assembler-arm-inl.h
index bd76d9a..3e19a45 100644
--- a/src/arm/assembler-arm-inl.h
+++ b/src/arm/assembler-arm-inl.h
@@ -223,9 +223,9 @@
if (mode == RelocInfo::EMBEDDED_OBJECT) {
StaticVisitor::VisitPointer(heap, target_object_address());
} else if (RelocInfo::IsCodeTarget(mode)) {
- StaticVisitor::VisitCodeTarget(this);
+ StaticVisitor::VisitCodeTarget(heap, this);
} else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) {
- StaticVisitor::VisitGlobalPropertyCell(this);
+ StaticVisitor::VisitGlobalPropertyCell(heap, this);
} else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
StaticVisitor::VisitExternalReference(target_reference_address());
#ifdef ENABLE_DEBUGGER_SUPPORT
@@ -234,7 +234,7 @@
IsPatchedReturnSequence()) ||
(RelocInfo::IsDebugBreakSlot(mode) &&
IsPatchedDebugBreakSlotSequence()))) {
- StaticVisitor::VisitDebugTarget(this);
+ StaticVisitor::VisitDebugTarget(heap, this);
#endif
} else if (mode == RelocInfo::RUNTIME_ENTRY) {
StaticVisitor::VisitRuntimeEntry(this);
diff --git a/src/arm/assembler-arm.cc b/src/arm/assembler-arm.cc
index be34df9..fd8e8b5 100644
--- a/src/arm/assembler-arm.cc
+++ b/src/arm/assembler-arm.cc
@@ -32,7 +32,7 @@
// The original source code covered by the above license above has been
// modified significantly by Google Inc.
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
#include "v8.h"
@@ -44,11 +44,12 @@
namespace v8 {
namespace internal {
-CpuFeatures::CpuFeatures()
- : supported_(0),
- enabled_(0),
- found_by_runtime_probing_(0) {
-}
+#ifdef DEBUG
+bool CpuFeatures::initialized_ = false;
+#endif
+unsigned CpuFeatures::supported_ = 0;
+unsigned CpuFeatures::found_by_runtime_probing_ = 0;
+
#ifdef __arm__
static uint64_t CpuFeaturesImpliedByCompiler() {
@@ -58,48 +59,52 @@
#endif // def CAN_USE_ARMV7_INSTRUCTIONS
// If the compiler is allowed to use VFP then we can use VFP too in our code
// generation even when generating snapshots. This won't work for cross
- // compilation.
+ // compilation. VFPv3 implies ARMv7, see ARM DDI 0406B, page A1-6.
#if defined(__VFP_FP__) && !defined(__SOFTFP__)
- answer |= 1u << VFP3;
+ answer |= 1u << VFP3 | 1u << ARMv7;
#endif // defined(__VFP_FP__) && !defined(__SOFTFP__)
#ifdef CAN_USE_VFP_INSTRUCTIONS
- answer |= 1u << VFP3;
+ answer |= 1u << VFP3 | 1u << ARMv7;
#endif // def CAN_USE_VFP_INSTRUCTIONS
return answer;
}
#endif // def __arm__
-void CpuFeatures::Probe(bool portable) {
+void CpuFeatures::Probe() {
+ ASSERT(!initialized_);
+#ifdef DEBUG
+ initialized_ = true;
+#endif
#ifndef __arm__
- // For the simulator=arm build, use VFP when FLAG_enable_vfp3 is enabled.
+ // For the simulator=arm build, use VFP when FLAG_enable_vfp3 is
+ // enabled. VFPv3 implies ARMv7, see ARM DDI 0406B, page A1-6.
if (FLAG_enable_vfp3) {
- supported_ |= 1u << VFP3;
+ supported_ |= 1u << VFP3 | 1u << ARMv7;
}
// For the simulator=arm build, use ARMv7 when FLAG_enable_armv7 is enabled
if (FLAG_enable_armv7) {
supported_ |= 1u << ARMv7;
}
#else // def __arm__
- if (portable && Serializer::enabled()) {
+ if (Serializer::enabled()) {
supported_ |= OS::CpuFeaturesImpliedByPlatform();
supported_ |= CpuFeaturesImpliedByCompiler();
return; // No features if we might serialize.
}
if (OS::ArmCpuHasFeature(VFP3)) {
- // This implementation also sets the VFP flags if
- // runtime detection of VFP returns true.
- supported_ |= 1u << VFP3;
- found_by_runtime_probing_ |= 1u << VFP3;
+ // This implementation also sets the VFP flags if runtime
+ // detection of VFP returns true. VFPv3 implies ARMv7, see ARM DDI
+ // 0406B, page A1-6.
+ supported_ |= 1u << VFP3 | 1u << ARMv7;
+ found_by_runtime_probing_ |= 1u << VFP3 | 1u << ARMv7;
}
if (OS::ArmCpuHasFeature(ARMv7)) {
supported_ |= 1u << ARMv7;
found_by_runtime_probing_ |= 1u << ARMv7;
}
-
- if (!portable) found_by_runtime_probing_ = 0;
#endif
}
@@ -268,8 +273,8 @@
static const int kMinimalBufferSize = 4*KB;
-Assembler::Assembler(void* buffer, int buffer_size)
- : AssemblerBase(Isolate::Current()),
+Assembler::Assembler(Isolate* arg_isolate, void* buffer, int buffer_size)
+ : AssemblerBase(arg_isolate),
positions_recorder_(this),
allow_peephole_optimization_(false),
emit_debug_code_(FLAG_debug_code) {
@@ -715,7 +720,7 @@
*instr ^= kMovMvnFlip;
return true;
} else if ((*instr & kMovLeaveCCMask) == kMovLeaveCCPattern) {
- if (Isolate::Current()->cpu_features()->IsSupported(ARMv7)) {
+ if (CpuFeatures::IsSupported(ARMv7)) {
if (imm32 < 0x10000) {
*instr ^= kMovwLeaveCCFlip;
*instr |= EncodeMovwImmediate(imm32);
@@ -779,7 +784,7 @@
// condition code additional instruction conventions can be used.
if ((instr & ~kCondMask) == 13*B21) { // mov, S not set
if (must_use_constant_pool() ||
- !Isolate::Current()->cpu_features()->IsSupported(ARMv7)) {
+ !CpuFeatures::IsSupported(ARMv7)) {
// mov instruction will be an ldr from constant pool (one instruction).
return true;
} else {
@@ -822,7 +827,7 @@
Condition cond = Instruction::ConditionField(instr);
if ((instr & ~kCondMask) == 13*B21) { // mov, S not set
if (x.must_use_constant_pool() ||
- !isolate()->cpu_features()->IsSupported(ARMv7)) {
+ !CpuFeatures::IsSupported(ARMv7)) {
RecordRelocInfo(x.rmode_, x.imm32_);
ldr(rd, MemOperand(pc, 0), cond);
} else {
@@ -1265,7 +1270,7 @@
const Operand& src,
Condition cond) {
// v6 and above.
- ASSERT(isolate()->cpu_features()->IsSupported(ARMv7));
+ ASSERT(CpuFeatures::IsSupported(ARMv7));
ASSERT(!dst.is(pc) && !src.rm_.is(pc));
ASSERT((satpos >= 0) && (satpos <= 31));
ASSERT((src.shift_op_ == ASR) || (src.shift_op_ == LSL));
@@ -1293,7 +1298,7 @@
int width,
Condition cond) {
// v7 and above.
- ASSERT(isolate()->cpu_features()->IsSupported(ARMv7));
+ ASSERT(CpuFeatures::IsSupported(ARMv7));
ASSERT(!dst.is(pc) && !src.is(pc));
ASSERT((lsb >= 0) && (lsb <= 31));
ASSERT((width >= 1) && (width <= (32 - lsb)));
@@ -1313,7 +1318,7 @@
int width,
Condition cond) {
// v7 and above.
- ASSERT(isolate()->cpu_features()->IsSupported(ARMv7));
+ ASSERT(CpuFeatures::IsSupported(ARMv7));
ASSERT(!dst.is(pc) && !src.is(pc));
ASSERT((lsb >= 0) && (lsb <= 31));
ASSERT((width >= 1) && (width <= (32 - lsb)));
@@ -1328,7 +1333,7 @@
// bfc dst, #lsb, #width
void Assembler::bfc(Register dst, int lsb, int width, Condition cond) {
// v7 and above.
- ASSERT(isolate()->cpu_features()->IsSupported(ARMv7));
+ ASSERT(CpuFeatures::IsSupported(ARMv7));
ASSERT(!dst.is(pc));
ASSERT((lsb >= 0) && (lsb <= 31));
ASSERT((width >= 1) && (width <= (32 - lsb)));
@@ -1347,7 +1352,7 @@
int width,
Condition cond) {
// v7 and above.
- ASSERT(isolate()->cpu_features()->IsSupported(ARMv7));
+ ASSERT(CpuFeatures::IsSupported(ARMv7));
ASSERT(!dst.is(pc) && !src.is(pc));
ASSERT((lsb >= 0) && (lsb <= 31));
ASSERT((width >= 1) && (width <= (32 - lsb)));
@@ -1619,7 +1624,7 @@
void Assembler::ldrd(Register dst1, Register dst2,
const MemOperand& src, Condition cond) {
- ASSERT(isolate()->cpu_features()->IsEnabled(ARMv7));
+ ASSERT(CpuFeatures::IsEnabled(ARMv7));
ASSERT(src.rm().is(no_reg));
ASSERT(!dst1.is(lr)); // r14.
ASSERT_EQ(0, dst1.code() % 2);
@@ -1634,7 +1639,7 @@
ASSERT(!src1.is(lr)); // r14.
ASSERT_EQ(0, src1.code() % 2);
ASSERT_EQ(src1.code() + 1, src2.code());
- ASSERT(isolate()->cpu_features()->IsEnabled(ARMv7));
+ ASSERT(CpuFeatures::IsEnabled(ARMv7));
addrmod3(cond | B7 | B6 | B5 | B4, src1, dst);
}
@@ -1821,45 +1826,6 @@
}
-void Assembler::stc(Coprocessor coproc,
- CRegister crd,
- const MemOperand& dst,
- LFlag l,
- Condition cond) {
- addrmod5(cond | B27 | B26 | l | coproc*B8, crd, dst);
-}
-
-
-void Assembler::stc(Coprocessor coproc,
- CRegister crd,
- Register rn,
- int option,
- LFlag l,
- Condition cond) {
- // Unindexed addressing.
- ASSERT(is_uint8(option));
- emit(cond | B27 | B26 | U | l | rn.code()*B16 | crd.code()*B12 |
- coproc*B8 | (option & 255));
-}
-
-
-void Assembler::stc2(Coprocessor
- coproc, CRegister crd,
- const MemOperand& dst,
- LFlag l) { // v5 and above
- stc(coproc, crd, dst, l, kSpecialCondition);
-}
-
-
-void Assembler::stc2(Coprocessor coproc,
- CRegister crd,
- Register rn,
- int option,
- LFlag l) { // v5 and above
- stc(coproc, crd, rn, option, l, kSpecialCondition);
-}
-
-
// Support for VFP.
void Assembler::vldr(const DwVfpRegister dst,
@@ -1870,7 +1836,7 @@
// Instruction details available in ARM DDI 0406A, A8-628.
// cond(31-28) | 1101(27-24)| U001(23-20) | Rbase(19-16) |
// Vdst(15-12) | 1011(11-8) | offset
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
int u = 1;
if (offset < 0) {
offset = -offset;
@@ -1912,7 +1878,7 @@
// Instruction details available in ARM DDI 0406A, A8-628.
// cond(31-28) | 1101(27-24)| U001(23-20) | Rbase(19-16) |
// Vdst(15-12) | 1010(11-8) | offset
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
int u = 1;
if (offset < 0) {
offset = -offset;
@@ -1956,7 +1922,7 @@
// Instruction details available in ARM DDI 0406A, A8-786.
// cond(31-28) | 1101(27-24)| U000(23-20) | | Rbase(19-16) |
// Vsrc(15-12) | 1011(11-8) | (offset/4)
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
int u = 1;
if (offset < 0) {
offset = -offset;
@@ -1997,7 +1963,7 @@
// Instruction details available in ARM DDI 0406A, A8-786.
// cond(31-28) | 1101(27-24)| U000(23-20) | Rbase(19-16) |
// Vdst(15-12) | 1010(11-8) | (offset/4)
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
int u = 1;
if (offset < 0) {
offset = -offset;
@@ -2032,6 +1998,88 @@
}
+void Assembler::vldm(BlockAddrMode am,
+ Register base,
+ DwVfpRegister first,
+ DwVfpRegister last,
+ Condition cond) {
+ // Instruction details available in ARM DDI 0406A, A8-626.
+ // cond(31-28) | 110(27-25)| PUDW1(24-20) | Rbase(19-16) |
+ // first(15-12) | 1010(11-8) | (count * 2)
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
+ ASSERT_LE(first.code(), last.code());
+ ASSERT(am == ia || am == ia_w || am == db_w);
+ ASSERT(!base.is(pc));
+
+ int sd, d;
+ first.split_code(&sd, &d);
+ int count = last.code() - first.code() + 1;
+ emit(cond | B27 | B26 | am | d*B22 | B20 | base.code()*B16 | sd*B12 |
+ 0xB*B8 | count*2);
+}
+
+
+void Assembler::vstm(BlockAddrMode am,
+ Register base,
+ DwVfpRegister first,
+ DwVfpRegister last,
+ Condition cond) {
+ // Instruction details available in ARM DDI 0406A, A8-784.
+ // cond(31-28) | 110(27-25)| PUDW0(24-20) | Rbase(19-16) |
+ // first(15-12) | 1011(11-8) | (count * 2)
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
+ ASSERT_LE(first.code(), last.code());
+ ASSERT(am == ia || am == ia_w || am == db_w);
+ ASSERT(!base.is(pc));
+
+ int sd, d;
+ first.split_code(&sd, &d);
+ int count = last.code() - first.code() + 1;
+ emit(cond | B27 | B26 | am | d*B22 | base.code()*B16 | sd*B12 |
+ 0xB*B8 | count*2);
+}
+
+void Assembler::vldm(BlockAddrMode am,
+ Register base,
+ SwVfpRegister first,
+ SwVfpRegister last,
+ Condition cond) {
+ // Instruction details available in ARM DDI 0406A, A8-626.
+ // cond(31-28) | 110(27-25)| PUDW1(24-20) | Rbase(19-16) |
+ // first(15-12) | 1010(11-8) | (count/2)
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
+ ASSERT_LE(first.code(), last.code());
+ ASSERT(am == ia || am == ia_w || am == db_w);
+ ASSERT(!base.is(pc));
+
+ int sd, d;
+ first.split_code(&sd, &d);
+ int count = last.code() - first.code() + 1;
+ emit(cond | B27 | B26 | am | d*B22 | B20 | base.code()*B16 | sd*B12 |
+ 0xA*B8 | count);
+}
+
+
+void Assembler::vstm(BlockAddrMode am,
+ Register base,
+ SwVfpRegister first,
+ SwVfpRegister last,
+ Condition cond) {
+ // Instruction details available in ARM DDI 0406A, A8-784.
+ // cond(31-28) | 110(27-25)| PUDW0(24-20) | Rbase(19-16) |
+ // first(15-12) | 1011(11-8) | (count/2)
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
+ ASSERT_LE(first.code(), last.code());
+ ASSERT(am == ia || am == ia_w || am == db_w);
+ ASSERT(!base.is(pc));
+
+ int sd, d;
+ first.split_code(&sd, &d);
+ int count = last.code() - first.code() + 1;
+ emit(cond | B27 | B26 | am | d*B22 | base.code()*B16 | sd*B12 |
+ 0xA*B8 | count);
+}
+
static void DoubleAsTwoUInt32(double d, uint32_t* lo, uint32_t* hi) {
uint64_t i;
memcpy(&i, &d, 8);
@@ -2043,7 +2091,7 @@
// Only works for little endian floating point formats.
// We don't support VFP on the mixed endian floating point platform.
static bool FitsVMOVDoubleImmediate(double d, uint32_t *encoding) {
- ASSERT(Isolate::Current()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
// VMOV can accept an immediate of the form:
//
@@ -2096,7 +2144,7 @@
const Condition cond) {
// Dd = immediate
// Instruction details available in ARM DDI 0406B, A8-640.
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
uint32_t enc;
if (FitsVMOVDoubleImmediate(imm, &enc)) {
@@ -2133,7 +2181,7 @@
const Condition cond) {
// Sd = Sm
// Instruction details available in ARM DDI 0406B, A8-642.
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
int sd, d, sm, m;
dst.split_code(&sd, &d);
src.split_code(&sm, &m);
@@ -2146,7 +2194,7 @@
const Condition cond) {
// Dd = Dm
// Instruction details available in ARM DDI 0406B, A8-642.
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
emit(cond | 0xE*B24 | 0xB*B20 |
dst.code()*B12 | 0x5*B9 | B8 | B6 | src.code());
}
@@ -2160,7 +2208,7 @@
// Instruction details available in ARM DDI 0406A, A8-646.
// cond(31-28) | 1100(27-24)| 010(23-21) | op=0(20) | Rt2(19-16) |
// Rt(15-12) | 1011(11-8) | 00(7-6) | M(5) | 1(4) | Vm
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
ASSERT(!src1.is(pc) && !src2.is(pc));
emit(cond | 0xC*B24 | B22 | src2.code()*B16 |
src1.code()*B12 | 0xB*B8 | B4 | dst.code());
@@ -2175,7 +2223,7 @@
// Instruction details available in ARM DDI 0406A, A8-646.
// cond(31-28) | 1100(27-24)| 010(23-21) | op=1(20) | Rt2(19-16) |
// Rt(15-12) | 1011(11-8) | 00(7-6) | M(5) | 1(4) | Vm
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
ASSERT(!dst1.is(pc) && !dst2.is(pc));
emit(cond | 0xC*B24 | B22 | B20 | dst2.code()*B16 |
dst1.code()*B12 | 0xB*B8 | B4 | src.code());
@@ -2189,7 +2237,7 @@
// Instruction details available in ARM DDI 0406A, A8-642.
// cond(31-28) | 1110(27-24)| 000(23-21) | op=0(20) | Vn(19-16) |
// Rt(15-12) | 1010(11-8) | N(7)=0 | 00(6-5) | 1(4) | 0000(3-0)
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
ASSERT(!src.is(pc));
int sn, n;
dst.split_code(&sn, &n);
@@ -2204,7 +2252,7 @@
// Instruction details available in ARM DDI 0406A, A8-642.
// cond(31-28) | 1110(27-24)| 000(23-21) | op=1(20) | Vn(19-16) |
// Rt(15-12) | 1010(11-8) | N(7)=0 | 00(6-5) | 1(4) | 0000(3-0)
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
ASSERT(!dst.is(pc));
int sn, n;
src.split_code(&sn, &n);
@@ -2329,7 +2377,7 @@
const SwVfpRegister src,
VFPConversionMode mode,
const Condition cond) {
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
emit(EncodeVCVT(F64, dst.code(), S32, src.code(), mode, cond));
}
@@ -2338,7 +2386,7 @@
const SwVfpRegister src,
VFPConversionMode mode,
const Condition cond) {
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
emit(EncodeVCVT(F32, dst.code(), S32, src.code(), mode, cond));
}
@@ -2347,7 +2395,7 @@
const SwVfpRegister src,
VFPConversionMode mode,
const Condition cond) {
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
emit(EncodeVCVT(F64, dst.code(), U32, src.code(), mode, cond));
}
@@ -2356,7 +2404,7 @@
const DwVfpRegister src,
VFPConversionMode mode,
const Condition cond) {
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
emit(EncodeVCVT(S32, dst.code(), F64, src.code(), mode, cond));
}
@@ -2365,7 +2413,7 @@
const DwVfpRegister src,
VFPConversionMode mode,
const Condition cond) {
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
emit(EncodeVCVT(U32, dst.code(), F64, src.code(), mode, cond));
}
@@ -2374,7 +2422,7 @@
const SwVfpRegister src,
VFPConversionMode mode,
const Condition cond) {
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
emit(EncodeVCVT(F64, dst.code(), F32, src.code(), mode, cond));
}
@@ -2383,7 +2431,7 @@
const DwVfpRegister src,
VFPConversionMode mode,
const Condition cond) {
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
emit(EncodeVCVT(F32, dst.code(), F64, src.code(), mode, cond));
}
@@ -2413,7 +2461,7 @@
// Instruction details available in ARM DDI 0406A, A8-536.
// cond(31-28) | 11100(27-23)| D=?(22) | 11(21-20) | Vn(19-16) |
// Vd(15-12) | 101(11-9) | sz(8)=1 | N(7)=0 | 0(6) | M=?(5) | 0(4) | Vm(3-0)
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
emit(cond | 0xE*B24 | 0x3*B20 | src1.code()*B16 |
dst.code()*B12 | 0x5*B9 | B8 | src2.code());
}
@@ -2428,7 +2476,7 @@
// Instruction details available in ARM DDI 0406A, A8-784.
// cond(31-28) | 11100(27-23)| D=?(22) | 11(21-20) | Vn(19-16) |
// Vd(15-12) | 101(11-9) | sz(8)=1 | N(7)=0 | 1(6) | M=?(5) | 0(4) | Vm(3-0)
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
emit(cond | 0xE*B24 | 0x3*B20 | src1.code()*B16 |
dst.code()*B12 | 0x5*B9 | B8 | B6 | src2.code());
}
@@ -2443,7 +2491,7 @@
// Instruction details available in ARM DDI 0406A, A8-784.
// cond(31-28) | 11100(27-23)| D=?(22) | 10(21-20) | Vn(19-16) |
// Vd(15-12) | 101(11-9) | sz(8)=1 | N(7)=0 | 0(6) | M=?(5) | 0(4) | Vm(3-0)
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
emit(cond | 0xE*B24 | 0x2*B20 | src1.code()*B16 |
dst.code()*B12 | 0x5*B9 | B8 | src2.code());
}
@@ -2458,7 +2506,7 @@
// Instruction details available in ARM DDI 0406A, A8-584.
// cond(31-28) | 11101(27-23)| D=?(22) | 00(21-20) | Vn(19-16) |
// Vd(15-12) | 101(11-9) | sz(8)=1 | N(7)=? | 0(6) | M=?(5) | 0(4) | Vm(3-0)
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
emit(cond | 0xE*B24 | B23 | src1.code()*B16 |
dst.code()*B12 | 0x5*B9 | B8 | src2.code());
}
@@ -2471,7 +2519,7 @@
// Instruction details available in ARM DDI 0406A, A8-570.
// cond(31-28) | 11101 (27-23)| D=?(22) | 11 (21-20) | 0100 (19-16) |
// Vd(15-12) | 101(11-9) | sz(8)=1 | E(7)=0 | 1(6) | M(5)=? | 0(4) | Vm(3-0)
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
emit(cond | 0xE*B24 |B23 | 0x3*B20 | B18 |
src1.code()*B12 | 0x5*B9 | B8 | B6 | src2.code());
}
@@ -2484,7 +2532,7 @@
// Instruction details available in ARM DDI 0406A, A8-570.
// cond(31-28) | 11101 (27-23)| D=?(22) | 11 (21-20) | 0101 (19-16) |
// Vd(15-12) | 101(11-9) | sz(8)=1 | E(7)=0 | 1(6) | M(5)=? | 0(4) | 0000(3-0)
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
ASSERT(src2 == 0.0);
emit(cond | 0xE*B24 |B23 | 0x3*B20 | B18 | B16 |
src1.code()*B12 | 0x5*B9 | B8 | B6);
@@ -2495,7 +2543,7 @@
// Instruction details available in ARM DDI 0406A, A8-652.
// cond(31-28) | 1110 (27-24) | 1110(23-20)| 0001 (19-16) |
// Rt(15-12) | 1010 (11-8) | 0(7) | 00 (6-5) | 1(4) | 0000(3-0)
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
emit(cond | 0xE*B24 | 0xE*B20 | B16 |
dst.code()*B12 | 0xA*B8 | B4);
}
@@ -2505,7 +2553,7 @@
// Instruction details available in ARM DDI 0406A, A8-652.
// cond(31-28) | 1110 (27-24) | 1111(23-20)| 0001 (19-16) |
// Rt(15-12) | 1010 (11-8) | 0(7) | 00 (6-5) | 1(4) | 0000(3-0)
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
emit(cond | 0xE*B24 | 0xF*B20 | B16 |
dst.code()*B12 | 0xA*B8 | B4);
}
@@ -2516,7 +2564,7 @@
const Condition cond) {
// cond(31-28) | 11101 (27-23)| D=?(22) | 11 (21-20) | 0001 (19-16) |
// Vd(15-12) | 101(11-9) | sz(8)=1 | 11 (7-6) | M(5)=? | 0(4) | Vm(3-0)
- ASSERT(isolate()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
emit(cond | 0xE*B24 | B23 | 0x3*B20 | B16 |
dst.code()*B12 | 0x5*B9 | B8 | 3*B6 | src.code());
}
diff --git a/src/arm/assembler-arm.h b/src/arm/assembler-arm.h
index 91e6244..9050c2c 100644
--- a/src/arm/assembler-arm.h
+++ b/src/arm/assembler-arm.h
@@ -32,7 +32,7 @@
// The original source code covered by the above license above has been
// modified significantly by Google Inc.
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// A light-weight ARM Assembler
// Generates user mode instructions for the ARM architecture up to version 5
@@ -468,58 +468,97 @@
// CpuFeatures keeps track of which features are supported by the target CPU.
// Supported features must be enabled by a Scope before use.
-class CpuFeatures {
+class CpuFeatures : public AllStatic {
public:
// Detect features of the target CPU. Set safe defaults if the serializer
// is enabled (snapshots must be portable).
- void Probe(bool portable);
+ static void Probe();
// Check whether a feature is supported by the target CPU.
- bool IsSupported(CpuFeature f) const {
+ static bool IsSupported(CpuFeature f) {
+ ASSERT(initialized_);
if (f == VFP3 && !FLAG_enable_vfp3) return false;
return (supported_ & (1u << f)) != 0;
}
+#ifdef DEBUG
// Check whether a feature is currently enabled.
- bool IsEnabled(CpuFeature f) const {
- return (enabled_ & (1u << f)) != 0;
+ static bool IsEnabled(CpuFeature f) {
+ ASSERT(initialized_);
+ Isolate* isolate = Isolate::UncheckedCurrent();
+ if (isolate == NULL) {
+ // When no isolate is available, work as if we're running in
+ // release mode.
+ return IsSupported(f);
+ }
+ unsigned enabled = static_cast<unsigned>(isolate->enabled_cpu_features());
+ return (enabled & (1u << f)) != 0;
}
+#endif
// Enable a specified feature within a scope.
class Scope BASE_EMBEDDED {
#ifdef DEBUG
public:
- explicit Scope(CpuFeature f)
- : cpu_features_(Isolate::Current()->cpu_features()),
- isolate_(Isolate::Current()) {
- ASSERT(cpu_features_->IsSupported(f));
+ explicit Scope(CpuFeature f) {
+ unsigned mask = 1u << f;
+ ASSERT(CpuFeatures::IsSupported(f));
ASSERT(!Serializer::enabled() ||
- (cpu_features_->found_by_runtime_probing_ & (1u << f)) == 0);
- old_enabled_ = cpu_features_->enabled_;
- cpu_features_->enabled_ |= 1u << f;
+ (CpuFeatures::found_by_runtime_probing_ & mask) == 0);
+ isolate_ = Isolate::UncheckedCurrent();
+ old_enabled_ = 0;
+ if (isolate_ != NULL) {
+ old_enabled_ = static_cast<unsigned>(isolate_->enabled_cpu_features());
+ isolate_->set_enabled_cpu_features(old_enabled_ | mask);
+ }
}
~Scope() {
- ASSERT_EQ(Isolate::Current(), isolate_);
- cpu_features_->enabled_ = old_enabled_;
+ ASSERT_EQ(Isolate::UncheckedCurrent(), isolate_);
+ if (isolate_ != NULL) {
+ isolate_->set_enabled_cpu_features(old_enabled_);
+ }
}
private:
- unsigned old_enabled_;
- CpuFeatures* cpu_features_;
Isolate* isolate_;
+ unsigned old_enabled_;
#else
public:
explicit Scope(CpuFeature f) {}
#endif
};
+ class TryForceFeatureScope BASE_EMBEDDED {
+ public:
+ explicit TryForceFeatureScope(CpuFeature f)
+ : old_supported_(CpuFeatures::supported_) {
+ if (CanForce()) {
+ CpuFeatures::supported_ |= (1u << f);
+ }
+ }
+
+ ~TryForceFeatureScope() {
+ if (CanForce()) {
+ CpuFeatures::supported_ = old_supported_;
+ }
+ }
+
+ private:
+ static bool CanForce() {
+ // It's only safe to temporarily force support of CPU features
+ // when there's only a single isolate, which is guaranteed when
+ // the serializer is enabled.
+ return Serializer::enabled();
+ }
+
+ const unsigned old_supported_;
+ };
+
private:
- CpuFeatures();
-
- unsigned supported_;
- unsigned enabled_;
- unsigned found_by_runtime_probing_;
-
- friend class Isolate;
+#ifdef DEBUG
+ static bool initialized_;
+#endif
+ static unsigned supported_;
+ static unsigned found_by_runtime_probing_;
DISALLOW_COPY_AND_ASSIGN(CpuFeatures);
};
@@ -564,7 +603,7 @@
// for code generation and assumes its size to be buffer_size. If the buffer
// is too small, a fatal error occurs. No deallocation of the buffer is done
// upon destruction of the assembler.
- Assembler(void* buffer, int buffer_size);
+ Assembler(Isolate* isolate, void* buffer, int buffer_size);
~Assembler();
// Overrides the default provided by FLAG_debug_code.
@@ -908,16 +947,6 @@
void ldc2(Coprocessor coproc, CRegister crd, Register base, int option,
LFlag l = Short); // v5 and above
- void stc(Coprocessor coproc, CRegister crd, const MemOperand& dst,
- LFlag l = Short, Condition cond = al);
- void stc(Coprocessor coproc, CRegister crd, Register base, int option,
- LFlag l = Short, Condition cond = al);
-
- void stc2(Coprocessor coproc, CRegister crd, const MemOperand& dst,
- LFlag l = Short); // v5 and above
- void stc2(Coprocessor coproc, CRegister crd, Register base, int option,
- LFlag l = Short); // v5 and above
-
// Support for VFP.
// All these APIs support S0 to S31 and D0 to D15.
// Currently these APIs do not support extended D registers, i.e, D16 to D31.
@@ -956,6 +985,30 @@
const MemOperand& dst,
const Condition cond = al);
+ void vldm(BlockAddrMode am,
+ Register base,
+ DwVfpRegister first,
+ DwVfpRegister last,
+ Condition cond = al);
+
+ void vstm(BlockAddrMode am,
+ Register base,
+ DwVfpRegister first,
+ DwVfpRegister last,
+ Condition cond = al);
+
+ void vldm(BlockAddrMode am,
+ Register base,
+ SwVfpRegister first,
+ SwVfpRegister last,
+ Condition cond = al);
+
+ void vstm(BlockAddrMode am,
+ Register base,
+ SwVfpRegister first,
+ SwVfpRegister last,
+ Condition cond = al);
+
void vmov(const DwVfpRegister dst,
double imm,
const Condition cond = al);
diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc
index f401cfd..5235dd3 100644
--- a/src/arm/builtins-arm.cc
+++ b/src/arm/builtins-arm.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -29,7 +29,7 @@
#if defined(V8_TARGET_ARCH_ARM)
-#include "codegen-inl.h"
+#include "codegen.h"
#include "debug.h"
#include "deoptimizer.h"
#include "full-codegen.h"
@@ -1173,9 +1173,11 @@
void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) {
- // Probe the CPU to set the supported features, because this builtin
- // may be called before the initialization performs CPU setup.
- masm->isolate()->cpu_features()->Probe(false);
+ CpuFeatures::TryForceFeatureScope scope(VFP3);
+ if (!CpuFeatures::IsSupported(VFP3)) {
+ __ Abort("Unreachable code: Cannot optimize without VFP3 support.");
+ return;
+ }
// Lookup the function in the JavaScript frame and push it as an
// argument to the on-stack replacement function.
diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc
index 441adfe..d66daea 100644
--- a/src/arm/code-stubs-arm.cc
+++ b/src/arm/code-stubs-arm.cc
@@ -308,13 +308,9 @@
void ConvertToDoubleStub::Generate(MacroAssembler* masm) {
-#ifndef BIG_ENDIAN_FLOATING_POINT
Register exponent = result1_;
Register mantissa = result2_;
-#else
- Register exponent = result2_;
- Register mantissa = result1_;
-#endif
+
Label not_special;
// Convert from Smi to integer.
__ mov(source_, Operand(source_, ASR, kSmiTagSize));
@@ -502,7 +498,7 @@
FloatingPointHelper::Destination destination,
Register scratch1,
Register scratch2) {
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
__ mov(scratch1, Operand(r0, ASR, kSmiTagSize));
__ vmov(d7.high(), scratch1);
@@ -521,7 +517,7 @@
ConvertToDoubleStub stub1(r3, r2, scratch1, scratch2);
__ push(lr);
__ Call(stub1.GetCode(), RelocInfo::CODE_TARGET);
- // Write Smi from r1 to r1 and r0 in double format. r9 is scratch.
+ // Write Smi from r1 to r1 and r0 in double format.
__ mov(scratch1, Operand(r1));
ConvertToDoubleStub stub2(r1, r0, scratch1, scratch2);
__ Call(stub2.GetCode(), RelocInfo::CODE_TARGET);
@@ -570,7 +566,7 @@
__ JumpIfNotHeapNumber(object, heap_number_map, scratch1, not_number);
// Handle loading a double from a heap number.
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3) &&
+ if (CpuFeatures::IsSupported(VFP3) &&
destination == kVFPRegisters) {
CpuFeatures::Scope scope(VFP3);
// Load the double from tagged HeapNumber to double register.
@@ -585,7 +581,7 @@
// Handle loading a double from a smi.
__ bind(&is_smi);
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
// Convert smi to double using VFP instructions.
__ SmiUntag(scratch1, object);
@@ -676,7 +672,7 @@
__ JumpIfNotSmi(object, &obj_is_not_smi);
__ SmiUntag(scratch1, object);
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
__ vmov(single_scratch, scratch1);
__ vcvt_f64_s32(double_dst, single_scratch);
@@ -686,51 +682,51 @@
} else {
Label fewer_than_20_useful_bits;
// Expected output:
- // | dst1 | dst2 |
+ // | dst2 | dst1 |
// | s | exp | mantissa |
// Check for zero.
__ cmp(scratch1, Operand(0));
- __ mov(dst1, scratch1);
__ mov(dst2, scratch1);
+ __ mov(dst1, scratch1);
__ b(eq, &done);
// Preload the sign of the value.
- __ and_(dst1, scratch1, Operand(HeapNumber::kSignMask), SetCC);
+ __ and_(dst2, scratch1, Operand(HeapNumber::kSignMask), SetCC);
// Get the absolute value of the object (as an unsigned integer).
__ rsb(scratch1, scratch1, Operand(0), SetCC, mi);
// Get mantisssa[51:20].
// Get the position of the first set bit.
- __ CountLeadingZeros(dst2, scratch1, scratch2);
- __ rsb(dst2, dst2, Operand(31));
+ __ CountLeadingZeros(dst1, scratch1, scratch2);
+ __ rsb(dst1, dst1, Operand(31));
// Set the exponent.
- __ add(scratch2, dst2, Operand(HeapNumber::kExponentBias));
- __ Bfi(dst1, scratch2, scratch2,
+ __ add(scratch2, dst1, Operand(HeapNumber::kExponentBias));
+ __ Bfi(dst2, scratch2, scratch2,
HeapNumber::kExponentShift, HeapNumber::kExponentBits);
// Clear the first non null bit.
__ mov(scratch2, Operand(1));
- __ bic(scratch1, scratch1, Operand(scratch2, LSL, dst2));
+ __ bic(scratch1, scratch1, Operand(scratch2, LSL, dst1));
- __ cmp(dst2, Operand(HeapNumber::kMantissaBitsInTopWord));
+ __ cmp(dst1, Operand(HeapNumber::kMantissaBitsInTopWord));
// Get the number of bits to set in the lower part of the mantissa.
- __ sub(scratch2, dst2, Operand(HeapNumber::kMantissaBitsInTopWord), SetCC);
+ __ sub(scratch2, dst1, Operand(HeapNumber::kMantissaBitsInTopWord), SetCC);
__ b(mi, &fewer_than_20_useful_bits);
// Set the higher 20 bits of the mantissa.
- __ orr(dst1, dst1, Operand(scratch1, LSR, scratch2));
+ __ orr(dst2, dst2, Operand(scratch1, LSR, scratch2));
__ rsb(scratch2, scratch2, Operand(32));
- __ mov(dst2, Operand(scratch1, LSL, scratch2));
+ __ mov(dst1, Operand(scratch1, LSL, scratch2));
__ b(&done);
__ bind(&fewer_than_20_useful_bits);
- __ rsb(scratch2, dst2, Operand(HeapNumber::kMantissaBitsInTopWord));
+ __ rsb(scratch2, dst1, Operand(HeapNumber::kMantissaBitsInTopWord));
__ mov(scratch2, Operand(scratch1, LSL, scratch2));
- __ orr(dst1, dst1, scratch2);
- // Set dst2 to 0.
- __ mov(dst2, Operand(0));
+ __ orr(dst2, dst2, scratch2);
+ // Set dst1 to 0.
+ __ mov(dst1, Operand(0));
}
__ b(&done);
@@ -744,7 +740,7 @@
__ JumpIfNotHeapNumber(object, heap_number_map, scratch1, not_int32);
// Load the number.
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
// Load the double value.
__ sub(scratch1, object, Operand(kHeapObjectTag));
@@ -818,7 +814,7 @@
// Object is a heap number.
// Convert the floating point value to a 32-bit integer.
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
SwVfpRegister single_scratch = double_scratch.low();
// Load the double value.
@@ -951,18 +947,10 @@
// Call C routine that may not cause GC or other trouble.
__ CallCFunction(ExternalReference::double_fp_operation(op, masm->isolate()),
4);
- // Store answer in the overwritable heap number.
-#if !defined(USE_ARM_EABI)
- // Double returned in fp coprocessor register 0 and 1, encoded as
- // register cr8. Offsets must be divisible by 4 for coprocessor so we
- // need to substract the tag from heap_number_result.
- __ sub(scratch, heap_number_result, Operand(kHeapObjectTag));
- __ stc(p1, cr8, MemOperand(scratch, HeapNumber::kValueOffset));
-#else
- // Double returned in registers 0 and 1.
+ // Store answer in the overwritable heap number. Double returned in
+ // registers r0 and r1.
__ Strd(r0, r1, FieldMemOperand(heap_number_result,
HeapNumber::kValueOffset));
-#endif
// Place heap_number_result in r0 and return to the pushed return address.
__ mov(r0, Operand(heap_number_result));
__ pop(pc);
@@ -1153,7 +1141,7 @@
}
// Lhs is a smi, rhs is a number.
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
// Convert lhs to a double in d7.
CpuFeatures::Scope scope(VFP3);
__ SmiToDoubleVFPRegister(lhs, d7, r7, s15);
@@ -1193,7 +1181,7 @@
}
// Rhs is a smi, lhs is a heap number.
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
// Load the double from lhs, tagged HeapNumber r1, to d7.
__ sub(r7, lhs, Operand(kHeapObjectTag));
@@ -1373,7 +1361,7 @@
// Both are heap numbers. Load them up then jump to the code we have
// for that.
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
__ sub(r7, rhs, Operand(kHeapObjectTag));
__ vldr(d6, r7, HeapNumber::kValueOffset);
@@ -1463,7 +1451,7 @@
Label load_result_from_cache;
if (!object_is_smi) {
__ JumpIfSmi(object, &is_smi);
- if (isolate->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
__ CheckMap(object,
scratch1,
@@ -1597,7 +1585,7 @@
// The arguments have been converted to doubles and stored in d6 and d7, if
// VFP3 is supported, or in r0, r1, r2, and r3.
Isolate* isolate = masm->isolate();
- if (isolate->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
__ bind(&lhs_not_nan);
CpuFeatures::Scope scope(VFP3);
Label no_nan;
@@ -1707,7 +1695,7 @@
// The stub returns zero for false, and a non-zero value for true.
void ToBooleanStub::Generate(MacroAssembler* masm) {
// This stub uses VFP3 instructions.
- ASSERT(Isolate::Current()->cpu_features()->IsEnabled(VFP3));
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
Label false_result;
Label not_heap_number;
@@ -1780,1064 +1768,6 @@
}
-// We fall into this code if the operands were Smis, but the result was
-// not (eg. overflow). We branch into this code (to the not_smi label) if
-// the operands were not both Smi. The operands are in r0 and r1. In order
-// to call the C-implemented binary fp operation routines we need to end up
-// with the double precision floating point operands in r0 and r1 (for the
-// value in r1) and r2 and r3 (for the value in r0).
-void GenericBinaryOpStub::HandleBinaryOpSlowCases(
- MacroAssembler* masm,
- Label* not_smi,
- Register lhs,
- Register rhs,
- const Builtins::JavaScript& builtin) {
- Label slow, slow_reverse, do_the_call;
- bool use_fp_registers =
- Isolate::Current()->cpu_features()->IsSupported(VFP3) &&
- Token::MOD != op_;
-
- ASSERT((lhs.is(r0) && rhs.is(r1)) || (lhs.is(r1) && rhs.is(r0)));
- Register heap_number_map = r6;
-
- if (ShouldGenerateSmiCode()) {
- __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
-
- // Smi-smi case (overflow).
- // Since both are Smis there is no heap number to overwrite, so allocate.
- // The new heap number is in r5. r3 and r7 are scratch.
- __ AllocateHeapNumber(
- r5, r3, r7, heap_number_map, lhs.is(r0) ? &slow_reverse : &slow);
-
- // If we have floating point hardware, inline ADD, SUB, MUL, and DIV,
- // using registers d7 and d6 for the double values.
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
- CpuFeatures::Scope scope(VFP3);
- __ mov(r7, Operand(rhs, ASR, kSmiTagSize));
- __ vmov(s15, r7);
- __ vcvt_f64_s32(d7, s15);
- __ mov(r7, Operand(lhs, ASR, kSmiTagSize));
- __ vmov(s13, r7);
- __ vcvt_f64_s32(d6, s13);
- if (!use_fp_registers) {
- __ vmov(r2, r3, d7);
- __ vmov(r0, r1, d6);
- }
- } else {
- // Write Smi from rhs to r3 and r2 in double format. r9 is scratch.
- __ mov(r7, Operand(rhs));
- ConvertToDoubleStub stub1(r3, r2, r7, r9);
- __ push(lr);
- __ Call(stub1.GetCode(), RelocInfo::CODE_TARGET);
- // Write Smi from lhs to r1 and r0 in double format. r9 is scratch.
- __ mov(r7, Operand(lhs));
- ConvertToDoubleStub stub2(r1, r0, r7, r9);
- __ Call(stub2.GetCode(), RelocInfo::CODE_TARGET);
- __ pop(lr);
- }
- __ jmp(&do_the_call); // Tail call. No return.
- }
-
- // We branch here if at least one of r0 and r1 is not a Smi.
- __ bind(not_smi);
- __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
-
- // After this point we have the left hand side in r1 and the right hand side
- // in r0.
- if (lhs.is(r0)) {
- __ Swap(r0, r1, ip);
- }
-
- // The type transition also calculates the answer.
- bool generate_code_to_calculate_answer = true;
-
- if (ShouldGenerateFPCode()) {
- // DIV has neither SmiSmi fast code nor specialized slow code.
- // So don't try to patch a DIV Stub.
- if (runtime_operands_type_ == BinaryOpIC::DEFAULT) {
- switch (op_) {
- case Token::ADD:
- case Token::SUB:
- case Token::MUL:
- GenerateTypeTransition(masm); // Tail call.
- generate_code_to_calculate_answer = false;
- break;
-
- case Token::DIV:
- // DIV has neither SmiSmi fast code nor specialized slow code.
- // So don't try to patch a DIV Stub.
- break;
-
- default:
- break;
- }
- }
-
- if (generate_code_to_calculate_answer) {
- Label r0_is_smi, r1_is_smi, finished_loading_r0, finished_loading_r1;
- if (mode_ == NO_OVERWRITE) {
- // In the case where there is no chance of an overwritable float we may
- // as well do the allocation immediately while r0 and r1 are untouched.
- __ AllocateHeapNumber(r5, r3, r7, heap_number_map, &slow);
- }
-
- // Move r0 to a double in r2-r3.
- __ tst(r0, Operand(kSmiTagMask));
- __ b(eq, &r0_is_smi); // It's a Smi so don't check it's a heap number.
- __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
- __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
- __ cmp(r4, heap_number_map);
- __ b(ne, &slow);
- if (mode_ == OVERWRITE_RIGHT) {
- __ mov(r5, Operand(r0)); // Overwrite this heap number.
- }
- if (use_fp_registers) {
- CpuFeatures::Scope scope(VFP3);
- // Load the double from tagged HeapNumber r0 to d7.
- __ sub(r7, r0, Operand(kHeapObjectTag));
- __ vldr(d7, r7, HeapNumber::kValueOffset);
- } else {
- // Calling convention says that second double is in r2 and r3.
- __ Ldrd(r2, r3, FieldMemOperand(r0, HeapNumber::kValueOffset));
- }
- __ jmp(&finished_loading_r0);
- __ bind(&r0_is_smi);
- if (mode_ == OVERWRITE_RIGHT) {
- // We can't overwrite a Smi so get address of new heap number into r5.
- __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
- }
-
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
- CpuFeatures::Scope scope(VFP3);
- // Convert smi in r0 to double in d7.
- __ mov(r7, Operand(r0, ASR, kSmiTagSize));
- __ vmov(s15, r7);
- __ vcvt_f64_s32(d7, s15);
- if (!use_fp_registers) {
- __ vmov(r2, r3, d7);
- }
- } else {
- // Write Smi from r0 to r3 and r2 in double format.
- __ mov(r7, Operand(r0));
- ConvertToDoubleStub stub3(r3, r2, r7, r4);
- __ push(lr);
- __ Call(stub3.GetCode(), RelocInfo::CODE_TARGET);
- __ pop(lr);
- }
-
- // HEAP_NUMBERS stub is slower than GENERIC on a pair of smis.
- // r0 is known to be a smi. If r1 is also a smi then switch to GENERIC.
- Label r1_is_not_smi;
- if ((runtime_operands_type_ == BinaryOpIC::HEAP_NUMBERS) &&
- HasSmiSmiFastPath()) {
- __ tst(r1, Operand(kSmiTagMask));
- __ b(ne, &r1_is_not_smi);
- GenerateTypeTransition(masm); // Tail call.
- }
-
- __ bind(&finished_loading_r0);
-
- // Move r1 to a double in r0-r1.
- __ tst(r1, Operand(kSmiTagMask));
- __ b(eq, &r1_is_smi); // It's a Smi so don't check it's a heap number.
- __ bind(&r1_is_not_smi);
- __ ldr(r4, FieldMemOperand(r1, HeapNumber::kMapOffset));
- __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
- __ cmp(r4, heap_number_map);
- __ b(ne, &slow);
- if (mode_ == OVERWRITE_LEFT) {
- __ mov(r5, Operand(r1)); // Overwrite this heap number.
- }
- if (use_fp_registers) {
- CpuFeatures::Scope scope(VFP3);
- // Load the double from tagged HeapNumber r1 to d6.
- __ sub(r7, r1, Operand(kHeapObjectTag));
- __ vldr(d6, r7, HeapNumber::kValueOffset);
- } else {
- // Calling convention says that first double is in r0 and r1.
- __ Ldrd(r0, r1, FieldMemOperand(r1, HeapNumber::kValueOffset));
- }
- __ jmp(&finished_loading_r1);
- __ bind(&r1_is_smi);
- if (mode_ == OVERWRITE_LEFT) {
- // We can't overwrite a Smi so get address of new heap number into r5.
- __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
- }
-
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
- CpuFeatures::Scope scope(VFP3);
- // Convert smi in r1 to double in d6.
- __ mov(r7, Operand(r1, ASR, kSmiTagSize));
- __ vmov(s13, r7);
- __ vcvt_f64_s32(d6, s13);
- if (!use_fp_registers) {
- __ vmov(r0, r1, d6);
- }
- } else {
- // Write Smi from r1 to r1 and r0 in double format.
- __ mov(r7, Operand(r1));
- ConvertToDoubleStub stub4(r1, r0, r7, r9);
- __ push(lr);
- __ Call(stub4.GetCode(), RelocInfo::CODE_TARGET);
- __ pop(lr);
- }
-
- __ bind(&finished_loading_r1);
- }
-
- if (generate_code_to_calculate_answer || do_the_call.is_linked()) {
- __ bind(&do_the_call);
- // If we are inlining the operation using VFP3 instructions for
- // add, subtract, multiply, or divide, the arguments are in d6 and d7.
- if (use_fp_registers) {
- CpuFeatures::Scope scope(VFP3);
- // ARMv7 VFP3 instructions to implement
- // double precision, add, subtract, multiply, divide.
-
- if (Token::MUL == op_) {
- __ vmul(d5, d6, d7);
- } else if (Token::DIV == op_) {
- __ vdiv(d5, d6, d7);
- } else if (Token::ADD == op_) {
- __ vadd(d5, d6, d7);
- } else if (Token::SUB == op_) {
- __ vsub(d5, d6, d7);
- } else {
- UNREACHABLE();
- }
- __ sub(r0, r5, Operand(kHeapObjectTag));
- __ vstr(d5, r0, HeapNumber::kValueOffset);
- __ add(r0, r0, Operand(kHeapObjectTag));
- __ Ret();
- } else {
- // If we did not inline the operation, then the arguments are in:
- // r0: Left value (least significant part of mantissa).
- // r1: Left value (sign, exponent, top of mantissa).
- // r2: Right value (least significant part of mantissa).
- // r3: Right value (sign, exponent, top of mantissa).
- // r5: Address of heap number for result.
-
- __ push(lr); // For later.
- __ PrepareCallCFunction(4, r4); // Two doubles count as 4 arguments.
- // Call C routine that may not cause GC or other trouble. r5 is callee
- // save.
- __ CallCFunction(
- ExternalReference::double_fp_operation(op_, masm->isolate()), 4);
- // Store answer in the overwritable heap number.
- #if !defined(USE_ARM_EABI)
- // Double returned in fp coprocessor register 0 and 1, encoded as
- // register cr8. Offsets must be divisible by 4 for coprocessor so we
- // need to substract the tag from r5.
- __ sub(r4, r5, Operand(kHeapObjectTag));
- __ stc(p1, cr8, MemOperand(r4, HeapNumber::kValueOffset));
- #else
- // Double returned in registers 0 and 1.
- __ Strd(r0, r1, FieldMemOperand(r5, HeapNumber::kValueOffset));
- #endif
- __ mov(r0, Operand(r5));
- // And we are done.
- __ pop(pc);
- }
- }
- }
-
- if (!generate_code_to_calculate_answer &&
- !slow_reverse.is_linked() &&
- !slow.is_linked()) {
- return;
- }
-
- if (lhs.is(r0)) {
- __ b(&slow);
- __ bind(&slow_reverse);
- __ Swap(r0, r1, ip);
- }
-
- heap_number_map = no_reg; // Don't use this any more from here on.
-
- // We jump to here if something goes wrong (one param is not a number of any
- // sort or new-space allocation fails).
- __ bind(&slow);
-
- // Push arguments to the stack
- __ Push(r1, r0);
-
- if (Token::ADD == op_) {
- // Test for string arguments before calling runtime.
- // r1 : first argument
- // r0 : second argument
- // sp[0] : second argument
- // sp[4] : first argument
-
- Label not_strings, not_string1, string1, string1_smi2;
- __ tst(r1, Operand(kSmiTagMask));
- __ b(eq, ¬_string1);
- __ CompareObjectType(r1, r2, r2, FIRST_NONSTRING_TYPE);
- __ b(ge, ¬_string1);
-
- // First argument is a a string, test second.
- __ tst(r0, Operand(kSmiTagMask));
- __ b(eq, &string1_smi2);
- __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE);
- __ b(ge, &string1);
-
- // First and second argument are strings.
- StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
- __ TailCallStub(&string_add_stub);
-
- __ bind(&string1_smi2);
- // First argument is a string, second is a smi. Try to lookup the number
- // string for the smi in the number string cache.
- NumberToStringStub::GenerateLookupNumberStringCache(
- masm, r0, r2, r4, r5, r6, true, &string1);
-
- // Replace second argument on stack and tailcall string add stub to make
- // the result.
- __ str(r2, MemOperand(sp, 0));
- __ TailCallStub(&string_add_stub);
-
- // Only first argument is a string.
- __ bind(&string1);
- __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_JS);
-
- // First argument was not a string, test second.
- __ bind(¬_string1);
- __ tst(r0, Operand(kSmiTagMask));
- __ b(eq, ¬_strings);
- __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE);
- __ b(ge, ¬_strings);
-
- // Only second argument is a string.
- __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_JS);
-
- __ bind(¬_strings);
- }
-
- __ InvokeBuiltin(builtin, JUMP_JS); // Tail call. No return.
-}
-
-
-// For bitwise ops where the inputs are not both Smis we here try to determine
-// whether both inputs are either Smis or at least heap numbers that can be
-// represented by a 32 bit signed value. We truncate towards zero as required
-// by the ES spec. If this is the case we do the bitwise op and see if the
-// result is a Smi. If so, great, otherwise we try to find a heap number to
-// write the answer into (either by allocating or by overwriting).
-// On entry the operands are in lhs and rhs. On exit the answer is in r0.
-void GenericBinaryOpStub::HandleNonSmiBitwiseOp(MacroAssembler* masm,
- Register lhs,
- Register rhs) {
- Label slow, result_not_a_smi;
- Label rhs_is_smi, lhs_is_smi;
- Label done_checking_rhs, done_checking_lhs;
-
- Register heap_number_map = r6;
- __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
-
- __ tst(lhs, Operand(kSmiTagMask));
- __ b(eq, &lhs_is_smi); // It's a Smi so don't check it's a heap number.
- __ ldr(r4, FieldMemOperand(lhs, HeapNumber::kMapOffset));
- __ cmp(r4, heap_number_map);
- __ b(ne, &slow);
- __ ConvertToInt32(lhs, r3, r5, r4, d0, &slow);
- __ jmp(&done_checking_lhs);
- __ bind(&lhs_is_smi);
- __ mov(r3, Operand(lhs, ASR, 1));
- __ bind(&done_checking_lhs);
-
- __ tst(rhs, Operand(kSmiTagMask));
- __ b(eq, &rhs_is_smi); // It's a Smi so don't check it's a heap number.
- __ ldr(r4, FieldMemOperand(rhs, HeapNumber::kMapOffset));
- __ cmp(r4, heap_number_map);
- __ b(ne, &slow);
- __ ConvertToInt32(rhs, r2, r5, r4, d0, &slow);
- __ jmp(&done_checking_rhs);
- __ bind(&rhs_is_smi);
- __ mov(r2, Operand(rhs, ASR, 1));
- __ bind(&done_checking_rhs);
-
- ASSERT(((lhs.is(r0) && rhs.is(r1)) || (lhs.is(r1) && rhs.is(r0))));
-
- // r0 and r1: Original operands (Smi or heap numbers).
- // r2 and r3: Signed int32 operands.
- switch (op_) {
- case Token::BIT_OR: __ orr(r2, r2, Operand(r3)); break;
- case Token::BIT_XOR: __ eor(r2, r2, Operand(r3)); break;
- case Token::BIT_AND: __ and_(r2, r2, Operand(r3)); break;
- case Token::SAR:
- // Use only the 5 least significant bits of the shift count.
- __ and_(r2, r2, Operand(0x1f));
- __ mov(r2, Operand(r3, ASR, r2));
- break;
- case Token::SHR:
- // Use only the 5 least significant bits of the shift count.
- __ and_(r2, r2, Operand(0x1f));
- __ mov(r2, Operand(r3, LSR, r2), SetCC);
- // SHR is special because it is required to produce a positive answer.
- // The code below for writing into heap numbers isn't capable of writing
- // the register as an unsigned int so we go to slow case if we hit this
- // case.
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
- __ b(mi, &result_not_a_smi);
- } else {
- __ b(mi, &slow);
- }
- break;
- case Token::SHL:
- // Use only the 5 least significant bits of the shift count.
- __ and_(r2, r2, Operand(0x1f));
- __ mov(r2, Operand(r3, LSL, r2));
- break;
- default: UNREACHABLE();
- }
- // check that the *signed* result fits in a smi
- __ add(r3, r2, Operand(0x40000000), SetCC);
- __ b(mi, &result_not_a_smi);
- __ mov(r0, Operand(r2, LSL, kSmiTagSize));
- __ Ret();
-
- Label have_to_allocate, got_a_heap_number;
- __ bind(&result_not_a_smi);
- switch (mode_) {
- case OVERWRITE_RIGHT: {
- __ tst(rhs, Operand(kSmiTagMask));
- __ b(eq, &have_to_allocate);
- __ mov(r5, Operand(rhs));
- break;
- }
- case OVERWRITE_LEFT: {
- __ tst(lhs, Operand(kSmiTagMask));
- __ b(eq, &have_to_allocate);
- __ mov(r5, Operand(lhs));
- break;
- }
- case NO_OVERWRITE: {
- // Get a new heap number in r5. r4 and r7 are scratch.
- __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
- }
- default: break;
- }
- __ bind(&got_a_heap_number);
- // r2: Answer as signed int32.
- // r5: Heap number to write answer into.
-
- // Nothing can go wrong now, so move the heap number to r0, which is the
- // result.
- __ mov(r0, Operand(r5));
-
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
- // Convert the int32 in r2 to the heap number in r0. r3 is corrupted.
- CpuFeatures::Scope scope(VFP3);
- __ vmov(s0, r2);
- if (op_ == Token::SHR) {
- __ vcvt_f64_u32(d0, s0);
- } else {
- __ vcvt_f64_s32(d0, s0);
- }
- __ sub(r3, r0, Operand(kHeapObjectTag));
- __ vstr(d0, r3, HeapNumber::kValueOffset);
- __ Ret();
- } else {
- // Tail call that writes the int32 in r2 to the heap number in r0, using
- // r3 as scratch. r0 is preserved and returned.
- WriteInt32ToHeapNumberStub stub(r2, r0, r3);
- __ TailCallStub(&stub);
- }
-
- if (mode_ != NO_OVERWRITE) {
- __ bind(&have_to_allocate);
- // Get a new heap number in r5. r4 and r7 are scratch.
- __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
- __ jmp(&got_a_heap_number);
- }
-
- // If all else failed then we go to the runtime system.
- __ bind(&slow);
- __ Push(lhs, rhs); // Restore stack.
- switch (op_) {
- case Token::BIT_OR:
- __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS);
- break;
- case Token::BIT_AND:
- __ InvokeBuiltin(Builtins::BIT_AND, JUMP_JS);
- break;
- case Token::BIT_XOR:
- __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_JS);
- break;
- case Token::SAR:
- __ InvokeBuiltin(Builtins::SAR, JUMP_JS);
- break;
- case Token::SHR:
- __ InvokeBuiltin(Builtins::SHR, JUMP_JS);
- break;
- case Token::SHL:
- __ InvokeBuiltin(Builtins::SHL, JUMP_JS);
- break;
- default:
- UNREACHABLE();
- }
-}
-
-
-
-
-// This function takes the known int in a register for the cases
-// where it doesn't know a good trick, and may deliver
-// a result that needs shifting.
-static void MultiplyByKnownIntInStub(
- MacroAssembler* masm,
- Register result,
- Register source,
- Register known_int_register, // Smi tagged.
- int known_int,
- int* required_shift) { // Including Smi tag shift
- switch (known_int) {
- case 3:
- __ add(result, source, Operand(source, LSL, 1));
- *required_shift = 1;
- break;
- case 5:
- __ add(result, source, Operand(source, LSL, 2));
- *required_shift = 1;
- break;
- case 6:
- __ add(result, source, Operand(source, LSL, 1));
- *required_shift = 2;
- break;
- case 7:
- __ rsb(result, source, Operand(source, LSL, 3));
- *required_shift = 1;
- break;
- case 9:
- __ add(result, source, Operand(source, LSL, 3));
- *required_shift = 1;
- break;
- case 10:
- __ add(result, source, Operand(source, LSL, 2));
- *required_shift = 2;
- break;
- default:
- ASSERT(!IsPowerOf2(known_int)); // That would be very inefficient.
- __ mul(result, source, known_int_register);
- *required_shift = 0;
- }
-}
-
-
-// This uses versions of the sum-of-digits-to-see-if-a-number-is-divisible-by-3
-// trick. See http://en.wikipedia.org/wiki/Divisibility_rule
-// Takes the sum of the digits base (mask + 1) repeatedly until we have a
-// number from 0 to mask. On exit the 'eq' condition flags are set if the
-// answer is exactly the mask.
-void IntegerModStub::DigitSum(MacroAssembler* masm,
- Register lhs,
- int mask,
- int shift,
- Label* entry) {
- ASSERT(mask > 0);
- ASSERT(mask <= 0xff); // This ensures we don't need ip to use it.
- Label loop;
- __ bind(&loop);
- __ and_(ip, lhs, Operand(mask));
- __ add(lhs, ip, Operand(lhs, LSR, shift));
- __ bind(entry);
- __ cmp(lhs, Operand(mask));
- __ b(gt, &loop);
-}
-
-
-void IntegerModStub::DigitSum(MacroAssembler* masm,
- Register lhs,
- Register scratch,
- int mask,
- int shift1,
- int shift2,
- Label* entry) {
- ASSERT(mask > 0);
- ASSERT(mask <= 0xff); // This ensures we don't need ip to use it.
- Label loop;
- __ bind(&loop);
- __ bic(scratch, lhs, Operand(mask));
- __ and_(ip, lhs, Operand(mask));
- __ add(lhs, ip, Operand(lhs, LSR, shift1));
- __ add(lhs, lhs, Operand(scratch, LSR, shift2));
- __ bind(entry);
- __ cmp(lhs, Operand(mask));
- __ b(gt, &loop);
-}
-
-
-// Splits the number into two halves (bottom half has shift bits). The top
-// half is subtracted from the bottom half. If the result is negative then
-// rhs is added.
-void IntegerModStub::ModGetInRangeBySubtraction(MacroAssembler* masm,
- Register lhs,
- int shift,
- int rhs) {
- int mask = (1 << shift) - 1;
- __ and_(ip, lhs, Operand(mask));
- __ sub(lhs, ip, Operand(lhs, LSR, shift), SetCC);
- __ add(lhs, lhs, Operand(rhs), LeaveCC, mi);
-}
-
-
-void IntegerModStub::ModReduce(MacroAssembler* masm,
- Register lhs,
- int max,
- int denominator) {
- int limit = denominator;
- while (limit * 2 <= max) limit *= 2;
- while (limit >= denominator) {
- __ cmp(lhs, Operand(limit));
- __ sub(lhs, lhs, Operand(limit), LeaveCC, ge);
- limit >>= 1;
- }
-}
-
-
-void IntegerModStub::ModAnswer(MacroAssembler* masm,
- Register result,
- Register shift_distance,
- Register mask_bits,
- Register sum_of_digits) {
- __ add(result, mask_bits, Operand(sum_of_digits, LSL, shift_distance));
- __ Ret();
-}
-
-
-// See comment for class.
-void IntegerModStub::Generate(MacroAssembler* masm) {
- __ mov(lhs_, Operand(lhs_, LSR, shift_distance_));
- __ bic(odd_number_, odd_number_, Operand(1));
- __ mov(odd_number_, Operand(odd_number_, LSL, 1));
- // We now have (odd_number_ - 1) * 2 in the register.
- // Build a switch out of branches instead of data because it avoids
- // having to teach the assembler about intra-code-object pointers
- // that are not in relative branch instructions.
- Label mod3, mod5, mod7, mod9, mod11, mod13, mod15, mod17, mod19;
- Label mod21, mod23, mod25;
- { Assembler::BlockConstPoolScope block_const_pool(masm);
- __ add(pc, pc, Operand(odd_number_));
- // When you read pc it is always 8 ahead, but when you write it you always
- // write the actual value. So we put in two nops to take up the slack.
- __ nop();
- __ nop();
- __ b(&mod3);
- __ b(&mod5);
- __ b(&mod7);
- __ b(&mod9);
- __ b(&mod11);
- __ b(&mod13);
- __ b(&mod15);
- __ b(&mod17);
- __ b(&mod19);
- __ b(&mod21);
- __ b(&mod23);
- __ b(&mod25);
- }
-
- // For each denominator we find a multiple that is almost only ones
- // when expressed in binary. Then we do the sum-of-digits trick for
- // that number. If the multiple is not 1 then we have to do a little
- // more work afterwards to get the answer into the 0-denominator-1
- // range.
- DigitSum(masm, lhs_, 3, 2, &mod3); // 3 = b11.
- __ sub(lhs_, lhs_, Operand(3), LeaveCC, eq);
- ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
-
- DigitSum(masm, lhs_, 0xf, 4, &mod5); // 5 * 3 = b1111.
- ModGetInRangeBySubtraction(masm, lhs_, 2, 5);
- ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
-
- DigitSum(masm, lhs_, 7, 3, &mod7); // 7 = b111.
- __ sub(lhs_, lhs_, Operand(7), LeaveCC, eq);
- ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
-
- DigitSum(masm, lhs_, 0x3f, 6, &mod9); // 7 * 9 = b111111.
- ModGetInRangeBySubtraction(masm, lhs_, 3, 9);
- ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
-
- DigitSum(masm, lhs_, r5, 0x3f, 6, 3, &mod11); // 5 * 11 = b110111.
- ModReduce(masm, lhs_, 0x3f, 11);
- ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
-
- DigitSum(masm, lhs_, r5, 0xff, 8, 5, &mod13); // 19 * 13 = b11110111.
- ModReduce(masm, lhs_, 0xff, 13);
- ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
-
- DigitSum(masm, lhs_, 0xf, 4, &mod15); // 15 = b1111.
- __ sub(lhs_, lhs_, Operand(15), LeaveCC, eq);
- ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
-
- DigitSum(masm, lhs_, 0xff, 8, &mod17); // 15 * 17 = b11111111.
- ModGetInRangeBySubtraction(masm, lhs_, 4, 17);
- ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
-
- DigitSum(masm, lhs_, r5, 0xff, 8, 5, &mod19); // 13 * 19 = b11110111.
- ModReduce(masm, lhs_, 0xff, 19);
- ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
-
- DigitSum(masm, lhs_, 0x3f, 6, &mod21); // 3 * 21 = b111111.
- ModReduce(masm, lhs_, 0x3f, 21);
- ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
-
- DigitSum(masm, lhs_, r5, 0xff, 8, 7, &mod23); // 11 * 23 = b11111101.
- ModReduce(masm, lhs_, 0xff, 23);
- ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
-
- DigitSum(masm, lhs_, r5, 0x7f, 7, 6, &mod25); // 5 * 25 = b1111101.
- ModReduce(masm, lhs_, 0x7f, 25);
- ModAnswer(masm, result_, shift_distance_, mask_bits_, lhs_);
-}
-
-
-void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
- // lhs_ : x
- // rhs_ : y
- // r0 : result
-
- Register result = r0;
- Register lhs = lhs_;
- Register rhs = rhs_;
-
- // This code can't cope with other register allocations yet.
- ASSERT(result.is(r0) &&
- ((lhs.is(r0) && rhs.is(r1)) ||
- (lhs.is(r1) && rhs.is(r0))));
-
- Register smi_test_reg = r7;
- Register scratch = r9;
-
- // All ops need to know whether we are dealing with two Smis. Set up
- // smi_test_reg to tell us that.
- if (ShouldGenerateSmiCode()) {
- __ orr(smi_test_reg, lhs, Operand(rhs));
- }
-
- switch (op_) {
- case Token::ADD: {
- Label not_smi;
- // Fast path.
- if (ShouldGenerateSmiCode()) {
- STATIC_ASSERT(kSmiTag == 0); // Adjust code below.
- __ tst(smi_test_reg, Operand(kSmiTagMask));
- __ b(ne, ¬_smi);
- __ add(r0, r1, Operand(r0), SetCC); // Add y optimistically.
- // Return if no overflow.
- __ Ret(vc);
- __ sub(r0, r0, Operand(r1)); // Revert optimistic add.
- }
- HandleBinaryOpSlowCases(masm, ¬_smi, lhs, rhs, Builtins::ADD);
- break;
- }
-
- case Token::SUB: {
- Label not_smi;
- // Fast path.
- if (ShouldGenerateSmiCode()) {
- STATIC_ASSERT(kSmiTag == 0); // Adjust code below.
- __ tst(smi_test_reg, Operand(kSmiTagMask));
- __ b(ne, ¬_smi);
- if (lhs.is(r1)) {
- __ sub(r0, r1, Operand(r0), SetCC); // Subtract y optimistically.
- // Return if no overflow.
- __ Ret(vc);
- __ sub(r0, r1, Operand(r0)); // Revert optimistic subtract.
- } else {
- __ sub(r0, r0, Operand(r1), SetCC); // Subtract y optimistically.
- // Return if no overflow.
- __ Ret(vc);
- __ add(r0, r0, Operand(r1)); // Revert optimistic subtract.
- }
- }
- HandleBinaryOpSlowCases(masm, ¬_smi, lhs, rhs, Builtins::SUB);
- break;
- }
-
- case Token::MUL: {
- Label not_smi, slow;
- if (ShouldGenerateSmiCode()) {
- STATIC_ASSERT(kSmiTag == 0); // adjust code below
- __ tst(smi_test_reg, Operand(kSmiTagMask));
- Register scratch2 = smi_test_reg;
- smi_test_reg = no_reg;
- __ b(ne, ¬_smi);
- // Remove tag from one operand (but keep sign), so that result is Smi.
- __ mov(ip, Operand(rhs, ASR, kSmiTagSize));
- // Do multiplication
- // scratch = lower 32 bits of ip * lhs.
- __ smull(scratch, scratch2, lhs, ip);
- // Go slow on overflows (overflow bit is not set).
- __ mov(ip, Operand(scratch, ASR, 31));
- // No overflow if higher 33 bits are identical.
- __ cmp(ip, Operand(scratch2));
- __ b(ne, &slow);
- // Go slow on zero result to handle -0.
- __ tst(scratch, Operand(scratch));
- __ mov(result, Operand(scratch), LeaveCC, ne);
- __ Ret(ne);
- // We need -0 if we were multiplying a negative number with 0 to get 0.
- // We know one of them was zero.
- __ add(scratch2, rhs, Operand(lhs), SetCC);
- __ mov(result, Operand(Smi::FromInt(0)), LeaveCC, pl);
- __ Ret(pl); // Return Smi 0 if the non-zero one was positive.
- // Slow case. We fall through here if we multiplied a negative number
- // with 0, because that would mean we should produce -0.
- __ bind(&slow);
- }
- HandleBinaryOpSlowCases(masm, ¬_smi, lhs, rhs, Builtins::MUL);
- break;
- }
-
- case Token::DIV:
- case Token::MOD: {
- Label not_smi;
- if (ShouldGenerateSmiCode() && specialized_on_rhs_) {
- Label lhs_is_unsuitable;
- __ JumpIfNotSmi(lhs, ¬_smi);
- if (IsPowerOf2(constant_rhs_)) {
- if (op_ == Token::MOD) {
- __ and_(rhs,
- lhs,
- Operand(0x80000000u | ((constant_rhs_ << kSmiTagSize) - 1)),
- SetCC);
- // We now have the answer, but if the input was negative we also
- // have the sign bit. Our work is done if the result is
- // positive or zero:
- if (!rhs.is(r0)) {
- __ mov(r0, rhs, LeaveCC, pl);
- }
- __ Ret(pl);
- // A mod of a negative left hand side must return a negative number.
- // Unfortunately if the answer is 0 then we must return -0. And we
- // already optimistically trashed rhs so we may need to restore it.
- __ eor(rhs, rhs, Operand(0x80000000u), SetCC);
- // Next two instructions are conditional on the answer being -0.
- __ mov(rhs, Operand(Smi::FromInt(constant_rhs_)), LeaveCC, eq);
- __ b(eq, &lhs_is_unsuitable);
- // We need to subtract the dividend. Eg. -3 % 4 == -3.
- __ sub(result, rhs, Operand(Smi::FromInt(constant_rhs_)));
- } else {
- ASSERT(op_ == Token::DIV);
- __ tst(lhs,
- Operand(0x80000000u | ((constant_rhs_ << kSmiTagSize) - 1)));
- __ b(ne, &lhs_is_unsuitable); // Go slow on negative or remainder.
- int shift = 0;
- int d = constant_rhs_;
- while ((d & 1) == 0) {
- d >>= 1;
- shift++;
- }
- __ mov(r0, Operand(lhs, LSR, shift));
- __ bic(r0, r0, Operand(kSmiTagMask));
- }
- } else {
- // Not a power of 2.
- __ tst(lhs, Operand(0x80000000u));
- __ b(ne, &lhs_is_unsuitable);
- // Find a fixed point reciprocal of the divisor so we can divide by
- // multiplying.
- double divisor = 1.0 / constant_rhs_;
- int shift = 32;
- double scale = 4294967296.0; // 1 << 32.
- uint32_t mul;
- // Maximise the precision of the fixed point reciprocal.
- while (true) {
- mul = static_cast<uint32_t>(scale * divisor);
- if (mul >= 0x7fffffff) break;
- scale *= 2.0;
- shift++;
- }
- mul++;
- Register scratch2 = smi_test_reg;
- smi_test_reg = no_reg;
- __ mov(scratch2, Operand(mul));
- __ umull(scratch, scratch2, scratch2, lhs);
- __ mov(scratch2, Operand(scratch2, LSR, shift - 31));
- // scratch2 is lhs / rhs. scratch2 is not Smi tagged.
- // rhs is still the known rhs. rhs is Smi tagged.
- // lhs is still the unkown lhs. lhs is Smi tagged.
- int required_scratch_shift = 0; // Including the Smi tag shift of 1.
- // scratch = scratch2 * rhs.
- MultiplyByKnownIntInStub(masm,
- scratch,
- scratch2,
- rhs,
- constant_rhs_,
- &required_scratch_shift);
- // scratch << required_scratch_shift is now the Smi tagged rhs *
- // (lhs / rhs) where / indicates integer division.
- if (op_ == Token::DIV) {
- __ cmp(lhs, Operand(scratch, LSL, required_scratch_shift));
- __ b(ne, &lhs_is_unsuitable); // There was a remainder.
- __ mov(result, Operand(scratch2, LSL, kSmiTagSize));
- } else {
- ASSERT(op_ == Token::MOD);
- __ sub(result, lhs, Operand(scratch, LSL, required_scratch_shift));
- }
- }
- __ Ret();
- __ bind(&lhs_is_unsuitable);
- } else if (op_ == Token::MOD &&
- runtime_operands_type_ != BinaryOpIC::HEAP_NUMBERS &&
- runtime_operands_type_ != BinaryOpIC::STRINGS) {
- // Do generate a bit of smi code for modulus even though the default for
- // modulus is not to do it, but as the ARM processor has no coprocessor
- // support for modulus checking for smis makes sense. We can handle
- // 1 to 25 times any power of 2. This covers over half the numbers from
- // 1 to 100 including all of the first 25. (Actually the constants < 10
- // are handled above by reciprocal multiplication. We only get here for
- // those cases if the right hand side is not a constant or for cases
- // like 192 which is 3*2^6 and ends up in the 3 case in the integer mod
- // stub.)
- Label slow;
- Label not_power_of_2;
- ASSERT(!ShouldGenerateSmiCode());
- STATIC_ASSERT(kSmiTag == 0); // Adjust code below.
- // Check for two positive smis.
- __ orr(smi_test_reg, lhs, Operand(rhs));
- __ tst(smi_test_reg, Operand(0x80000000u | kSmiTagMask));
- __ b(ne, &slow);
- // Check that rhs is a power of two and not zero.
- Register mask_bits = r3;
- __ sub(scratch, rhs, Operand(1), SetCC);
- __ b(mi, &slow);
- __ and_(mask_bits, rhs, Operand(scratch), SetCC);
- __ b(ne, ¬_power_of_2);
- // Calculate power of two modulus.
- __ and_(result, lhs, Operand(scratch));
- __ Ret();
-
- __ bind(¬_power_of_2);
- __ eor(scratch, scratch, Operand(mask_bits));
- // At least two bits are set in the modulus. The high one(s) are in
- // mask_bits and the low one is scratch + 1.
- __ and_(mask_bits, scratch, Operand(lhs));
- Register shift_distance = scratch;
- scratch = no_reg;
-
- // The rhs consists of a power of 2 multiplied by some odd number.
- // The power-of-2 part we handle by putting the corresponding bits
- // from the lhs in the mask_bits register, and the power in the
- // shift_distance register. Shift distance is never 0 due to Smi
- // tagging.
- __ CountLeadingZeros(r4, shift_distance, shift_distance);
- __ rsb(shift_distance, r4, Operand(32));
-
- // Now we need to find out what the odd number is. The last bit is
- // always 1.
- Register odd_number = r4;
- __ mov(odd_number, Operand(rhs, LSR, shift_distance));
- __ cmp(odd_number, Operand(25));
- __ b(gt, &slow);
-
- IntegerModStub stub(
- result, shift_distance, odd_number, mask_bits, lhs, r5);
- __ Jump(stub.GetCode(), RelocInfo::CODE_TARGET); // Tail call.
-
- __ bind(&slow);
- }
- HandleBinaryOpSlowCases(
- masm,
- ¬_smi,
- lhs,
- rhs,
- op_ == Token::MOD ? Builtins::MOD : Builtins::DIV);
- break;
- }
-
- case Token::BIT_OR:
- case Token::BIT_AND:
- case Token::BIT_XOR:
- case Token::SAR:
- case Token::SHR:
- case Token::SHL: {
- Label slow;
- STATIC_ASSERT(kSmiTag == 0); // adjust code below
- __ tst(smi_test_reg, Operand(kSmiTagMask));
- __ b(ne, &slow);
- Register scratch2 = smi_test_reg;
- smi_test_reg = no_reg;
- switch (op_) {
- case Token::BIT_OR: __ orr(result, rhs, Operand(lhs)); break;
- case Token::BIT_AND: __ and_(result, rhs, Operand(lhs)); break;
- case Token::BIT_XOR: __ eor(result, rhs, Operand(lhs)); break;
- case Token::SAR:
- // Remove tags from right operand.
- __ GetLeastBitsFromSmi(scratch2, rhs, 5);
- __ mov(result, Operand(lhs, ASR, scratch2));
- // Smi tag result.
- __ bic(result, result, Operand(kSmiTagMask));
- break;
- case Token::SHR:
- // Remove tags from operands. We can't do this on a 31 bit number
- // because then the 0s get shifted into bit 30 instead of bit 31.
- __ mov(scratch, Operand(lhs, ASR, kSmiTagSize)); // x
- __ GetLeastBitsFromSmi(scratch2, rhs, 5);
- __ mov(scratch, Operand(scratch, LSR, scratch2));
- // Unsigned shift is not allowed to produce a negative number, so
- // check the sign bit and the sign bit after Smi tagging.
- __ tst(scratch, Operand(0xc0000000));
- __ b(ne, &slow);
- // Smi tag result.
- __ mov(result, Operand(scratch, LSL, kSmiTagSize));
- break;
- case Token::SHL:
- // Remove tags from operands.
- __ mov(scratch, Operand(lhs, ASR, kSmiTagSize)); // x
- __ GetLeastBitsFromSmi(scratch2, rhs, 5);
- __ mov(scratch, Operand(scratch, LSL, scratch2));
- // Check that the signed result fits in a Smi.
- __ add(scratch2, scratch, Operand(0x40000000), SetCC);
- __ b(mi, &slow);
- __ mov(result, Operand(scratch, LSL, kSmiTagSize));
- break;
- default: UNREACHABLE();
- }
- __ Ret();
- __ bind(&slow);
- HandleNonSmiBitwiseOp(masm, lhs, rhs);
- break;
- }
-
- default: UNREACHABLE();
- }
- // This code should be unreachable.
- __ stop("Unreachable");
-
- // Generate an unreachable reference to the DEFAULT stub so that it can be
- // found at the end of this stub when clearing ICs at GC.
- // TODO(kaznacheev): Check performance impact and get rid of this.
- if (runtime_operands_type_ != BinaryOpIC::DEFAULT) {
- GenericBinaryOpStub uninit(MinorKey(), BinaryOpIC::DEFAULT);
- __ CallStub(&uninit);
- }
-}
-
-
-void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
- Label get_result;
-
- __ Push(r1, r0);
-
- __ mov(r2, Operand(Smi::FromInt(MinorKey())));
- __ mov(r1, Operand(Smi::FromInt(op_)));
- __ mov(r0, Operand(Smi::FromInt(runtime_operands_type_)));
- __ Push(r2, r1, r0);
-
- __ TailCallExternalReference(
- ExternalReference(IC_Utility(IC::kBinaryOp_Patch), masm->isolate()),
- 5,
- 1);
-}
-
-
-Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
- GenericBinaryOpStub stub(key, type_info);
- return stub.GetCode();
-}
-
-
Handle<Code> GetTypeRecordingBinaryOpStub(int key,
TRBinaryOpIC::TypeInfo type_info,
TRBinaryOpIC::TypeInfo result_type_info) {
@@ -2887,6 +1817,9 @@
case TRBinaryOpIC::ODDBALL:
GenerateOddballStub(masm);
break;
+ case TRBinaryOpIC::BOTH_STRING:
+ GenerateBothStringStub(masm);
+ break;
case TRBinaryOpIC::STRING:
GenerateStringStub(masm);
break;
@@ -3077,7 +2010,7 @@
// Load left and right operands into d6 and d7 or r0/r1 and r2/r3
// depending on whether VFP3 is available or not.
FloatingPointHelper::Destination destination =
- Isolate::Current()->cpu_features()->IsSupported(VFP3) &&
+ CpuFeatures::IsSupported(VFP3) &&
op_ != Token::MOD ?
FloatingPointHelper::kVFPRegisters :
FloatingPointHelper::kCoreRegisters;
@@ -3132,6 +2065,9 @@
op_,
result,
scratch1);
+ if (FLAG_debug_code) {
+ __ stop("Unreachable code.");
+ }
}
break;
}
@@ -3190,7 +2126,7 @@
// The code below for writing into heap numbers isn't capable of
// writing the register as an unsigned int so we go to slow case if we
// hit this case.
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
__ b(mi, &result_not_a_smi);
} else {
__ b(mi, not_numbers);
@@ -3229,7 +2165,7 @@
// result.
__ mov(r0, Operand(r5));
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
// Convert the int32 in r2 to the heap number in r0. r3 is corrupted. As
// mentioned above SHR needs to always produce a positive result.
CpuFeatures::Scope scope(VFP3);
@@ -3261,6 +2197,7 @@
// requested the code falls through. If number allocation is requested but a
// heap number cannot be allocated the code jumps to the lable gc_required.
void TypeRecordingBinaryOpStub::GenerateSmiCode(MacroAssembler* masm,
+ Label* use_runtime,
Label* gc_required,
SmiCodeGenerateHeapNumberResults allow_heapnumber_results) {
Label not_smis;
@@ -3282,7 +2219,7 @@
// If heap number results are possible generate the result in an allocated
// heap number.
if (allow_heapnumber_results == ALLOW_HEAPNUMBER_RESULTS) {
- GenerateFPOperation(masm, true, NULL, gc_required);
+ GenerateFPOperation(masm, true, use_runtime, gc_required);
}
__ bind(¬_smis);
}
@@ -3294,11 +2231,14 @@
if (result_type_ == TRBinaryOpIC::UNINITIALIZED ||
result_type_ == TRBinaryOpIC::SMI) {
// Only allow smi results.
- GenerateSmiCode(masm, NULL, NO_HEAPNUMBER_RESULTS);
+ GenerateSmiCode(masm, &call_runtime, NULL, NO_HEAPNUMBER_RESULTS);
} else {
// Allow heap number result and don't make a transition if a heap number
// cannot be allocated.
- GenerateSmiCode(masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS);
+ GenerateSmiCode(masm,
+ &call_runtime,
+ &call_runtime,
+ ALLOW_HEAPNUMBER_RESULTS);
}
// Code falls through if the result is not returned as either a smi or heap
@@ -3320,6 +2260,36 @@
}
+void TypeRecordingBinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
+ Label call_runtime;
+ ASSERT(operands_type_ == TRBinaryOpIC::BOTH_STRING);
+ ASSERT(op_ == Token::ADD);
+ // If both arguments are strings, call the string add stub.
+ // Otherwise, do a transition.
+
+ // Registers containing left and right operands respectively.
+ Register left = r1;
+ Register right = r0;
+
+ // Test if left operand is a string.
+ __ JumpIfSmi(left, &call_runtime);
+ __ CompareObjectType(left, r2, r2, FIRST_NONSTRING_TYPE);
+ __ b(ge, &call_runtime);
+
+ // Test if right operand is a string.
+ __ JumpIfSmi(right, &call_runtime);
+ __ CompareObjectType(right, r2, r2, FIRST_NONSTRING_TYPE);
+ __ b(ge, &call_runtime);
+
+ StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_stub);
+
+ __ bind(&call_runtime);
+ GenerateTypeTransition(masm);
+}
+
+
void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
ASSERT(operands_type_ == TRBinaryOpIC::INT32);
@@ -3358,7 +2328,7 @@
// Jump to type transition if they are not. The registers r0 and r1 (right
// and left) are preserved for the runtime call.
FloatingPointHelper::Destination destination =
- Isolate::Current()->cpu_features()->IsSupported(VFP3) &&
+ CpuFeatures::IsSupported(VFP3) &&
op_ != Token::MOD ?
FloatingPointHelper::kVFPRegisters :
FloatingPointHelper::kCoreRegisters;
@@ -3485,6 +2455,9 @@
// Call the C function to handle the double operation.
FloatingPointHelper::CallCCodeForDoubleOperation(
masm, op_, heap_number_result, scratch1);
+ if (FLAG_debug_code) {
+ __ stop("Unreachable code.");
+ }
}
break;
@@ -3545,7 +2518,7 @@
// to return a heap number if we can.
// The non vfp3 code does not support this special case, so jump to
// runtime if we don't support it.
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
__ b(mi,
(result_type_ <= TRBinaryOpIC::INT32) ? &transition
: &return_heap_number);
@@ -3571,16 +2544,16 @@
__ Ret();
__ bind(&return_heap_number);
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
- CpuFeatures::Scope scope(VFP3);
- heap_number_result = r5;
- GenerateHeapResultAllocation(masm,
- heap_number_result,
- heap_number_map,
- scratch1,
- scratch2,
- &call_runtime);
+ heap_number_result = r5;
+ GenerateHeapResultAllocation(masm,
+ heap_number_result,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ &call_runtime);
+ if (CpuFeatures::IsSupported(VFP3)) {
+ CpuFeatures::Scope scope(VFP3);
if (op_ != Token::SHR) {
// Convert the result to a floating point value.
__ vmov(double_scratch.low(), r2);
@@ -3599,6 +2572,7 @@
} else {
// Tail call that writes the int32 in r2 to the heap number in r0, using
// r3 as scratch. r0 is preserved and returned.
+ __ mov(r0, r5);
WriteInt32ToHeapNumberStub stub(r2, r0, r3);
__ TailCallStub(&stub);
}
@@ -3665,7 +2639,7 @@
void TypeRecordingBinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
Label call_runtime, call_string_add_or_runtime;
- GenerateSmiCode(masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS);
+ GenerateSmiCode(masm, &call_runtime, &call_runtime, ALLOW_HEAPNUMBER_RESULTS);
GenerateFPOperation(masm, false, &call_string_add_or_runtime, &call_runtime);
@@ -3806,7 +2780,7 @@
const Register cache_entry = r0;
const bool tagged = (argument_type_ == TAGGED);
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
if (tagged) {
// Argument is a number and is on stack and in r0.
@@ -3894,7 +2868,7 @@
__ vldr(d2, FieldMemOperand(r6, HeapNumber::kValueOffset));
}
__ Ret();
- } // if (Isolate::Current()->cpu_features()->IsSupported(VFP3))
+ } // if (CpuFeatures::IsSupported(VFP3))
__ bind(&calculate);
if (tagged) {
@@ -3903,7 +2877,7 @@
ExternalReference(RuntimeFunction(), masm->isolate());
__ TailCallExternalReference(runtime_function, 1, 1);
} else {
- if (!Isolate::Current()->cpu_features()->IsSupported(VFP3)) UNREACHABLE();
+ if (!CpuFeatures::IsSupported(VFP3)) UNREACHABLE();
CpuFeatures::Scope scope(VFP3);
Label no_update;
@@ -4102,7 +3076,7 @@
__ mov(r0, Operand(r2));
}
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
// Convert the int32 in r1 to the heap number in r0. r2 is corrupted.
CpuFeatures::Scope scope(VFP3);
__ vmov(s0, r1);
@@ -4143,7 +3117,7 @@
void MathPowStub::Generate(MacroAssembler* masm) {
Label call_runtime;
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
Label base_not_smi;
@@ -4737,7 +3711,7 @@
__ b(ne, &slow);
// Null is not instance of anything.
- __ cmp(scratch, Operand(FACTORY->null_value()));
+ __ cmp(scratch, Operand(masm->isolate()->factory()->null_value()));
__ b(ne, &object_not_null);
__ mov(r0, Operand(Smi::FromInt(1)));
__ Ret(HasArgsInRegisters() ? 0 : 2);
@@ -5235,7 +4209,7 @@
__ bind(&failure);
// For failure and exception return null.
- __ mov(r0, Operand(FACTORY->null_value()));
+ __ mov(r0, Operand(masm->isolate()->factory()->null_value()));
__ add(sp, sp, Operand(4 * kPointerSize));
__ Ret();
@@ -5306,6 +4280,8 @@
const int kMaxInlineLength = 100;
Label slowcase;
Label done;
+ Factory* factory = masm->isolate()->factory();
+
__ ldr(r1, MemOperand(sp, kPointerSize * 2));
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagSize == 1);
@@ -5340,7 +4316,7 @@
// Interleave operations for better latency.
__ ldr(r2, ContextOperand(cp, Context::GLOBAL_INDEX));
__ add(r3, r0, Operand(JSRegExpResult::kSize));
- __ mov(r4, Operand(FACTORY->empty_fixed_array()));
+ __ mov(r4, Operand(factory->empty_fixed_array()));
__ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalContextOffset));
__ str(r3, FieldMemOperand(r0, JSObject::kElementsOffset));
__ ldr(r2, ContextOperand(r2, Context::REGEXP_RESULT_MAP_INDEX));
@@ -5361,13 +4337,13 @@
// r5: Number of elements in array, untagged.
// Set map.
- __ mov(r2, Operand(FACTORY->fixed_array_map()));
+ __ mov(r2, Operand(factory->fixed_array_map()));
__ str(r2, FieldMemOperand(r3, HeapObject::kMapOffset));
// Set FixedArray length.
__ mov(r6, Operand(r5, LSL, kSmiTagSize));
__ str(r6, FieldMemOperand(r3, FixedArray::kLengthOffset));
// Fill contents of fixed-array with the-hole.
- __ mov(r2, Operand(FACTORY->the_hole_value()));
+ __ mov(r2, Operand(factory->the_hole_value()));
__ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
// Fill fixed array elements with hole.
// r0: JSArray, tagged.
@@ -6807,7 +5783,7 @@
// Inlining the double comparison and falling back to the general compare
// stub if NaN is involved or VFP3 is unsupported.
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
// Load left and right operand
diff --git a/src/arm/code-stubs-arm.h b/src/arm/code-stubs-arm.h
index 1dde255..0bb0025 100644
--- a/src/arm/code-stubs-arm.h
+++ b/src/arm/code-stubs-arm.h
@@ -71,162 +71,6 @@
};
-class GenericBinaryOpStub : public CodeStub {
- public:
- static const int kUnknownIntValue = -1;
-
- GenericBinaryOpStub(Token::Value op,
- OverwriteMode mode,
- Register lhs,
- Register rhs,
- int constant_rhs = kUnknownIntValue)
- : op_(op),
- mode_(mode),
- lhs_(lhs),
- rhs_(rhs),
- constant_rhs_(constant_rhs),
- specialized_on_rhs_(RhsIsOneWeWantToOptimizeFor(op, constant_rhs)),
- runtime_operands_type_(BinaryOpIC::UNINIT_OR_SMI),
- name_(NULL) { }
-
- GenericBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info)
- : op_(OpBits::decode(key)),
- mode_(ModeBits::decode(key)),
- lhs_(LhsRegister(RegisterBits::decode(key))),
- rhs_(RhsRegister(RegisterBits::decode(key))),
- constant_rhs_(KnownBitsForMinorKey(KnownIntBits::decode(key))),
- specialized_on_rhs_(RhsIsOneWeWantToOptimizeFor(op_, constant_rhs_)),
- runtime_operands_type_(type_info),
- name_(NULL) { }
-
- private:
- Token::Value op_;
- OverwriteMode mode_;
- Register lhs_;
- Register rhs_;
- int constant_rhs_;
- bool specialized_on_rhs_;
- BinaryOpIC::TypeInfo runtime_operands_type_;
- char* name_;
-
- static const int kMaxKnownRhs = 0x40000000;
- static const int kKnownRhsKeyBits = 6;
-
- // Minor key encoding in 17 bits.
- class ModeBits: public BitField<OverwriteMode, 0, 2> {};
- class OpBits: public BitField<Token::Value, 2, 6> {};
- class TypeInfoBits: public BitField<int, 8, 3> {};
- class RegisterBits: public BitField<bool, 11, 1> {};
- class KnownIntBits: public BitField<int, 12, kKnownRhsKeyBits> {};
-
- Major MajorKey() { return GenericBinaryOp; }
- int MinorKey() {
- ASSERT((lhs_.is(r0) && rhs_.is(r1)) ||
- (lhs_.is(r1) && rhs_.is(r0)));
- // Encode the parameters in a unique 18 bit value.
- return OpBits::encode(op_)
- | ModeBits::encode(mode_)
- | KnownIntBits::encode(MinorKeyForKnownInt())
- | TypeInfoBits::encode(runtime_operands_type_)
- | RegisterBits::encode(lhs_.is(r0));
- }
-
- void Generate(MacroAssembler* masm);
- void HandleNonSmiBitwiseOp(MacroAssembler* masm,
- Register lhs,
- Register rhs);
- void HandleBinaryOpSlowCases(MacroAssembler* masm,
- Label* not_smi,
- Register lhs,
- Register rhs,
- const Builtins::JavaScript& builtin);
- void GenerateTypeTransition(MacroAssembler* masm);
-
- static bool RhsIsOneWeWantToOptimizeFor(Token::Value op, int constant_rhs) {
- if (constant_rhs == kUnknownIntValue) return false;
- if (op == Token::DIV) return constant_rhs >= 2 && constant_rhs <= 3;
- if (op == Token::MOD) {
- if (constant_rhs <= 1) return false;
- if (constant_rhs <= 10) return true;
- if (constant_rhs <= kMaxKnownRhs && IsPowerOf2(constant_rhs)) return true;
- return false;
- }
- return false;
- }
-
- int MinorKeyForKnownInt() {
- if (!specialized_on_rhs_) return 0;
- if (constant_rhs_ <= 10) return constant_rhs_ + 1;
- ASSERT(IsPowerOf2(constant_rhs_));
- int key = 12;
- int d = constant_rhs_;
- while ((d & 1) == 0) {
- key++;
- d >>= 1;
- }
- ASSERT(key >= 0 && key < (1 << kKnownRhsKeyBits));
- return key;
- }
-
- int KnownBitsForMinorKey(int key) {
- if (!key) return 0;
- if (key <= 11) return key - 1;
- int d = 1;
- while (key != 12) {
- key--;
- d <<= 1;
- }
- return d;
- }
-
- Register LhsRegister(bool lhs_is_r0) {
- return lhs_is_r0 ? r0 : r1;
- }
-
- Register RhsRegister(bool lhs_is_r0) {
- return lhs_is_r0 ? r1 : r0;
- }
-
- bool HasSmiSmiFastPath() {
- return op_ != Token::DIV;
- }
-
- bool ShouldGenerateSmiCode() {
- return ((op_ != Token::DIV && op_ != Token::MOD) || specialized_on_rhs_) &&
- runtime_operands_type_ != BinaryOpIC::HEAP_NUMBERS &&
- runtime_operands_type_ != BinaryOpIC::STRINGS;
- }
-
- bool ShouldGenerateFPCode() {
- return runtime_operands_type_ != BinaryOpIC::STRINGS;
- }
-
- virtual int GetCodeKind() { return Code::BINARY_OP_IC; }
-
- virtual InlineCacheState GetICState() {
- return BinaryOpIC::ToState(runtime_operands_type_);
- }
-
- const char* GetName();
-
- virtual void FinishCode(Code* code) {
- code->set_binary_op_type(runtime_operands_type_);
- }
-
-#ifdef DEBUG
- void Print() {
- if (!specialized_on_rhs_) {
- PrintF("GenericBinaryOpStub (%s)\n", Token::String(op_));
- } else {
- PrintF("GenericBinaryOpStub (%s by %d)\n",
- Token::String(op_),
- constant_rhs_);
- }
- }
-#endif
-};
-
-
class TypeRecordingBinaryOpStub: public CodeStub {
public:
TypeRecordingBinaryOpStub(Token::Value op, OverwriteMode mode)
@@ -235,7 +79,7 @@
operands_type_(TRBinaryOpIC::UNINITIALIZED),
result_type_(TRBinaryOpIC::UNINITIALIZED),
name_(NULL) {
- use_vfp3_ = Isolate::Current()->cpu_features()->IsSupported(VFP3);
+ use_vfp3_ = CpuFeatures::IsSupported(VFP3);
ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
}
@@ -303,6 +147,7 @@
Label* not_numbers,
Label* gc_required);
void GenerateSmiCode(MacroAssembler* masm,
+ Label* use_runtime,
Label* gc_required,
SmiCodeGenerateHeapNumberResults heapnumber_results);
void GenerateLoadArguments(MacroAssembler* masm);
@@ -313,6 +158,7 @@
void GenerateHeapNumberStub(MacroAssembler* masm);
void GenerateOddballStub(MacroAssembler* masm);
void GenerateStringStub(MacroAssembler* masm);
+ void GenerateBothStringStub(MacroAssembler* masm);
void GenerateGenericStub(MacroAssembler* masm);
void GenerateAddStrings(MacroAssembler* masm);
void GenerateCallRuntime(MacroAssembler* masm);
@@ -413,102 +259,6 @@
};
-// This stub can do a fast mod operation without using fp.
-// It is tail called from the GenericBinaryOpStub and it always
-// returns an answer. It never causes GC so it doesn't need a real frame.
-//
-// The inputs are always positive Smis. This is never called
-// where the denominator is a power of 2. We handle that separately.
-//
-// If we consider the denominator as an odd number multiplied by a power of 2,
-// then:
-// * The exponent (power of 2) is in the shift_distance register.
-// * The odd number is in the odd_number register. It is always in the range
-// of 3 to 25.
-// * The bits from the numerator that are to be copied to the answer (there are
-// shift_distance of them) are in the mask_bits register.
-// * The other bits of the numerator have been shifted down and are in the lhs
-// register.
-class IntegerModStub : public CodeStub {
- public:
- IntegerModStub(Register result,
- Register shift_distance,
- Register odd_number,
- Register mask_bits,
- Register lhs,
- Register scratch)
- : result_(result),
- shift_distance_(shift_distance),
- odd_number_(odd_number),
- mask_bits_(mask_bits),
- lhs_(lhs),
- scratch_(scratch) {
- // We don't code these in the minor key, so they should always be the same.
- // We don't really want to fix that since this stub is rather large and we
- // don't want many copies of it.
- ASSERT(shift_distance_.is(r9));
- ASSERT(odd_number_.is(r4));
- ASSERT(mask_bits_.is(r3));
- ASSERT(scratch_.is(r5));
- }
-
- private:
- Register result_;
- Register shift_distance_;
- Register odd_number_;
- Register mask_bits_;
- Register lhs_;
- Register scratch_;
-
- // Minor key encoding in 16 bits.
- class ResultRegisterBits: public BitField<int, 0, 4> {};
- class LhsRegisterBits: public BitField<int, 4, 4> {};
-
- Major MajorKey() { return IntegerMod; }
- int MinorKey() {
- // Encode the parameters in a unique 16 bit value.
- return ResultRegisterBits::encode(result_.code())
- | LhsRegisterBits::encode(lhs_.code());
- }
-
- void Generate(MacroAssembler* masm);
-
- const char* GetName() { return "IntegerModStub"; }
-
- // Utility functions.
- void DigitSum(MacroAssembler* masm,
- Register lhs,
- int mask,
- int shift,
- Label* entry);
- void DigitSum(MacroAssembler* masm,
- Register lhs,
- Register scratch,
- int mask,
- int shift1,
- int shift2,
- Label* entry);
- void ModGetInRangeBySubtraction(MacroAssembler* masm,
- Register lhs,
- int shift,
- int rhs);
- void ModReduce(MacroAssembler* masm,
- Register lhs,
- int max,
- int denominator);
- void ModAnswer(MacroAssembler* masm,
- Register result,
- Register shift_distance,
- Register mask_bits,
- Register sum_of_digits);
-
-
-#ifdef DEBUG
- void Print() { PrintF("IntegerModStub\n"); }
-#endif
-};
-
-
// This stub can convert a signed int32 to a heap number (double). It does
// not work for int32s that are in Smi range! No GC occurs during this stub
// so you don't have to set up the frame.
diff --git a/src/arm/codegen-arm-inl.h b/src/arm/codegen-arm-inl.h
deleted file mode 100644
index 81ed2d0..0000000
--- a/src/arm/codegen-arm-inl.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-#ifndef V8_ARM_CODEGEN_ARM_INL_H_
-#define V8_ARM_CODEGEN_ARM_INL_H_
-
-#include "virtual-frame-arm.h"
-
-namespace v8 {
-namespace internal {
-
-#define __ ACCESS_MASM(masm_)
-
-// Platform-specific inline functions.
-
-void DeferredCode::Jump() { __ jmp(&entry_label_); }
-void DeferredCode::Branch(Condition cond) { __ b(cond, &entry_label_); }
-
-#undef __
-
-} } // namespace v8::internal
-
-#endif // V8_ARM_CODEGEN_ARM_INL_H_
diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc
index 91c4747..bf748a9 100644
--- a/src/arm/codegen-arm.cc
+++ b/src/arm/codegen-arm.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -29,56 +29,14 @@
#if defined(V8_TARGET_ARCH_ARM)
-#include "bootstrapper.h"
-#include "code-stubs.h"
-#include "codegen-inl.h"
-#include "compiler.h"
-#include "debug.h"
-#include "ic-inl.h"
-#include "jsregexp.h"
-#include "jump-target-inl.h"
-#include "parser.h"
-#include "regexp-macro-assembler.h"
-#include "regexp-stack.h"
-#include "register-allocator-inl.h"
-#include "runtime.h"
-#include "scopes.h"
-#include "stub-cache.h"
-#include "virtual-frame-inl.h"
-#include "virtual-frame-arm-inl.h"
+#include "codegen.h"
namespace v8 {
namespace internal {
-
-#define __ ACCESS_MASM(masm_)
-
-// -------------------------------------------------------------------------
-// Platform-specific DeferredCode functions.
-
-void DeferredCode::SaveRegisters() {
- // On ARM you either have a completely spilled frame or you
- // handle it yourself, but at the moment there's no automation
- // of registers and deferred code.
-}
-
-
-void DeferredCode::RestoreRegisters() {
-}
-
-
// -------------------------------------------------------------------------
// Platform-specific RuntimeCallHelper functions.
-void VirtualFrameRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
- frame_state_->frame()->AssertIsSpilled();
-}
-
-
-void VirtualFrameRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
-}
-
-
void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
masm->EnterInternalFrame();
}
@@ -89,7348 +47,6 @@
}
-// -------------------------------------------------------------------------
-// CodeGenState implementation.
-
-CodeGenState::CodeGenState(CodeGenerator* owner)
- : owner_(owner),
- previous_(owner->state()) {
- owner->set_state(this);
-}
-
-
-ConditionCodeGenState::ConditionCodeGenState(CodeGenerator* owner,
- JumpTarget* true_target,
- JumpTarget* false_target)
- : CodeGenState(owner),
- true_target_(true_target),
- false_target_(false_target) {
- owner->set_state(this);
-}
-
-
-TypeInfoCodeGenState::TypeInfoCodeGenState(CodeGenerator* owner,
- Slot* slot,
- TypeInfo type_info)
- : CodeGenState(owner),
- slot_(slot) {
- owner->set_state(this);
- old_type_info_ = owner->set_type_info(slot, type_info);
-}
-
-
-CodeGenState::~CodeGenState() {
- ASSERT(owner_->state() == this);
- owner_->set_state(previous_);
-}
-
-
-TypeInfoCodeGenState::~TypeInfoCodeGenState() {
- owner()->set_type_info(slot_, old_type_info_);
-}
-
-// -------------------------------------------------------------------------
-// CodeGenerator implementation
-
-CodeGenerator::CodeGenerator(MacroAssembler* masm)
- : deferred_(8),
- masm_(masm),
- info_(NULL),
- frame_(NULL),
- allocator_(NULL),
- cc_reg_(al),
- state_(NULL),
- loop_nesting_(0),
- type_info_(NULL),
- function_return_(JumpTarget::BIDIRECTIONAL),
- function_return_is_shadowed_(false) {
-}
-
-
-// Calling conventions:
-// fp: caller's frame pointer
-// sp: stack pointer
-// r1: called JS function
-// cp: callee's context
-
-void CodeGenerator::Generate(CompilationInfo* info) {
- // Record the position for debugging purposes.
- CodeForFunctionPosition(info->function());
- Comment cmnt(masm_, "[ function compiled by virtual frame code generator");
-
- // Initialize state.
- info_ = info;
-
- int slots = scope()->num_parameters() + scope()->num_stack_slots();
- ScopedVector<TypeInfo> type_info_array(slots);
- for (int i = 0; i < slots; i++) {
- type_info_array[i] = TypeInfo::Unknown();
- }
- type_info_ = &type_info_array;
-
- ASSERT(allocator_ == NULL);
- RegisterAllocator register_allocator(this);
- allocator_ = ®ister_allocator;
- ASSERT(frame_ == NULL);
- frame_ = new VirtualFrame();
- cc_reg_ = al;
-
- // Adjust for function-level loop nesting.
- ASSERT_EQ(0, loop_nesting_);
- loop_nesting_ = info->is_in_loop() ? 1 : 0;
-
- {
- CodeGenState state(this);
-
- // Entry:
- // Stack: receiver, arguments
- // lr: return address
- // fp: caller's frame pointer
- // sp: stack pointer
- // r1: called JS function
- // cp: callee's context
- allocator_->Initialize();
-
-#ifdef DEBUG
- if (strlen(FLAG_stop_at) > 0 &&
- info->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
- frame_->SpillAll();
- __ stop("stop-at");
- }
-#endif
-
- frame_->Enter();
- // tos: code slot
-
- // Allocate space for locals and initialize them. This also checks
- // for stack overflow.
- frame_->AllocateStackSlots();
-
- frame_->AssertIsSpilled();
- int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
- if (heap_slots > 0) {
- // Allocate local context.
- // Get outer context and create a new context based on it.
- __ ldr(r0, frame_->Function());
- frame_->EmitPush(r0);
- if (heap_slots <= FastNewContextStub::kMaximumSlots) {
- FastNewContextStub stub(heap_slots);
- frame_->CallStub(&stub, 1);
- } else {
- frame_->CallRuntime(Runtime::kNewContext, 1);
- }
-
-#ifdef DEBUG
- JumpTarget verified_true;
- __ cmp(r0, cp);
- verified_true.Branch(eq);
- __ stop("NewContext: r0 is expected to be the same as cp");
- verified_true.Bind();
-#endif
- // Update context local.
- __ str(cp, frame_->Context());
- }
-
- // TODO(1241774): Improve this code:
- // 1) only needed if we have a context
- // 2) no need to recompute context ptr every single time
- // 3) don't copy parameter operand code from SlotOperand!
- {
- Comment cmnt2(masm_, "[ copy context parameters into .context");
- // Note that iteration order is relevant here! If we have the same
- // parameter twice (e.g., function (x, y, x)), and that parameter
- // needs to be copied into the context, it must be the last argument
- // passed to the parameter that needs to be copied. This is a rare
- // case so we don't check for it, instead we rely on the copying
- // order: such a parameter is copied repeatedly into the same
- // context location and thus the last value is what is seen inside
- // the function.
- frame_->AssertIsSpilled();
- for (int i = 0; i < scope()->num_parameters(); i++) {
- Variable* par = scope()->parameter(i);
- Slot* slot = par->AsSlot();
- if (slot != NULL && slot->type() == Slot::CONTEXT) {
- ASSERT(!scope()->is_global_scope()); // No params in global scope.
- __ ldr(r1, frame_->ParameterAt(i));
- // Loads r2 with context; used below in RecordWrite.
- __ str(r1, SlotOperand(slot, r2));
- // Load the offset into r3.
- int slot_offset =
- FixedArray::kHeaderSize + slot->index() * kPointerSize;
- __ RecordWrite(r2, Operand(slot_offset), r3, r1);
- }
- }
- }
-
- // Store the arguments object. This must happen after context
- // initialization because the arguments object may be stored in
- // the context.
- if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) {
- StoreArgumentsObject(true);
- }
-
- // Initialize ThisFunction reference if present.
- if (scope()->is_function_scope() && scope()->function() != NULL) {
- frame_->EmitPushRoot(Heap::kTheHoleValueRootIndex);
- StoreToSlot(scope()->function()->AsSlot(), NOT_CONST_INIT);
- }
-
- // Initialize the function return target after the locals are set
- // up, because it needs the expected frame height from the frame.
- function_return_.SetExpectedHeight();
- function_return_is_shadowed_ = false;
-
- // Generate code to 'execute' declarations and initialize functions
- // (source elements). In case of an illegal redeclaration we need to
- // handle that instead of processing the declarations.
- if (scope()->HasIllegalRedeclaration()) {
- Comment cmnt(masm_, "[ illegal redeclarations");
- scope()->VisitIllegalRedeclaration(this);
- } else {
- Comment cmnt(masm_, "[ declarations");
- ProcessDeclarations(scope()->declarations());
- // Bail out if a stack-overflow exception occurred when processing
- // declarations.
- if (HasStackOverflow()) return;
- }
-
- if (FLAG_trace) {
- frame_->CallRuntime(Runtime::kTraceEnter, 0);
- // Ignore the return value.
- }
-
- // Compile the body of the function in a vanilla state. Don't
- // bother compiling all the code if the scope has an illegal
- // redeclaration.
- if (!scope()->HasIllegalRedeclaration()) {
- Comment cmnt(masm_, "[ function body");
-#ifdef DEBUG
- bool is_builtin = Isolate::Current()->bootstrapper()->IsActive();
- bool should_trace =
- is_builtin ? FLAG_trace_builtin_calls : FLAG_trace_calls;
- if (should_trace) {
- frame_->CallRuntime(Runtime::kDebugTrace, 0);
- // Ignore the return value.
- }
-#endif
- VisitStatements(info->function()->body());
- }
- }
-
- // Handle the return from the function.
- if (has_valid_frame()) {
- // If there is a valid frame, control flow can fall off the end of
- // the body. In that case there is an implicit return statement.
- ASSERT(!function_return_is_shadowed_);
- frame_->PrepareForReturn();
- __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
- if (function_return_.is_bound()) {
- function_return_.Jump();
- } else {
- function_return_.Bind();
- GenerateReturnSequence();
- }
- } else if (function_return_.is_linked()) {
- // If the return target has dangling jumps to it, then we have not
- // yet generated the return sequence. This can happen when (a)
- // control does not flow off the end of the body so we did not
- // compile an artificial return statement just above, and (b) there
- // are return statements in the body but (c) they are all shadowed.
- function_return_.Bind();
- GenerateReturnSequence();
- }
-
- // Adjust for function-level loop nesting.
- ASSERT(loop_nesting_ == info->is_in_loop()? 1 : 0);
- loop_nesting_ = 0;
-
- // Code generation state must be reset.
- ASSERT(!has_cc());
- ASSERT(state_ == NULL);
- ASSERT(loop_nesting() == 0);
- ASSERT(!function_return_is_shadowed_);
- function_return_.Unuse();
- DeleteFrame();
-
- // Process any deferred code using the register allocator.
- if (!HasStackOverflow()) {
- ProcessDeferred();
- }
-
- allocator_ = NULL;
- type_info_ = NULL;
-}
-
-
-int CodeGenerator::NumberOfSlot(Slot* slot) {
- if (slot == NULL) return kInvalidSlotNumber;
- switch (slot->type()) {
- case Slot::PARAMETER:
- return slot->index();
- case Slot::LOCAL:
- return slot->index() + scope()->num_parameters();
- default:
- break;
- }
- return kInvalidSlotNumber;
-}
-
-
-MemOperand CodeGenerator::SlotOperand(Slot* slot, Register tmp) {
- // Currently, this assertion will fail if we try to assign to
- // a constant variable that is constant because it is read-only
- // (such as the variable referring to a named function expression).
- // We need to implement assignments to read-only variables.
- // Ideally, we should do this during AST generation (by converting
- // such assignments into expression statements); however, in general
- // we may not be able to make the decision until past AST generation,
- // that is when the entire program is known.
- ASSERT(slot != NULL);
- int index = slot->index();
- switch (slot->type()) {
- case Slot::PARAMETER:
- return frame_->ParameterAt(index);
-
- case Slot::LOCAL:
- return frame_->LocalAt(index);
-
- case Slot::CONTEXT: {
- // Follow the context chain if necessary.
- ASSERT(!tmp.is(cp)); // do not overwrite context register
- Register context = cp;
- int chain_length = scope()->ContextChainLength(slot->var()->scope());
- for (int i = 0; i < chain_length; i++) {
- // Load the closure.
- // (All contexts, even 'with' contexts, have a closure,
- // and it is the same for all contexts inside a function.
- // There is no need to go to the function context first.)
- __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
- // Load the function context (which is the incoming, outer context).
- __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
- context = tmp;
- }
- // We may have a 'with' context now. Get the function context.
- // (In fact this mov may never be the needed, since the scope analysis
- // may not permit a direct context access in this case and thus we are
- // always at a function context. However it is safe to dereference be-
- // cause the function context of a function context is itself. Before
- // deleting this mov we should try to create a counter-example first,
- // though...)
- __ ldr(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
- return ContextOperand(tmp, index);
- }
-
- default:
- UNREACHABLE();
- return MemOperand(r0, 0);
- }
-}
-
-
-MemOperand CodeGenerator::ContextSlotOperandCheckExtensions(
- Slot* slot,
- Register tmp,
- Register tmp2,
- JumpTarget* slow) {
- ASSERT(slot->type() == Slot::CONTEXT);
- Register context = cp;
-
- for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) {
- if (s->num_heap_slots() > 0) {
- if (s->calls_eval()) {
- // Check that extension is NULL.
- __ ldr(tmp2, ContextOperand(context, Context::EXTENSION_INDEX));
- __ tst(tmp2, tmp2);
- slow->Branch(ne);
- }
- __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
- __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
- context = tmp;
- }
- }
- // Check that last extension is NULL.
- __ ldr(tmp2, ContextOperand(context, Context::EXTENSION_INDEX));
- __ tst(tmp2, tmp2);
- slow->Branch(ne);
- __ ldr(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
- return ContextOperand(tmp, slot->index());
-}
-
-
-// Loads a value on TOS. If it is a boolean value, the result may have been
-// (partially) translated into branches, or it may have set the condition
-// code register. If force_cc is set, the value is forced to set the
-// condition code register and no value is pushed. If the condition code
-// register was set, has_cc() is true and cc_reg_ contains the condition to
-// test for 'true'.
-void CodeGenerator::LoadCondition(Expression* x,
- JumpTarget* true_target,
- JumpTarget* false_target,
- bool force_cc) {
- ASSERT(!has_cc());
- int original_height = frame_->height();
-
- { ConditionCodeGenState new_state(this, true_target, false_target);
- Visit(x);
-
- // If we hit a stack overflow, we may not have actually visited
- // the expression. In that case, we ensure that we have a
- // valid-looking frame state because we will continue to generate
- // code as we unwind the C++ stack.
- //
- // It's possible to have both a stack overflow and a valid frame
- // state (eg, a subexpression overflowed, visiting it returned
- // with a dummied frame state, and visiting this expression
- // returned with a normal-looking state).
- if (HasStackOverflow() &&
- has_valid_frame() &&
- !has_cc() &&
- frame_->height() == original_height) {
- true_target->Jump();
- }
- }
- if (force_cc && frame_ != NULL && !has_cc()) {
- // Convert the TOS value to a boolean in the condition code register.
- ToBoolean(true_target, false_target);
- }
- ASSERT(!force_cc || !has_valid_frame() || has_cc());
- ASSERT(!has_valid_frame() ||
- (has_cc() && frame_->height() == original_height) ||
- (!has_cc() && frame_->height() == original_height + 1));
-}
-
-
-void CodeGenerator::Load(Expression* expr) {
- // We generally assume that we are not in a spilled scope for most
- // of the code generator. A failure to ensure this caused issue 815
- // and this assert is designed to catch similar issues.
- frame_->AssertIsNotSpilled();
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- JumpTarget true_target;
- JumpTarget false_target;
- LoadCondition(expr, &true_target, &false_target, false);
-
- if (has_cc()) {
- // Convert cc_reg_ into a boolean value.
- JumpTarget loaded;
- JumpTarget materialize_true;
- materialize_true.Branch(cc_reg_);
- frame_->EmitPushRoot(Heap::kFalseValueRootIndex);
- loaded.Jump();
- materialize_true.Bind();
- frame_->EmitPushRoot(Heap::kTrueValueRootIndex);
- loaded.Bind();
- cc_reg_ = al;
- }
-
- if (true_target.is_linked() || false_target.is_linked()) {
- // We have at least one condition value that has been "translated"
- // into a branch, thus it needs to be loaded explicitly.
- JumpTarget loaded;
- if (frame_ != NULL) {
- loaded.Jump(); // Don't lose the current TOS.
- }
- bool both = true_target.is_linked() && false_target.is_linked();
- // Load "true" if necessary.
- if (true_target.is_linked()) {
- true_target.Bind();
- frame_->EmitPushRoot(Heap::kTrueValueRootIndex);
- }
- // If both "true" and "false" need to be loaded jump across the code for
- // "false".
- if (both) {
- loaded.Jump();
- }
- // Load "false" if necessary.
- if (false_target.is_linked()) {
- false_target.Bind();
- frame_->EmitPushRoot(Heap::kFalseValueRootIndex);
- }
- // A value is loaded on all paths reaching this point.
- loaded.Bind();
- }
- ASSERT(has_valid_frame());
- ASSERT(!has_cc());
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::LoadGlobal() {
- Register reg = frame_->GetTOSRegister();
- __ ldr(reg, GlobalObjectOperand());
- frame_->EmitPush(reg);
-}
-
-
-void CodeGenerator::LoadGlobalReceiver(Register scratch) {
- Register reg = frame_->GetTOSRegister();
- __ ldr(reg, ContextOperand(cp, Context::GLOBAL_INDEX));
- __ ldr(reg,
- FieldMemOperand(reg, GlobalObject::kGlobalReceiverOffset));
- frame_->EmitPush(reg);
-}
-
-
-ArgumentsAllocationMode CodeGenerator::ArgumentsMode() {
- if (scope()->arguments() == NULL) return NO_ARGUMENTS_ALLOCATION;
-
- // In strict mode there is no need for shadow arguments.
- ASSERT(scope()->arguments_shadow() != NULL || scope()->is_strict_mode());
- // We don't want to do lazy arguments allocation for functions that
- // have heap-allocated contexts, because it interfers with the
- // uninitialized const tracking in the context objects.
- return (scope()->num_heap_slots() > 0 || scope()->is_strict_mode())
- ? EAGER_ARGUMENTS_ALLOCATION
- : LAZY_ARGUMENTS_ALLOCATION;
-}
-
-
-void CodeGenerator::StoreArgumentsObject(bool initial) {
- ArgumentsAllocationMode mode = ArgumentsMode();
- ASSERT(mode != NO_ARGUMENTS_ALLOCATION);
-
- Comment cmnt(masm_, "[ store arguments object");
- if (mode == LAZY_ARGUMENTS_ALLOCATION && initial) {
- // When using lazy arguments allocation, we store the hole value
- // as a sentinel indicating that the arguments object hasn't been
- // allocated yet.
- frame_->EmitPushRoot(Heap::kArgumentsMarkerRootIndex);
- } else {
- frame_->SpillAll();
- ArgumentsAccessStub stub(is_strict_mode()
- ? ArgumentsAccessStub::NEW_STRICT
- : ArgumentsAccessStub::NEW_NON_STRICT);
- __ ldr(r2, frame_->Function());
- // The receiver is below the arguments, the return address, and the
- // frame pointer on the stack.
- const int kReceiverDisplacement = 2 + scope()->num_parameters();
- __ add(r1, fp, Operand(kReceiverDisplacement * kPointerSize));
- __ mov(r0, Operand(Smi::FromInt(scope()->num_parameters())));
- frame_->Adjust(3);
- __ Push(r2, r1, r0);
- frame_->CallStub(&stub, 3);
- frame_->EmitPush(r0);
- }
-
- Variable* arguments = scope()->arguments();
- Variable* shadow = scope()->arguments_shadow();
- ASSERT(arguments != NULL && arguments->AsSlot() != NULL);
- ASSERT((shadow != NULL && shadow->AsSlot() != NULL) ||
- scope()->is_strict_mode());
-
- JumpTarget done;
- if (mode == LAZY_ARGUMENTS_ALLOCATION && !initial) {
- // We have to skip storing into the arguments slot if it has
- // already been written to. This can happen if the a function
- // has a local variable named 'arguments'.
- LoadFromSlot(scope()->arguments()->AsSlot(), NOT_INSIDE_TYPEOF);
- Register arguments = frame_->PopToRegister();
- __ LoadRoot(ip, Heap::kArgumentsMarkerRootIndex);
- __ cmp(arguments, ip);
- done.Branch(ne);
- }
- StoreToSlot(arguments->AsSlot(), NOT_CONST_INIT);
- if (mode == LAZY_ARGUMENTS_ALLOCATION) done.Bind();
- if (shadow != NULL) {
- StoreToSlot(shadow->AsSlot(), NOT_CONST_INIT);
- }
-}
-
-
-void CodeGenerator::LoadTypeofExpression(Expression* expr) {
- // Special handling of identifiers as subexpressions of typeof.
- Variable* variable = expr->AsVariableProxy()->AsVariable();
- if (variable != NULL && !variable->is_this() && variable->is_global()) {
- // For a global variable we build the property reference
- // <global>.<variable> and perform a (regular non-contextual) property
- // load to make sure we do not get reference errors.
- Slot global(variable, Slot::CONTEXT, Context::GLOBAL_INDEX);
- Literal key(variable->name());
- Property property(&global, &key, RelocInfo::kNoPosition);
- Reference ref(this, &property);
- ref.GetValue();
- } else if (variable != NULL && variable->AsSlot() != NULL) {
- // For a variable that rewrites to a slot, we signal it is the immediate
- // subexpression of a typeof.
- LoadFromSlotCheckForArguments(variable->AsSlot(), INSIDE_TYPEOF);
- } else {
- // Anything else can be handled normally.
- Load(expr);
- }
-}
-
-
-Reference::Reference(CodeGenerator* cgen,
- Expression* expression,
- bool persist_after_get)
- : cgen_(cgen),
- expression_(expression),
- type_(ILLEGAL),
- persist_after_get_(persist_after_get) {
- // We generally assume that we are not in a spilled scope for most
- // of the code generator. A failure to ensure this caused issue 815
- // and this assert is designed to catch similar issues.
- cgen->frame()->AssertIsNotSpilled();
- cgen->LoadReference(this);
-}
-
-
-Reference::~Reference() {
- ASSERT(is_unloaded() || is_illegal());
-}
-
-
-void CodeGenerator::LoadReference(Reference* ref) {
- Comment cmnt(masm_, "[ LoadReference");
- Expression* e = ref->expression();
- Property* property = e->AsProperty();
- Variable* var = e->AsVariableProxy()->AsVariable();
-
- if (property != NULL) {
- // The expression is either a property or a variable proxy that rewrites
- // to a property.
- Load(property->obj());
- if (property->key()->IsPropertyName()) {
- ref->set_type(Reference::NAMED);
- } else {
- Load(property->key());
- ref->set_type(Reference::KEYED);
- }
- } else if (var != NULL) {
- // The expression is a variable proxy that does not rewrite to a
- // property. Global variables are treated as named property references.
- if (var->is_global()) {
- LoadGlobal();
- ref->set_type(Reference::NAMED);
- } else {
- ASSERT(var->AsSlot() != NULL);
- ref->set_type(Reference::SLOT);
- }
- } else {
- // Anything else is a runtime error.
- Load(e);
- frame_->CallRuntime(Runtime::kThrowReferenceError, 1);
- }
-}
-
-
-void CodeGenerator::UnloadReference(Reference* ref) {
- int size = ref->size();
- ref->set_unloaded();
- if (size == 0) return;
-
- // Pop a reference from the stack while preserving TOS.
- VirtualFrame::RegisterAllocationScope scope(this);
- Comment cmnt(masm_, "[ UnloadReference");
- if (size > 0) {
- Register tos = frame_->PopToRegister();
- frame_->Drop(size);
- frame_->EmitPush(tos);
- }
-}
-
-
-// ECMA-262, section 9.2, page 30: ToBoolean(). Convert the given
-// register to a boolean in the condition code register. The code
-// may jump to 'false_target' in case the register converts to 'false'.
-void CodeGenerator::ToBoolean(JumpTarget* true_target,
- JumpTarget* false_target) {
- // Note: The generated code snippet does not change stack variables.
- // Only the condition code should be set.
- bool known_smi = frame_->KnownSmiAt(0);
- Register tos = frame_->PopToRegister();
-
- // Fast case checks
-
- // Check if the value is 'false'.
- if (!known_smi) {
- __ LoadRoot(ip, Heap::kFalseValueRootIndex);
- __ cmp(tos, ip);
- false_target->Branch(eq);
-
- // Check if the value is 'true'.
- __ LoadRoot(ip, Heap::kTrueValueRootIndex);
- __ cmp(tos, ip);
- true_target->Branch(eq);
-
- // Check if the value is 'undefined'.
- __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
- __ cmp(tos, ip);
- false_target->Branch(eq);
- }
-
- // Check if the value is a smi.
- __ cmp(tos, Operand(Smi::FromInt(0)));
-
- if (!known_smi) {
- false_target->Branch(eq);
- __ tst(tos, Operand(kSmiTagMask));
- true_target->Branch(eq);
-
- // Slow case.
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
- CpuFeatures::Scope scope(VFP3);
- // Implements the slow case by using ToBooleanStub.
- // The ToBooleanStub takes a single argument, and
- // returns a non-zero value for true, or zero for false.
- // Both the argument value and the return value use the
- // register assigned to tos_
- ToBooleanStub stub(tos);
- frame_->CallStub(&stub, 0);
- // Convert the result in "tos" to a condition code.
- __ cmp(tos, Operand(0, RelocInfo::NONE));
- } else {
- // Implements slow case by calling the runtime.
- frame_->EmitPush(tos);
- frame_->CallRuntime(Runtime::kToBool, 1);
- // Convert the result (r0) to a condition code.
- __ LoadRoot(ip, Heap::kFalseValueRootIndex);
- __ cmp(r0, ip);
- }
- }
-
- cc_reg_ = ne;
-}
-
-
-void CodeGenerator::GenericBinaryOperation(Token::Value op,
- OverwriteMode overwrite_mode,
- GenerateInlineSmi inline_smi,
- int constant_rhs) {
- // top of virtual frame: y
- // 2nd elt. on virtual frame : x
- // result : top of virtual frame
-
- // Stub is entered with a call: 'return address' is in lr.
- switch (op) {
- case Token::ADD:
- case Token::SUB:
- if (inline_smi) {
- JumpTarget done;
- Register rhs = frame_->PopToRegister();
- Register lhs = frame_->PopToRegister(rhs);
- Register scratch = VirtualFrame::scratch0();
- __ orr(scratch, rhs, Operand(lhs));
- // Check they are both small and positive.
- __ tst(scratch, Operand(kSmiTagMask | 0xc0000000));
- ASSERT(rhs.is(r0) || lhs.is(r0)); // r0 is free now.
- STATIC_ASSERT(kSmiTag == 0);
- if (op == Token::ADD) {
- __ add(r0, lhs, Operand(rhs), LeaveCC, eq);
- } else {
- __ sub(r0, lhs, Operand(rhs), LeaveCC, eq);
- }
- done.Branch(eq);
- GenericBinaryOpStub stub(op, overwrite_mode, lhs, rhs, constant_rhs);
- frame_->SpillAll();
- frame_->CallStub(&stub, 0);
- done.Bind();
- frame_->EmitPush(r0);
- break;
- } else {
- // Fall through!
- }
- case Token::BIT_OR:
- case Token::BIT_AND:
- case Token::BIT_XOR:
- if (inline_smi) {
- bool rhs_is_smi = frame_->KnownSmiAt(0);
- bool lhs_is_smi = frame_->KnownSmiAt(1);
- Register rhs = frame_->PopToRegister();
- Register lhs = frame_->PopToRegister(rhs);
- Register smi_test_reg;
- Condition cond;
- if (!rhs_is_smi || !lhs_is_smi) {
- if (rhs_is_smi) {
- smi_test_reg = lhs;
- } else if (lhs_is_smi) {
- smi_test_reg = rhs;
- } else {
- smi_test_reg = VirtualFrame::scratch0();
- __ orr(smi_test_reg, rhs, Operand(lhs));
- }
- // Check they are both Smis.
- __ tst(smi_test_reg, Operand(kSmiTagMask));
- cond = eq;
- } else {
- cond = al;
- }
- ASSERT(rhs.is(r0) || lhs.is(r0)); // r0 is free now.
- if (op == Token::BIT_OR) {
- __ orr(r0, lhs, Operand(rhs), LeaveCC, cond);
- } else if (op == Token::BIT_AND) {
- __ and_(r0, lhs, Operand(rhs), LeaveCC, cond);
- } else {
- ASSERT(op == Token::BIT_XOR);
- STATIC_ASSERT(kSmiTag == 0);
- __ eor(r0, lhs, Operand(rhs), LeaveCC, cond);
- }
- if (cond != al) {
- JumpTarget done;
- done.Branch(cond);
- GenericBinaryOpStub stub(op, overwrite_mode, lhs, rhs, constant_rhs);
- frame_->SpillAll();
- frame_->CallStub(&stub, 0);
- done.Bind();
- }
- frame_->EmitPush(r0);
- break;
- } else {
- // Fall through!
- }
- case Token::MUL:
- case Token::DIV:
- case Token::MOD:
- case Token::SHL:
- case Token::SHR:
- case Token::SAR: {
- Register rhs = frame_->PopToRegister();
- Register lhs = frame_->PopToRegister(rhs); // Don't pop to rhs register.
- GenericBinaryOpStub stub(op, overwrite_mode, lhs, rhs, constant_rhs);
- frame_->SpillAll();
- frame_->CallStub(&stub, 0);
- frame_->EmitPush(r0);
- break;
- }
-
- case Token::COMMA: {
- Register scratch = frame_->PopToRegister();
- // Simply discard left value.
- frame_->Drop();
- frame_->EmitPush(scratch);
- break;
- }
-
- default:
- // Other cases should have been handled before this point.
- UNREACHABLE();
- break;
- }
-}
-
-
-class DeferredInlineSmiOperation: public DeferredCode {
- public:
- DeferredInlineSmiOperation(Token::Value op,
- int value,
- bool reversed,
- OverwriteMode overwrite_mode,
- Register tos)
- : op_(op),
- value_(value),
- reversed_(reversed),
- overwrite_mode_(overwrite_mode),
- tos_register_(tos) {
- set_comment("[ DeferredInlinedSmiOperation");
- }
-
- virtual void Generate();
- // This stub makes explicit calls to SaveRegisters(), RestoreRegisters() and
- // Exit(). Currently on ARM SaveRegisters() and RestoreRegisters() are empty
- // methods, it is the responsibility of the deferred code to save and restore
- // registers.
- virtual bool AutoSaveAndRestore() { return false; }
-
- void JumpToNonSmiInput(Condition cond);
- void JumpToAnswerOutOfRange(Condition cond);
-
- private:
- void GenerateNonSmiInput();
- void GenerateAnswerOutOfRange();
- void WriteNonSmiAnswer(Register answer,
- Register heap_number,
- Register scratch);
-
- Token::Value op_;
- int value_;
- bool reversed_;
- OverwriteMode overwrite_mode_;
- Register tos_register_;
- Label non_smi_input_;
- Label answer_out_of_range_;
-};
-
-
-// For bit operations we try harder and handle the case where the input is not
-// a Smi but a 32bits integer without calling the generic stub.
-void DeferredInlineSmiOperation::JumpToNonSmiInput(Condition cond) {
- ASSERT(Token::IsBitOp(op_));
-
- __ b(cond, &non_smi_input_);
-}
-
-
-// For bit operations the result is always 32bits so we handle the case where
-// the result does not fit in a Smi without calling the generic stub.
-void DeferredInlineSmiOperation::JumpToAnswerOutOfRange(Condition cond) {
- ASSERT(Token::IsBitOp(op_));
-
- if ((op_ == Token::SHR) &&
- !Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
- // >>> requires an unsigned to double conversion and the non VFP code
- // does not support this conversion.
- __ b(cond, entry_label());
- } else {
- __ b(cond, &answer_out_of_range_);
- }
-}
-
-
-// On entry the non-constant side of the binary operation is in tos_register_
-// and the constant smi side is nowhere. The tos_register_ is not used by the
-// virtual frame. On exit the answer is in the tos_register_ and the virtual
-// frame is unchanged.
-void DeferredInlineSmiOperation::Generate() {
- VirtualFrame copied_frame(*frame_state()->frame());
- copied_frame.SpillAll();
-
- Register lhs = r1;
- Register rhs = r0;
- switch (op_) {
- case Token::ADD: {
- // Revert optimistic add.
- if (reversed_) {
- __ sub(r0, tos_register_, Operand(Smi::FromInt(value_)));
- __ mov(r1, Operand(Smi::FromInt(value_)));
- } else {
- __ sub(r1, tos_register_, Operand(Smi::FromInt(value_)));
- __ mov(r0, Operand(Smi::FromInt(value_)));
- }
- break;
- }
-
- case Token::SUB: {
- // Revert optimistic sub.
- if (reversed_) {
- __ rsb(r0, tos_register_, Operand(Smi::FromInt(value_)));
- __ mov(r1, Operand(Smi::FromInt(value_)));
- } else {
- __ add(r1, tos_register_, Operand(Smi::FromInt(value_)));
- __ mov(r0, Operand(Smi::FromInt(value_)));
- }
- break;
- }
-
- // For these operations there is no optimistic operation that needs to be
- // reverted.
- case Token::MUL:
- case Token::MOD:
- case Token::BIT_OR:
- case Token::BIT_XOR:
- case Token::BIT_AND:
- case Token::SHL:
- case Token::SHR:
- case Token::SAR: {
- if (tos_register_.is(r1)) {
- __ mov(r0, Operand(Smi::FromInt(value_)));
- } else {
- ASSERT(tos_register_.is(r0));
- __ mov(r1, Operand(Smi::FromInt(value_)));
- }
- if (reversed_ == tos_register_.is(r1)) {
- lhs = r0;
- rhs = r1;
- }
- break;
- }
-
- default:
- // Other cases should have been handled before this point.
- UNREACHABLE();
- break;
- }
-
- GenericBinaryOpStub stub(op_, overwrite_mode_, lhs, rhs, value_);
- __ CallStub(&stub);
-
- // The generic stub returns its value in r0, but that's not
- // necessarily what we want. We want whatever the inlined code
- // expected, which is that the answer is in the same register as
- // the operand was.
- __ Move(tos_register_, r0);
-
- // The tos register was not in use for the virtual frame that we
- // came into this function with, so we can merge back to that frame
- // without trashing it.
- copied_frame.MergeTo(frame_state()->frame());
-
- Exit();
-
- if (non_smi_input_.is_linked()) {
- GenerateNonSmiInput();
- }
-
- if (answer_out_of_range_.is_linked()) {
- GenerateAnswerOutOfRange();
- }
-}
-
-
-// Convert and write the integer answer into heap_number.
-void DeferredInlineSmiOperation::WriteNonSmiAnswer(Register answer,
- Register heap_number,
- Register scratch) {
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
- CpuFeatures::Scope scope(VFP3);
- __ vmov(s0, answer);
- if (op_ == Token::SHR) {
- __ vcvt_f64_u32(d0, s0);
- } else {
- __ vcvt_f64_s32(d0, s0);
- }
- __ sub(scratch, heap_number, Operand(kHeapObjectTag));
- __ vstr(d0, scratch, HeapNumber::kValueOffset);
- } else {
- WriteInt32ToHeapNumberStub stub(answer, heap_number, scratch);
- __ CallStub(&stub);
- }
-}
-
-
-void DeferredInlineSmiOperation::GenerateNonSmiInput() {
- // We know the left hand side is not a Smi and the right hand side is an
- // immediate value (value_) which can be represented as a Smi. We only
- // handle bit operations.
- ASSERT(Token::IsBitOp(op_));
-
- if (FLAG_debug_code) {
- __ Abort("Should not fall through!");
- }
-
- __ bind(&non_smi_input_);
- if (FLAG_debug_code) {
- __ AbortIfSmi(tos_register_);
- }
-
- // This routine uses the registers from r2 to r6. At the moment they are
- // not used by the register allocator, but when they are it should use
- // SpillAll and MergeTo like DeferredInlineSmiOperation::Generate() above.
-
- Register heap_number_map = r7;
- __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
- __ ldr(r3, FieldMemOperand(tos_register_, HeapNumber::kMapOffset));
- __ cmp(r3, heap_number_map);
- // Not a number, fall back to the GenericBinaryOpStub.
- __ b(ne, entry_label());
-
- Register int32 = r2;
- // Not a 32bits signed int, fall back to the GenericBinaryOpStub.
- __ ConvertToInt32(tos_register_, int32, r4, r5, d0, entry_label());
-
- // tos_register_ (r0 or r1): Original heap number.
- // int32: signed 32bits int.
-
- Label result_not_a_smi;
- int shift_value = value_ & 0x1f;
- switch (op_) {
- case Token::BIT_OR: __ orr(int32, int32, Operand(value_)); break;
- case Token::BIT_XOR: __ eor(int32, int32, Operand(value_)); break;
- case Token::BIT_AND: __ and_(int32, int32, Operand(value_)); break;
- case Token::SAR:
- ASSERT(!reversed_);
- if (shift_value != 0) {
- __ mov(int32, Operand(int32, ASR, shift_value));
- }
- break;
- case Token::SHR:
- ASSERT(!reversed_);
- if (shift_value != 0) {
- __ mov(int32, Operand(int32, LSR, shift_value), SetCC);
- } else {
- // SHR is special because it is required to produce a positive answer.
- __ cmp(int32, Operand(0, RelocInfo::NONE));
- }
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
- __ b(mi, &result_not_a_smi);
- } else {
- // Non VFP code cannot convert from unsigned to double, so fall back
- // to GenericBinaryOpStub.
- __ b(mi, entry_label());
- }
- break;
- case Token::SHL:
- ASSERT(!reversed_);
- if (shift_value != 0) {
- __ mov(int32, Operand(int32, LSL, shift_value));
- }
- break;
- default: UNREACHABLE();
- }
- // Check that the *signed* result fits in a smi. Not necessary for AND, SAR
- // if the shift if more than 0 or SHR if the shit is more than 1.
- if (!( (op_ == Token::AND && value_ >= 0) ||
- ((op_ == Token::SAR) && (shift_value > 0)) ||
- ((op_ == Token::SHR) && (shift_value > 1)))) {
- __ add(r3, int32, Operand(0x40000000), SetCC);
- __ b(mi, &result_not_a_smi);
- }
- __ mov(tos_register_, Operand(int32, LSL, kSmiTagSize));
- Exit();
-
- if (result_not_a_smi.is_linked()) {
- __ bind(&result_not_a_smi);
- if (overwrite_mode_ != OVERWRITE_LEFT) {
- ASSERT((overwrite_mode_ == NO_OVERWRITE) ||
- (overwrite_mode_ == OVERWRITE_RIGHT));
- // If the allocation fails, fall back to the GenericBinaryOpStub.
- __ AllocateHeapNumber(r4, r5, r6, heap_number_map, entry_label());
- // Nothing can go wrong now, so overwrite tos.
- __ mov(tos_register_, Operand(r4));
- }
-
- // int32: answer as signed 32bits integer.
- // tos_register_: Heap number to write the answer into.
- WriteNonSmiAnswer(int32, tos_register_, r3);
-
- Exit();
- }
-}
-
-
-void DeferredInlineSmiOperation::GenerateAnswerOutOfRange() {
- // The input from a bitwise operation were Smis but the result cannot fit
- // into a Smi, so we store it into a heap number. VirtualFrame::scratch0()
- // holds the untagged result to be converted. tos_register_ contains the
- // input. See the calls to JumpToAnswerOutOfRange to see how we got here.
- ASSERT(Token::IsBitOp(op_));
- ASSERT(!reversed_);
-
- Register untagged_result = VirtualFrame::scratch0();
-
- if (FLAG_debug_code) {
- __ Abort("Should not fall through!");
- }
-
- __ bind(&answer_out_of_range_);
- if (((value_ & 0x1f) == 0) && (op_ == Token::SHR)) {
- // >>> 0 is a special case where the untagged_result register is not set up
- // yet. We untag the input to get it.
- __ mov(untagged_result, Operand(tos_register_, ASR, kSmiTagSize));
- }
-
- // This routine uses the registers from r2 to r6. At the moment they are
- // not used by the register allocator, but when they are it should use
- // SpillAll and MergeTo like DeferredInlineSmiOperation::Generate() above.
-
- // Allocate the result heap number.
- Register heap_number_map = VirtualFrame::scratch1();
- Register heap_number = r4;
- __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
- // If the allocation fails, fall back to the GenericBinaryOpStub.
- __ AllocateHeapNumber(heap_number, r5, r6, heap_number_map, entry_label());
- WriteNonSmiAnswer(untagged_result, heap_number, r3);
- __ mov(tos_register_, Operand(heap_number));
-
- Exit();
-}
-
-
-static bool PopCountLessThanEqual2(unsigned int x) {
- x &= x - 1;
- return (x & (x - 1)) == 0;
-}
-
-
-// Returns the index of the lowest bit set.
-static int BitPosition(unsigned x) {
- int bit_posn = 0;
- while ((x & 0xf) == 0) {
- bit_posn += 4;
- x >>= 4;
- }
- while ((x & 1) == 0) {
- bit_posn++;
- x >>= 1;
- }
- return bit_posn;
-}
-
-
-// Can we multiply by x with max two shifts and an add.
-// This answers yes to all integers from 2 to 10.
-static bool IsEasyToMultiplyBy(int x) {
- if (x < 2) return false; // Avoid special cases.
- if (x > (Smi::kMaxValue + 1) >> 2) return false; // Almost always overflows.
- if (IsPowerOf2(x)) return true; // Simple shift.
- if (PopCountLessThanEqual2(x)) return true; // Shift and add and shift.
- if (IsPowerOf2(x + 1)) return true; // Patterns like 11111.
- return false;
-}
-
-
-// Can multiply by anything that IsEasyToMultiplyBy returns true for.
-// Source and destination may be the same register. This routine does
-// not set carry and overflow the way a mul instruction would.
-static void InlineMultiplyByKnownInt(MacroAssembler* masm,
- Register source,
- Register destination,
- int known_int) {
- if (IsPowerOf2(known_int)) {
- masm->mov(destination, Operand(source, LSL, BitPosition(known_int)));
- } else if (PopCountLessThanEqual2(known_int)) {
- int first_bit = BitPosition(known_int);
- int second_bit = BitPosition(known_int ^ (1 << first_bit));
- masm->add(destination, source,
- Operand(source, LSL, second_bit - first_bit));
- if (first_bit != 0) {
- masm->mov(destination, Operand(destination, LSL, first_bit));
- }
- } else {
- ASSERT(IsPowerOf2(known_int + 1)); // Patterns like 1111.
- int the_bit = BitPosition(known_int + 1);
- masm->rsb(destination, source, Operand(source, LSL, the_bit));
- }
-}
-
-
-void CodeGenerator::SmiOperation(Token::Value op,
- Handle<Object> value,
- bool reversed,
- OverwriteMode mode) {
- int int_value = Smi::cast(*value)->value();
-
- bool both_sides_are_smi = frame_->KnownSmiAt(0);
-
- bool something_to_inline;
- switch (op) {
- case Token::ADD:
- case Token::SUB:
- case Token::BIT_AND:
- case Token::BIT_OR:
- case Token::BIT_XOR: {
- something_to_inline = true;
- break;
- }
- case Token::SHL: {
- something_to_inline = (both_sides_are_smi || !reversed);
- break;
- }
- case Token::SHR:
- case Token::SAR: {
- if (reversed) {
- something_to_inline = false;
- } else {
- something_to_inline = true;
- }
- break;
- }
- case Token::MOD: {
- if (reversed || int_value < 2 || !IsPowerOf2(int_value)) {
- something_to_inline = false;
- } else {
- something_to_inline = true;
- }
- break;
- }
- case Token::MUL: {
- if (!IsEasyToMultiplyBy(int_value)) {
- something_to_inline = false;
- } else {
- something_to_inline = true;
- }
- break;
- }
- default: {
- something_to_inline = false;
- break;
- }
- }
-
- if (!something_to_inline) {
- if (!reversed) {
- // Push the rhs onto the virtual frame by putting it in a TOS register.
- Register rhs = frame_->GetTOSRegister();
- __ mov(rhs, Operand(value));
- frame_->EmitPush(rhs, TypeInfo::Smi());
- GenericBinaryOperation(op, mode, GENERATE_INLINE_SMI, int_value);
- } else {
- // Pop the rhs, then push lhs and rhs in the right order. Only performs
- // at most one pop, the rest takes place in TOS registers.
- Register lhs = frame_->GetTOSRegister(); // Get reg for pushing.
- Register rhs = frame_->PopToRegister(lhs); // Don't use lhs for this.
- __ mov(lhs, Operand(value));
- frame_->EmitPush(lhs, TypeInfo::Smi());
- TypeInfo t = both_sides_are_smi ? TypeInfo::Smi() : TypeInfo::Unknown();
- frame_->EmitPush(rhs, t);
- GenericBinaryOperation(op, mode, GENERATE_INLINE_SMI,
- GenericBinaryOpStub::kUnknownIntValue);
- }
- return;
- }
-
- // We move the top of stack to a register (normally no move is invoved).
- Register tos = frame_->PopToRegister();
- switch (op) {
- case Token::ADD: {
- DeferredCode* deferred =
- new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
-
- __ add(tos, tos, Operand(value), SetCC);
- deferred->Branch(vs);
- if (!both_sides_are_smi) {
- __ tst(tos, Operand(kSmiTagMask));
- deferred->Branch(ne);
- }
- deferred->BindExit();
- frame_->EmitPush(tos);
- break;
- }
-
- case Token::SUB: {
- DeferredCode* deferred =
- new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
-
- if (reversed) {
- __ rsb(tos, tos, Operand(value), SetCC);
- } else {
- __ sub(tos, tos, Operand(value), SetCC);
- }
- deferred->Branch(vs);
- if (!both_sides_are_smi) {
- __ tst(tos, Operand(kSmiTagMask));
- deferred->Branch(ne);
- }
- deferred->BindExit();
- frame_->EmitPush(tos);
- break;
- }
-
-
- case Token::BIT_OR:
- case Token::BIT_XOR:
- case Token::BIT_AND: {
- if (both_sides_are_smi) {
- switch (op) {
- case Token::BIT_OR: __ orr(tos, tos, Operand(value)); break;
- case Token::BIT_XOR: __ eor(tos, tos, Operand(value)); break;
- case Token::BIT_AND: __ And(tos, tos, Operand(value)); break;
- default: UNREACHABLE();
- }
- frame_->EmitPush(tos, TypeInfo::Smi());
- } else {
- DeferredInlineSmiOperation* deferred =
- new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
- __ tst(tos, Operand(kSmiTagMask));
- deferred->JumpToNonSmiInput(ne);
- switch (op) {
- case Token::BIT_OR: __ orr(tos, tos, Operand(value)); break;
- case Token::BIT_XOR: __ eor(tos, tos, Operand(value)); break;
- case Token::BIT_AND: __ And(tos, tos, Operand(value)); break;
- default: UNREACHABLE();
- }
- deferred->BindExit();
- TypeInfo result_type = TypeInfo::Integer32();
- if (op == Token::BIT_AND && int_value >= 0) {
- result_type = TypeInfo::Smi();
- }
- frame_->EmitPush(tos, result_type);
- }
- break;
- }
-
- case Token::SHL:
- if (reversed) {
- ASSERT(both_sides_are_smi);
- int max_shift = 0;
- int max_result = int_value == 0 ? 1 : int_value;
- while (Smi::IsValid(max_result << 1)) {
- max_shift++;
- max_result <<= 1;
- }
- DeferredCode* deferred =
- new DeferredInlineSmiOperation(op, int_value, true, mode, tos);
- // Mask off the last 5 bits of the shift operand (rhs). This is part
- // of the definition of shift in JS and we know we have a Smi so we
- // can safely do this. The masked version gets passed to the
- // deferred code, but that makes no difference.
- __ and_(tos, tos, Operand(Smi::FromInt(0x1f)));
- __ cmp(tos, Operand(Smi::FromInt(max_shift)));
- deferred->Branch(ge);
- Register scratch = VirtualFrame::scratch0();
- __ mov(scratch, Operand(tos, ASR, kSmiTagSize)); // Untag.
- __ mov(tos, Operand(Smi::FromInt(int_value))); // Load constant.
- __ mov(tos, Operand(tos, LSL, scratch)); // Shift constant.
- deferred->BindExit();
- TypeInfo result = TypeInfo::Integer32();
- frame_->EmitPush(tos, result);
- break;
- }
- // Fall through!
- case Token::SHR:
- case Token::SAR: {
- ASSERT(!reversed);
- int shift_value = int_value & 0x1f;
- TypeInfo result = TypeInfo::Number();
-
- if (op == Token::SHR) {
- if (shift_value > 1) {
- result = TypeInfo::Smi();
- } else if (shift_value > 0) {
- result = TypeInfo::Integer32();
- }
- } else if (op == Token::SAR) {
- if (shift_value > 0) {
- result = TypeInfo::Smi();
- } else {
- result = TypeInfo::Integer32();
- }
- } else {
- ASSERT(op == Token::SHL);
- result = TypeInfo::Integer32();
- }
-
- DeferredInlineSmiOperation* deferred =
- new DeferredInlineSmiOperation(op, shift_value, false, mode, tos);
- if (!both_sides_are_smi) {
- __ tst(tos, Operand(kSmiTagMask));
- deferred->JumpToNonSmiInput(ne);
- }
- switch (op) {
- case Token::SHL: {
- if (shift_value != 0) {
- Register untagged_result = VirtualFrame::scratch0();
- Register scratch = VirtualFrame::scratch1();
- int adjusted_shift = shift_value - kSmiTagSize;
- ASSERT(adjusted_shift >= 0);
-
- if (adjusted_shift != 0) {
- __ mov(untagged_result, Operand(tos, LSL, adjusted_shift));
- } else {
- __ mov(untagged_result, Operand(tos));
- }
- // Check that the *signed* result fits in a smi.
- __ add(scratch, untagged_result, Operand(0x40000000), SetCC);
- deferred->JumpToAnswerOutOfRange(mi);
- __ mov(tos, Operand(untagged_result, LSL, kSmiTagSize));
- }
- break;
- }
- case Token::SHR: {
- if (shift_value != 0) {
- Register untagged_result = VirtualFrame::scratch0();
- // Remove tag.
- __ mov(untagged_result, Operand(tos, ASR, kSmiTagSize));
- __ mov(untagged_result, Operand(untagged_result, LSR, shift_value));
- if (shift_value == 1) {
- // Check that the *unsigned* result fits in a smi.
- // Neither of the two high-order bits can be set:
- // - 0x80000000: high bit would be lost when smi tagging
- // - 0x40000000: this number would convert to negative when Smi
- // tagging.
- // These two cases can only happen with shifts by 0 or 1 when
- // handed a valid smi.
- __ tst(untagged_result, Operand(0xc0000000));
- deferred->JumpToAnswerOutOfRange(ne);
- }
- __ mov(tos, Operand(untagged_result, LSL, kSmiTagSize));
- } else {
- __ cmp(tos, Operand(0, RelocInfo::NONE));
- deferred->JumpToAnswerOutOfRange(mi);
- }
- break;
- }
- case Token::SAR: {
- if (shift_value != 0) {
- // Do the shift and the tag removal in one operation. If the shift
- // is 31 bits (the highest possible value) then we emit the
- // instruction as a shift by 0 which in the ARM ISA means shift
- // arithmetically by 32.
- __ mov(tos, Operand(tos, ASR, (kSmiTagSize + shift_value) & 0x1f));
- __ mov(tos, Operand(tos, LSL, kSmiTagSize));
- }
- break;
- }
- default: UNREACHABLE();
- }
- deferred->BindExit();
- frame_->EmitPush(tos, result);
- break;
- }
-
- case Token::MOD: {
- ASSERT(!reversed);
- ASSERT(int_value >= 2);
- ASSERT(IsPowerOf2(int_value));
- DeferredCode* deferred =
- new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
- unsigned mask = (0x80000000u | kSmiTagMask);
- __ tst(tos, Operand(mask));
- deferred->Branch(ne); // Go to deferred code on non-Smis and negative.
- mask = (int_value << kSmiTagSize) - 1;
- __ and_(tos, tos, Operand(mask));
- deferred->BindExit();
- // Mod of positive power of 2 Smi gives a Smi if the lhs is an integer.
- frame_->EmitPush(
- tos,
- both_sides_are_smi ? TypeInfo::Smi() : TypeInfo::Number());
- break;
- }
-
- case Token::MUL: {
- ASSERT(IsEasyToMultiplyBy(int_value));
- DeferredCode* deferred =
- new DeferredInlineSmiOperation(op, int_value, reversed, mode, tos);
- unsigned max_smi_that_wont_overflow = Smi::kMaxValue / int_value;
- max_smi_that_wont_overflow <<= kSmiTagSize;
- unsigned mask = 0x80000000u;
- while ((mask & max_smi_that_wont_overflow) == 0) {
- mask |= mask >> 1;
- }
- mask |= kSmiTagMask;
- // This does a single mask that checks for a too high value in a
- // conservative way and for a non-Smi. It also filters out negative
- // numbers, unfortunately, but since this code is inline we prefer
- // brevity to comprehensiveness.
- __ tst(tos, Operand(mask));
- deferred->Branch(ne);
- InlineMultiplyByKnownInt(masm_, tos, tos, int_value);
- deferred->BindExit();
- frame_->EmitPush(tos);
- break;
- }
-
- default:
- UNREACHABLE();
- break;
- }
-}
-
-
-void CodeGenerator::Comparison(Condition cond,
- Expression* left,
- Expression* right,
- bool strict) {
- VirtualFrame::RegisterAllocationScope scope(this);
-
- if (left != NULL) Load(left);
- if (right != NULL) Load(right);
-
- // sp[0] : y
- // sp[1] : x
- // result : cc register
-
- // Strict only makes sense for equality comparisons.
- ASSERT(!strict || cond == eq);
-
- Register lhs;
- Register rhs;
-
- bool lhs_is_smi;
- bool rhs_is_smi;
-
- // We load the top two stack positions into registers chosen by the virtual
- // frame. This should keep the register shuffling to a minimum.
- // Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order.
- if (cond == gt || cond == le) {
- cond = ReverseCondition(cond);
- lhs_is_smi = frame_->KnownSmiAt(0);
- rhs_is_smi = frame_->KnownSmiAt(1);
- lhs = frame_->PopToRegister();
- rhs = frame_->PopToRegister(lhs); // Don't pop to the same register again!
- } else {
- rhs_is_smi = frame_->KnownSmiAt(0);
- lhs_is_smi = frame_->KnownSmiAt(1);
- rhs = frame_->PopToRegister();
- lhs = frame_->PopToRegister(rhs); // Don't pop to the same register again!
- }
-
- bool both_sides_are_smi = (lhs_is_smi && rhs_is_smi);
-
- ASSERT(rhs.is(r0) || rhs.is(r1));
- ASSERT(lhs.is(r0) || lhs.is(r1));
-
- JumpTarget exit;
-
- if (!both_sides_are_smi) {
- // Now we have the two sides in r0 and r1. We flush any other registers
- // because the stub doesn't know about register allocation.
- frame_->SpillAll();
- Register scratch = VirtualFrame::scratch0();
- Register smi_test_reg;
- if (lhs_is_smi) {
- smi_test_reg = rhs;
- } else if (rhs_is_smi) {
- smi_test_reg = lhs;
- } else {
- __ orr(scratch, lhs, Operand(rhs));
- smi_test_reg = scratch;
- }
- __ tst(smi_test_reg, Operand(kSmiTagMask));
- JumpTarget smi;
- smi.Branch(eq);
-
- // Perform non-smi comparison by stub.
- // CompareStub takes arguments in r0 and r1, returns <0, >0 or 0 in r0.
- // We call with 0 args because there are 0 on the stack.
- CompareStub stub(cond, strict, NO_SMI_COMPARE_IN_STUB, lhs, rhs);
- frame_->CallStub(&stub, 0);
- __ cmp(r0, Operand(0, RelocInfo::NONE));
- exit.Jump();
-
- smi.Bind();
- }
-
- // Do smi comparisons by pointer comparison.
- __ cmp(lhs, Operand(rhs));
-
- exit.Bind();
- cc_reg_ = cond;
-}
-
-
-// Call the function on the stack with the given arguments.
-void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
- CallFunctionFlags flags,
- int position) {
- // Push the arguments ("left-to-right") on the stack.
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- }
-
- // Record the position for debugging purposes.
- CodeForSourcePosition(position);
-
- // Use the shared code stub to call the function.
- InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub call_function(arg_count, in_loop, flags);
- frame_->CallStub(&call_function, arg_count + 1);
-
- // Restore context and pop function from the stack.
- __ ldr(cp, frame_->Context());
- frame_->Drop(); // discard the TOS
-}
-
-
-void CodeGenerator::CallApplyLazy(Expression* applicand,
- Expression* receiver,
- VariableProxy* arguments,
- int position) {
- // An optimized implementation of expressions of the form
- // x.apply(y, arguments).
- // If the arguments object of the scope has not been allocated,
- // and x.apply is Function.prototype.apply, this optimization
- // just copies y and the arguments of the current function on the
- // stack, as receiver and arguments, and calls x.
- // In the implementation comments, we call x the applicand
- // and y the receiver.
-
- ASSERT(ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION);
- ASSERT(arguments->IsArguments());
-
- // Load applicand.apply onto the stack. This will usually
- // give us a megamorphic load site. Not super, but it works.
- Load(applicand);
- Handle<String> name = FACTORY->LookupAsciiSymbol("apply");
- frame_->Dup();
- frame_->CallLoadIC(name, RelocInfo::CODE_TARGET);
- frame_->EmitPush(r0);
-
- // Load the receiver and the existing arguments object onto the
- // expression stack. Avoid allocating the arguments object here.
- Load(receiver);
- LoadFromSlot(scope()->arguments()->AsSlot(), NOT_INSIDE_TYPEOF);
-
- // At this point the top two stack elements are probably in registers
- // since they were just loaded. Ensure they are in regs and get the
- // regs.
- Register receiver_reg = frame_->Peek2();
- Register arguments_reg = frame_->Peek();
-
- // From now on the frame is spilled.
- frame_->SpillAll();
-
- // Emit the source position information after having loaded the
- // receiver and the arguments.
- CodeForSourcePosition(position);
- // Contents of the stack at this point:
- // sp[0]: arguments object of the current function or the hole.
- // sp[1]: receiver
- // sp[2]: applicand.apply
- // sp[3]: applicand.
-
- // Check if the arguments object has been lazily allocated
- // already. If so, just use that instead of copying the arguments
- // from the stack. This also deals with cases where a local variable
- // named 'arguments' has been introduced.
- JumpTarget slow;
- Label done;
- __ LoadRoot(ip, Heap::kArgumentsMarkerRootIndex);
- __ cmp(ip, arguments_reg);
- slow.Branch(ne);
-
- Label build_args;
- // Get rid of the arguments object probe.
- frame_->Drop();
- // Stack now has 3 elements on it.
- // Contents of stack at this point:
- // sp[0]: receiver - in the receiver_reg register.
- // sp[1]: applicand.apply
- // sp[2]: applicand.
-
- // Check that the receiver really is a JavaScript object.
- __ JumpIfSmi(receiver_reg, &build_args);
- // We allow all JSObjects including JSFunctions. As long as
- // JS_FUNCTION_TYPE is the last instance type and it is right
- // after LAST_JS_OBJECT_TYPE, we do not have to check the upper
- // bound.
- STATIC_ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
- STATIC_ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
- __ CompareObjectType(receiver_reg, r2, r3, FIRST_JS_OBJECT_TYPE);
- __ b(lt, &build_args);
-
- // Check that applicand.apply is Function.prototype.apply.
- __ ldr(r0, MemOperand(sp, kPointerSize));
- __ JumpIfSmi(r0, &build_args);
- __ CompareObjectType(r0, r1, r2, JS_FUNCTION_TYPE);
- __ b(ne, &build_args);
- Handle<Code> apply_code(
- Isolate::Current()->builtins()->builtin(Builtins::kFunctionApply));
- __ ldr(r1, FieldMemOperand(r0, JSFunction::kCodeEntryOffset));
- __ sub(r1, r1, Operand(Code::kHeaderSize - kHeapObjectTag));
- __ cmp(r1, Operand(apply_code));
- __ b(ne, &build_args);
-
- // Check that applicand is a function.
- __ ldr(r1, MemOperand(sp, 2 * kPointerSize));
- __ JumpIfSmi(r1, &build_args);
- __ CompareObjectType(r1, r2, r3, JS_FUNCTION_TYPE);
- __ b(ne, &build_args);
-
- // Copy the arguments to this function possibly from the
- // adaptor frame below it.
- Label invoke, adapted;
- __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
- __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
- __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
- __ b(eq, &adapted);
-
- // No arguments adaptor frame. Copy fixed number of arguments.
- __ mov(r0, Operand(scope()->num_parameters()));
- for (int i = 0; i < scope()->num_parameters(); i++) {
- __ ldr(r2, frame_->ParameterAt(i));
- __ push(r2);
- }
- __ jmp(&invoke);
-
- // Arguments adaptor frame present. Copy arguments from there, but
- // avoid copying too many arguments to avoid stack overflows.
- __ bind(&adapted);
- static const uint32_t kArgumentsLimit = 1 * KB;
- __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
- __ mov(r0, Operand(r0, LSR, kSmiTagSize));
- __ mov(r3, r0);
- __ cmp(r0, Operand(kArgumentsLimit));
- __ b(gt, &build_args);
-
- // Loop through the arguments pushing them onto the execution
- // stack. We don't inform the virtual frame of the push, so we don't
- // have to worry about getting rid of the elements from the virtual
- // frame.
- Label loop;
- // r3 is a small non-negative integer, due to the test above.
- __ cmp(r3, Operand(0, RelocInfo::NONE));
- __ b(eq, &invoke);
- // Compute the address of the first argument.
- __ add(r2, r2, Operand(r3, LSL, kPointerSizeLog2));
- __ add(r2, r2, Operand(kPointerSize));
- __ bind(&loop);
- // Post-decrement argument address by kPointerSize on each iteration.
- __ ldr(r4, MemOperand(r2, kPointerSize, NegPostIndex));
- __ push(r4);
- __ sub(r3, r3, Operand(1), SetCC);
- __ b(gt, &loop);
-
- // Invoke the function.
- __ bind(&invoke);
- ParameterCount actual(r0);
- __ InvokeFunction(r1, actual, CALL_FUNCTION);
- // Drop applicand.apply and applicand from the stack, and push
- // the result of the function call, but leave the spilled frame
- // unchanged, with 3 elements, so it is correct when we compile the
- // slow-case code.
- __ add(sp, sp, Operand(2 * kPointerSize));
- __ push(r0);
- // Stack now has 1 element:
- // sp[0]: result
- __ jmp(&done);
-
- // Slow-case: Allocate the arguments object since we know it isn't
- // there, and fall-through to the slow-case where we call
- // applicand.apply.
- __ bind(&build_args);
- // Stack now has 3 elements, because we have jumped from where:
- // sp[0]: receiver
- // sp[1]: applicand.apply
- // sp[2]: applicand.
- StoreArgumentsObject(false);
-
- // Stack and frame now have 4 elements.
- slow.Bind();
-
- // Generic computation of x.apply(y, args) with no special optimization.
- // Flip applicand.apply and applicand on the stack, so
- // applicand looks like the receiver of the applicand.apply call.
- // Then process it as a normal function call.
- __ ldr(r0, MemOperand(sp, 3 * kPointerSize));
- __ ldr(r1, MemOperand(sp, 2 * kPointerSize));
- __ Strd(r0, r1, MemOperand(sp, 2 * kPointerSize));
-
- CallFunctionStub call_function(2, NOT_IN_LOOP, NO_CALL_FUNCTION_FLAGS);
- frame_->CallStub(&call_function, 3);
- // The function and its two arguments have been dropped.
- frame_->Drop(); // Drop the receiver as well.
- frame_->EmitPush(r0);
- frame_->SpillAll(); // A spilled frame is also jumping to label done.
- // Stack now has 1 element:
- // sp[0]: result
- __ bind(&done);
-
- // Restore the context register after a call.
- __ ldr(cp, frame_->Context());
-}
-
-
-void CodeGenerator::Branch(bool if_true, JumpTarget* target) {
- ASSERT(has_cc());
- Condition cond = if_true ? cc_reg_ : NegateCondition(cc_reg_);
- target->Branch(cond);
- cc_reg_ = al;
-}
-
-
-void CodeGenerator::CheckStack() {
- frame_->SpillAll();
- Comment cmnt(masm_, "[ check stack");
- __ LoadRoot(ip, Heap::kStackLimitRootIndex);
- masm_->cmp(sp, Operand(ip));
- StackCheckStub stub;
- // Call the stub if lower.
- masm_->mov(ip,
- Operand(reinterpret_cast<intptr_t>(stub.GetCode().location()),
- RelocInfo::CODE_TARGET),
- LeaveCC,
- lo);
- masm_->Call(ip, lo);
-}
-
-
-void CodeGenerator::VisitStatements(ZoneList<Statement*>* statements) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- for (int i = 0; frame_ != NULL && i < statements->length(); i++) {
- Visit(statements->at(i));
- }
- ASSERT(!has_valid_frame() || frame_->height() == original_height);
-}
-
-
-void CodeGenerator::VisitBlock(Block* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ Block");
- CodeForStatementPosition(node);
- node->break_target()->SetExpectedHeight();
- VisitStatements(node->statements());
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- node->break_target()->Unuse();
- ASSERT(!has_valid_frame() || frame_->height() == original_height);
-}
-
-
-void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
- frame_->EmitPush(cp);
- frame_->EmitPush(Operand(pairs));
- frame_->EmitPush(Operand(Smi::FromInt(is_eval() ? 1 : 0)));
- frame_->EmitPush(Operand(Smi::FromInt(strict_mode_flag())));
-
- frame_->CallRuntime(Runtime::kDeclareGlobals, 4);
- // The result is discarded.
-}
-
-
-void CodeGenerator::VisitDeclaration(Declaration* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ Declaration");
- Variable* var = node->proxy()->var();
- ASSERT(var != NULL); // must have been resolved
- Slot* slot = var->AsSlot();
-
- // If it was not possible to allocate the variable at compile time,
- // we need to "declare" it at runtime to make sure it actually
- // exists in the local context.
- if (slot != NULL && slot->type() == Slot::LOOKUP) {
- // Variables with a "LOOKUP" slot were introduced as non-locals
- // during variable resolution and must have mode DYNAMIC.
- ASSERT(var->is_dynamic());
- // For now, just do a runtime call.
- frame_->EmitPush(cp);
- frame_->EmitPush(Operand(var->name()));
- // Declaration nodes are always declared in only two modes.
- ASSERT(node->mode() == Variable::VAR || node->mode() == Variable::CONST);
- PropertyAttributes attr = node->mode() == Variable::VAR ? NONE : READ_ONLY;
- frame_->EmitPush(Operand(Smi::FromInt(attr)));
- // Push initial value, if any.
- // Note: For variables we must not push an initial value (such as
- // 'undefined') because we may have a (legal) redeclaration and we
- // must not destroy the current value.
- if (node->mode() == Variable::CONST) {
- frame_->EmitPushRoot(Heap::kTheHoleValueRootIndex);
- } else if (node->fun() != NULL) {
- Load(node->fun());
- } else {
- frame_->EmitPush(Operand(0, RelocInfo::NONE));
- }
-
- frame_->CallRuntime(Runtime::kDeclareContextSlot, 4);
- // Ignore the return value (declarations are statements).
-
- ASSERT(frame_->height() == original_height);
- return;
- }
-
- ASSERT(!var->is_global());
-
- // If we have a function or a constant, we need to initialize the variable.
- Expression* val = NULL;
- if (node->mode() == Variable::CONST) {
- val = new Literal(FACTORY->the_hole_value());
- } else {
- val = node->fun(); // NULL if we don't have a function
- }
-
-
- if (val != NULL) {
- WriteBarrierCharacter wb_info =
- val->type()->IsLikelySmi() ? LIKELY_SMI : UNLIKELY_SMI;
- if (val->AsLiteral() != NULL) wb_info = NEVER_NEWSPACE;
- // Set initial value.
- Reference target(this, node->proxy());
- Load(val);
- target.SetValue(NOT_CONST_INIT, wb_info);
-
- // Get rid of the assigned value (declarations are statements).
- frame_->Drop();
- }
- ASSERT(frame_->height() == original_height);
-}
-
-
-void CodeGenerator::VisitExpressionStatement(ExpressionStatement* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ ExpressionStatement");
- CodeForStatementPosition(node);
- Expression* expression = node->expression();
- expression->MarkAsStatement();
- Load(expression);
- frame_->Drop();
- ASSERT(frame_->height() == original_height);
-}
-
-
-void CodeGenerator::VisitEmptyStatement(EmptyStatement* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "// EmptyStatement");
- CodeForStatementPosition(node);
- // nothing to do
- ASSERT(frame_->height() == original_height);
-}
-
-
-void CodeGenerator::VisitIfStatement(IfStatement* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ IfStatement");
- // Generate different code depending on which parts of the if statement
- // are present or not.
- bool has_then_stm = node->HasThenStatement();
- bool has_else_stm = node->HasElseStatement();
-
- CodeForStatementPosition(node);
-
- JumpTarget exit;
- if (has_then_stm && has_else_stm) {
- Comment cmnt(masm_, "[ IfThenElse");
- JumpTarget then;
- JumpTarget else_;
- // if (cond)
- LoadCondition(node->condition(), &then, &else_, true);
- if (frame_ != NULL) {
- Branch(false, &else_);
- }
- // then
- if (frame_ != NULL || then.is_linked()) {
- then.Bind();
- Visit(node->then_statement());
- }
- if (frame_ != NULL) {
- exit.Jump();
- }
- // else
- if (else_.is_linked()) {
- else_.Bind();
- Visit(node->else_statement());
- }
-
- } else if (has_then_stm) {
- Comment cmnt(masm_, "[ IfThen");
- ASSERT(!has_else_stm);
- JumpTarget then;
- // if (cond)
- LoadCondition(node->condition(), &then, &exit, true);
- if (frame_ != NULL) {
- Branch(false, &exit);
- }
- // then
- if (frame_ != NULL || then.is_linked()) {
- then.Bind();
- Visit(node->then_statement());
- }
-
- } else if (has_else_stm) {
- Comment cmnt(masm_, "[ IfElse");
- ASSERT(!has_then_stm);
- JumpTarget else_;
- // if (!cond)
- LoadCondition(node->condition(), &exit, &else_, true);
- if (frame_ != NULL) {
- Branch(true, &exit);
- }
- // else
- if (frame_ != NULL || else_.is_linked()) {
- else_.Bind();
- Visit(node->else_statement());
- }
-
- } else {
- Comment cmnt(masm_, "[ If");
- ASSERT(!has_then_stm && !has_else_stm);
- // if (cond)
- LoadCondition(node->condition(), &exit, &exit, false);
- if (frame_ != NULL) {
- if (has_cc()) {
- cc_reg_ = al;
- } else {
- frame_->Drop();
- }
- }
- }
-
- // end
- if (exit.is_linked()) {
- exit.Bind();
- }
- ASSERT(!has_valid_frame() || frame_->height() == original_height);
-}
-
-
-void CodeGenerator::VisitContinueStatement(ContinueStatement* node) {
- Comment cmnt(masm_, "[ ContinueStatement");
- CodeForStatementPosition(node);
- node->target()->continue_target()->Jump();
-}
-
-
-void CodeGenerator::VisitBreakStatement(BreakStatement* node) {
- Comment cmnt(masm_, "[ BreakStatement");
- CodeForStatementPosition(node);
- node->target()->break_target()->Jump();
-}
-
-
-void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
- Comment cmnt(masm_, "[ ReturnStatement");
-
- CodeForStatementPosition(node);
- Load(node->expression());
- frame_->PopToR0();
- frame_->PrepareForReturn();
- if (function_return_is_shadowed_) {
- function_return_.Jump();
- } else {
- // Pop the result from the frame and prepare the frame for
- // returning thus making it easier to merge.
- if (function_return_.is_bound()) {
- // If the function return label is already bound we reuse the
- // code by jumping to the return site.
- function_return_.Jump();
- } else {
- function_return_.Bind();
- GenerateReturnSequence();
- }
- }
-}
-
-
-void CodeGenerator::GenerateReturnSequence() {
- if (FLAG_trace) {
- // Push the return value on the stack as the parameter.
- // Runtime::TraceExit returns the parameter as it is.
- frame_->EmitPush(r0);
- frame_->CallRuntime(Runtime::kTraceExit, 1);
- }
-
-#ifdef DEBUG
- // Add a label for checking the size of the code used for returning.
- Label check_exit_codesize;
- masm_->bind(&check_exit_codesize);
-#endif
- // Make sure that the constant pool is not emitted inside of the return
- // sequence.
- { Assembler::BlockConstPoolScope block_const_pool(masm_);
- // Tear down the frame which will restore the caller's frame pointer and
- // the link register.
- frame_->Exit();
-
- // Here we use masm_-> instead of the __ macro to avoid the code coverage
- // tool from instrumenting as we rely on the code size here.
- int32_t sp_delta = (scope()->num_parameters() + 1) * kPointerSize;
- masm_->add(sp, sp, Operand(sp_delta));
- masm_->Jump(lr);
- DeleteFrame();
-
-#ifdef DEBUG
- // Check that the size of the code used for returning is large enough
- // for the debugger's requirements.
- ASSERT(Assembler::kJSReturnSequenceInstructions <=
- masm_->InstructionsGeneratedSince(&check_exit_codesize));
-#endif
- }
-}
-
-
-void CodeGenerator::VisitWithEnterStatement(WithEnterStatement* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ WithEnterStatement");
- CodeForStatementPosition(node);
- Load(node->expression());
- if (node->is_catch_block()) {
- frame_->CallRuntime(Runtime::kPushCatchContext, 1);
- } else {
- frame_->CallRuntime(Runtime::kPushContext, 1);
- }
-#ifdef DEBUG
- JumpTarget verified_true;
- __ cmp(r0, cp);
- verified_true.Branch(eq);
- __ stop("PushContext: r0 is expected to be the same as cp");
- verified_true.Bind();
-#endif
- // Update context local.
- __ str(cp, frame_->Context());
- ASSERT(frame_->height() == original_height);
-}
-
-
-void CodeGenerator::VisitWithExitStatement(WithExitStatement* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ WithExitStatement");
- CodeForStatementPosition(node);
- // Pop context.
- __ ldr(cp, ContextOperand(cp, Context::PREVIOUS_INDEX));
- // Update context local.
- __ str(cp, frame_->Context());
- ASSERT(frame_->height() == original_height);
-}
-
-
-void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ SwitchStatement");
- CodeForStatementPosition(node);
- node->break_target()->SetExpectedHeight();
-
- Load(node->tag());
-
- JumpTarget next_test;
- JumpTarget fall_through;
- JumpTarget default_entry;
- JumpTarget default_exit(JumpTarget::BIDIRECTIONAL);
- ZoneList<CaseClause*>* cases = node->cases();
- int length = cases->length();
- CaseClause* default_clause = NULL;
-
- for (int i = 0; i < length; i++) {
- CaseClause* clause = cases->at(i);
- if (clause->is_default()) {
- // Remember the default clause and compile it at the end.
- default_clause = clause;
- continue;
- }
-
- Comment cmnt(masm_, "[ Case clause");
- // Compile the test.
- next_test.Bind();
- next_test.Unuse();
- // Duplicate TOS.
- frame_->Dup();
- Comparison(eq, NULL, clause->label(), true);
- Branch(false, &next_test);
-
- // Before entering the body from the test, remove the switch value from
- // the stack.
- frame_->Drop();
-
- // Label the body so that fall through is enabled.
- if (i > 0 && cases->at(i - 1)->is_default()) {
- default_exit.Bind();
- } else {
- fall_through.Bind();
- fall_through.Unuse();
- }
- VisitStatements(clause->statements());
-
- // If control flow can fall through from the body, jump to the next body
- // or the end of the statement.
- if (frame_ != NULL) {
- if (i < length - 1 && cases->at(i + 1)->is_default()) {
- default_entry.Jump();
- } else {
- fall_through.Jump();
- }
- }
- }
-
- // The final "test" removes the switch value.
- next_test.Bind();
- frame_->Drop();
-
- // If there is a default clause, compile it.
- if (default_clause != NULL) {
- Comment cmnt(masm_, "[ Default clause");
- default_entry.Bind();
- VisitStatements(default_clause->statements());
- // If control flow can fall out of the default and there is a case after
- // it, jump to that case's body.
- if (frame_ != NULL && default_exit.is_bound()) {
- default_exit.Jump();
- }
- }
-
- if (fall_through.is_linked()) {
- fall_through.Bind();
- }
-
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- node->break_target()->Unuse();
- ASSERT(!has_valid_frame() || frame_->height() == original_height);
-}
-
-
-void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ DoWhileStatement");
- CodeForStatementPosition(node);
- node->break_target()->SetExpectedHeight();
- JumpTarget body(JumpTarget::BIDIRECTIONAL);
- IncrementLoopNesting();
-
- // Label the top of the loop for the backward CFG edge. If the test
- // is always true we can use the continue target, and if the test is
- // always false there is no need.
- ConditionAnalysis info = AnalyzeCondition(node->cond());
- switch (info) {
- case ALWAYS_TRUE:
- node->continue_target()->SetExpectedHeight();
- node->continue_target()->Bind();
- break;
- case ALWAYS_FALSE:
- node->continue_target()->SetExpectedHeight();
- break;
- case DONT_KNOW:
- node->continue_target()->SetExpectedHeight();
- body.Bind();
- break;
- }
-
- CheckStack(); // TODO(1222600): ignore if body contains calls.
- Visit(node->body());
-
- // Compile the test.
- switch (info) {
- case ALWAYS_TRUE:
- // If control can fall off the end of the body, jump back to the
- // top.
- if (has_valid_frame()) {
- node->continue_target()->Jump();
- }
- break;
- case ALWAYS_FALSE:
- // If we have a continue in the body, we only have to bind its
- // jump target.
- if (node->continue_target()->is_linked()) {
- node->continue_target()->Bind();
- }
- break;
- case DONT_KNOW:
- // We have to compile the test expression if it can be reached by
- // control flow falling out of the body or via continue.
- if (node->continue_target()->is_linked()) {
- node->continue_target()->Bind();
- }
- if (has_valid_frame()) {
- Comment cmnt(masm_, "[ DoWhileCondition");
- CodeForDoWhileConditionPosition(node);
- LoadCondition(node->cond(), &body, node->break_target(), true);
- if (has_valid_frame()) {
- // A invalid frame here indicates that control did not
- // fall out of the test expression.
- Branch(true, &body);
- }
- }
- break;
- }
-
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- DecrementLoopNesting();
- ASSERT(!has_valid_frame() || frame_->height() == original_height);
-}
-
-
-void CodeGenerator::VisitWhileStatement(WhileStatement* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ WhileStatement");
- CodeForStatementPosition(node);
-
- // If the test is never true and has no side effects there is no need
- // to compile the test or body.
- ConditionAnalysis info = AnalyzeCondition(node->cond());
- if (info == ALWAYS_FALSE) return;
-
- node->break_target()->SetExpectedHeight();
- IncrementLoopNesting();
-
- // Label the top of the loop with the continue target for the backward
- // CFG edge.
- node->continue_target()->SetExpectedHeight();
- node->continue_target()->Bind();
-
- if (info == DONT_KNOW) {
- JumpTarget body(JumpTarget::BIDIRECTIONAL);
- LoadCondition(node->cond(), &body, node->break_target(), true);
- if (has_valid_frame()) {
- // A NULL frame indicates that control did not fall out of the
- // test expression.
- Branch(false, node->break_target());
- }
- if (has_valid_frame() || body.is_linked()) {
- body.Bind();
- }
- }
-
- if (has_valid_frame()) {
- CheckStack(); // TODO(1222600): ignore if body contains calls.
- Visit(node->body());
-
- // If control flow can fall out of the body, jump back to the top.
- if (has_valid_frame()) {
- node->continue_target()->Jump();
- }
- }
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- DecrementLoopNesting();
- ASSERT(!has_valid_frame() || frame_->height() == original_height);
-}
-
-
-void CodeGenerator::VisitForStatement(ForStatement* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ ForStatement");
- CodeForStatementPosition(node);
- if (node->init() != NULL) {
- Visit(node->init());
- }
-
- // If the test is never true there is no need to compile the test or
- // body.
- ConditionAnalysis info = AnalyzeCondition(node->cond());
- if (info == ALWAYS_FALSE) return;
-
- node->break_target()->SetExpectedHeight();
- IncrementLoopNesting();
-
- // We know that the loop index is a smi if it is not modified in the
- // loop body and it is checked against a constant limit in the loop
- // condition. In this case, we reset the static type information of the
- // loop index to smi before compiling the body, the update expression, and
- // the bottom check of the loop condition.
- TypeInfoCodeGenState type_info_scope(this,
- node->is_fast_smi_loop() ?
- node->loop_variable()->AsSlot() :
- NULL,
- TypeInfo::Smi());
-
- // If there is no update statement, label the top of the loop with the
- // continue target, otherwise with the loop target.
- JumpTarget loop(JumpTarget::BIDIRECTIONAL);
- if (node->next() == NULL) {
- node->continue_target()->SetExpectedHeight();
- node->continue_target()->Bind();
- } else {
- node->continue_target()->SetExpectedHeight();
- loop.Bind();
- }
-
- // If the test is always true, there is no need to compile it.
- if (info == DONT_KNOW) {
- JumpTarget body;
- LoadCondition(node->cond(), &body, node->break_target(), true);
- if (has_valid_frame()) {
- Branch(false, node->break_target());
- }
- if (has_valid_frame() || body.is_linked()) {
- body.Bind();
- }
- }
-
- if (has_valid_frame()) {
- CheckStack(); // TODO(1222600): ignore if body contains calls.
- Visit(node->body());
-
- if (node->next() == NULL) {
- // If there is no update statement and control flow can fall out
- // of the loop, jump directly to the continue label.
- if (has_valid_frame()) {
- node->continue_target()->Jump();
- }
- } else {
- // If there is an update statement and control flow can reach it
- // via falling out of the body of the loop or continuing, we
- // compile the update statement.
- if (node->continue_target()->is_linked()) {
- node->continue_target()->Bind();
- }
- if (has_valid_frame()) {
- // Record source position of the statement as this code which is
- // after the code for the body actually belongs to the loop
- // statement and not the body.
- CodeForStatementPosition(node);
- Visit(node->next());
- loop.Jump();
- }
- }
- }
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- DecrementLoopNesting();
- ASSERT(!has_valid_frame() || frame_->height() == original_height);
-}
-
-
-void CodeGenerator::VisitForInStatement(ForInStatement* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ ForInStatement");
- CodeForStatementPosition(node);
-
- JumpTarget primitive;
- JumpTarget jsobject;
- JumpTarget fixed_array;
- JumpTarget entry(JumpTarget::BIDIRECTIONAL);
- JumpTarget end_del_check;
- JumpTarget exit;
-
- // Get the object to enumerate over (converted to JSObject).
- Load(node->enumerable());
-
- VirtualFrame::SpilledScope spilled_scope(frame_);
- // Both SpiderMonkey and kjs ignore null and undefined in contrast
- // to the specification. 12.6.4 mandates a call to ToObject.
- frame_->EmitPop(r0);
- __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
- __ cmp(r0, ip);
- exit.Branch(eq);
- __ LoadRoot(ip, Heap::kNullValueRootIndex);
- __ cmp(r0, ip);
- exit.Branch(eq);
-
- // Stack layout in body:
- // [iteration counter (Smi)]
- // [length of array]
- // [FixedArray]
- // [Map or 0]
- // [Object]
-
- // Check if enumerable is already a JSObject
- __ tst(r0, Operand(kSmiTagMask));
- primitive.Branch(eq);
- __ CompareObjectType(r0, r1, r1, FIRST_JS_OBJECT_TYPE);
- jsobject.Branch(hs);
-
- primitive.Bind();
- frame_->EmitPush(r0);
- frame_->InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS, 1);
-
- jsobject.Bind();
- // Get the set of properties (as a FixedArray or Map).
- // r0: value to be iterated over
- frame_->EmitPush(r0); // Push the object being iterated over.
-
- // Check cache validity in generated code. This is a fast case for
- // the JSObject::IsSimpleEnum cache validity checks. If we cannot
- // guarantee cache validity, call the runtime system to check cache
- // validity or get the property names in a fixed array.
- JumpTarget call_runtime;
- JumpTarget loop(JumpTarget::BIDIRECTIONAL);
- JumpTarget check_prototype;
- JumpTarget use_cache;
- __ mov(r1, Operand(r0));
- loop.Bind();
- // Check that there are no elements.
- __ ldr(r2, FieldMemOperand(r1, JSObject::kElementsOffset));
- __ LoadRoot(r4, Heap::kEmptyFixedArrayRootIndex);
- __ cmp(r2, r4);
- call_runtime.Branch(ne);
- // Check that instance descriptors are not empty so that we can
- // check for an enum cache. Leave the map in r3 for the subsequent
- // prototype load.
- __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
- __ ldr(r2, FieldMemOperand(r3, Map::kInstanceDescriptorsOffset));
- __ LoadRoot(ip, Heap::kEmptyDescriptorArrayRootIndex);
- __ cmp(r2, ip);
- call_runtime.Branch(eq);
- // Check that there in an enum cache in the non-empty instance
- // descriptors. This is the case if the next enumeration index
- // field does not contain a smi.
- __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumerationIndexOffset));
- __ tst(r2, Operand(kSmiTagMask));
- call_runtime.Branch(eq);
- // For all objects but the receiver, check that the cache is empty.
- // r4: empty fixed array root.
- __ cmp(r1, r0);
- check_prototype.Branch(eq);
- __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumCacheBridgeCacheOffset));
- __ cmp(r2, r4);
- call_runtime.Branch(ne);
- check_prototype.Bind();
- // Load the prototype from the map and loop if non-null.
- __ ldr(r1, FieldMemOperand(r3, Map::kPrototypeOffset));
- __ LoadRoot(ip, Heap::kNullValueRootIndex);
- __ cmp(r1, ip);
- loop.Branch(ne);
- // The enum cache is valid. Load the map of the object being
- // iterated over and use the cache for the iteration.
- __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset));
- use_cache.Jump();
-
- call_runtime.Bind();
- // Call the runtime to get the property names for the object.
- frame_->EmitPush(r0); // push the object (slot 4) for the runtime call
- frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1);
-
- // If we got a map from the runtime call, we can do a fast
- // modification check. Otherwise, we got a fixed array, and we have
- // to do a slow check.
- // r0: map or fixed array (result from call to
- // Runtime::kGetPropertyNamesFast)
- __ mov(r2, Operand(r0));
- __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset));
- __ LoadRoot(ip, Heap::kMetaMapRootIndex);
- __ cmp(r1, ip);
- fixed_array.Branch(ne);
-
- use_cache.Bind();
- // Get enum cache
- // r0: map (either the result from a call to
- // Runtime::kGetPropertyNamesFast or has been fetched directly from
- // the object)
- __ mov(r1, Operand(r0));
- __ ldr(r1, FieldMemOperand(r1, Map::kInstanceDescriptorsOffset));
- __ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumerationIndexOffset));
- __ ldr(r2,
- FieldMemOperand(r1, DescriptorArray::kEnumCacheBridgeCacheOffset));
-
- frame_->EmitPush(r0); // map
- frame_->EmitPush(r2); // enum cache bridge cache
- __ ldr(r0, FieldMemOperand(r2, FixedArray::kLengthOffset));
- frame_->EmitPush(r0);
- __ mov(r0, Operand(Smi::FromInt(0)));
- frame_->EmitPush(r0);
- entry.Jump();
-
- fixed_array.Bind();
- __ mov(r1, Operand(Smi::FromInt(0)));
- frame_->EmitPush(r1); // insert 0 in place of Map
- frame_->EmitPush(r0);
-
- // Push the length of the array and the initial index onto the stack.
- __ ldr(r0, FieldMemOperand(r0, FixedArray::kLengthOffset));
- frame_->EmitPush(r0);
- __ mov(r0, Operand(Smi::FromInt(0))); // init index
- frame_->EmitPush(r0);
-
- // Condition.
- entry.Bind();
- // sp[0] : index
- // sp[1] : array/enum cache length
- // sp[2] : array or enum cache
- // sp[3] : 0 or map
- // sp[4] : enumerable
- // Grab the current frame's height for the break and continue
- // targets only after all the state is pushed on the frame.
- node->break_target()->SetExpectedHeight();
- node->continue_target()->SetExpectedHeight();
-
- // Load the current count to r0, load the length to r1.
- __ Ldrd(r0, r1, frame_->ElementAt(0));
- __ cmp(r0, r1); // compare to the array length
- node->break_target()->Branch(hs);
-
- // Get the i'th entry of the array.
- __ ldr(r2, frame_->ElementAt(2));
- __ add(r2, r2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
- __ ldr(r3, MemOperand(r2, r0, LSL, kPointerSizeLog2 - kSmiTagSize));
-
- // Get Map or 0.
- __ ldr(r2, frame_->ElementAt(3));
- // Check if this (still) matches the map of the enumerable.
- // If not, we have to filter the key.
- __ ldr(r1, frame_->ElementAt(4));
- __ ldr(r1, FieldMemOperand(r1, HeapObject::kMapOffset));
- __ cmp(r1, Operand(r2));
- end_del_check.Branch(eq);
-
- // Convert the entry to a string (or null if it isn't a property anymore).
- __ ldr(r0, frame_->ElementAt(4)); // push enumerable
- frame_->EmitPush(r0);
- frame_->EmitPush(r3); // push entry
- frame_->InvokeBuiltin(Builtins::FILTER_KEY, CALL_JS, 2);
- __ mov(r3, Operand(r0), SetCC);
- // If the property has been removed while iterating, we just skip it.
- node->continue_target()->Branch(eq);
-
- end_del_check.Bind();
- // Store the entry in the 'each' expression and take another spin in the
- // loop. r3: i'th entry of the enum cache (or string there of)
- frame_->EmitPush(r3); // push entry
- { VirtualFrame::RegisterAllocationScope scope(this);
- Reference each(this, node->each());
- if (!each.is_illegal()) {
- if (each.size() > 0) {
- // Loading a reference may leave the frame in an unspilled state.
- frame_->SpillAll(); // Sync stack to memory.
- // Get the value (under the reference on the stack) from memory.
- __ ldr(r0, frame_->ElementAt(each.size()));
- frame_->EmitPush(r0);
- each.SetValue(NOT_CONST_INIT, UNLIKELY_SMI);
- frame_->Drop(2); // The result of the set and the extra pushed value.
- } else {
- // If the reference was to a slot we rely on the convenient property
- // that it doesn't matter whether a value (eg, ebx pushed above) is
- // right on top of or right underneath a zero-sized reference.
- each.SetValue(NOT_CONST_INIT, UNLIKELY_SMI);
- frame_->Drop(1); // Drop the result of the set operation.
- }
- }
- }
- // Body.
- CheckStack(); // TODO(1222600): ignore if body contains calls.
- { VirtualFrame::RegisterAllocationScope scope(this);
- Visit(node->body());
- }
-
- // Next. Reestablish a spilled frame in case we are coming here via
- // a continue in the body.
- node->continue_target()->Bind();
- frame_->SpillAll();
- frame_->EmitPop(r0);
- __ add(r0, r0, Operand(Smi::FromInt(1)));
- frame_->EmitPush(r0);
- entry.Jump();
-
- // Cleanup. No need to spill because VirtualFrame::Drop is safe for
- // any frame.
- node->break_target()->Bind();
- frame_->Drop(5);
-
- // Exit.
- exit.Bind();
- node->continue_target()->Unuse();
- node->break_target()->Unuse();
- ASSERT(frame_->height() == original_height);
-}
-
-
-void CodeGenerator::VisitTryCatchStatement(TryCatchStatement* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- VirtualFrame::SpilledScope spilled_scope(frame_);
- Comment cmnt(masm_, "[ TryCatchStatement");
- CodeForStatementPosition(node);
-
- JumpTarget try_block;
- JumpTarget exit;
-
- try_block.Call();
- // --- Catch block ---
- frame_->EmitPush(r0);
-
- // Store the caught exception in the catch variable.
- Variable* catch_var = node->catch_var()->var();
- ASSERT(catch_var != NULL && catch_var->AsSlot() != NULL);
- StoreToSlot(catch_var->AsSlot(), NOT_CONST_INIT);
-
- // Remove the exception from the stack.
- frame_->Drop();
-
- { VirtualFrame::RegisterAllocationScope scope(this);
- VisitStatements(node->catch_block()->statements());
- }
- if (frame_ != NULL) {
- exit.Jump();
- }
-
-
- // --- Try block ---
- try_block.Bind();
-
- frame_->PushTryHandler(TRY_CATCH_HANDLER);
- int handler_height = frame_->height();
-
- // Shadow the labels for all escapes from the try block, including
- // returns. During shadowing, the original label is hidden as the
- // LabelShadow and operations on the original actually affect the
- // shadowing label.
- //
- // We should probably try to unify the escaping labels and the return
- // label.
- int nof_escapes = node->escaping_targets()->length();
- List<ShadowTarget*> shadows(1 + nof_escapes);
-
- // Add the shadow target for the function return.
- static const int kReturnShadowIndex = 0;
- shadows.Add(new ShadowTarget(&function_return_));
- bool function_return_was_shadowed = function_return_is_shadowed_;
- function_return_is_shadowed_ = true;
- ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
-
- // Add the remaining shadow targets.
- for (int i = 0; i < nof_escapes; i++) {
- shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
- }
-
- // Generate code for the statements in the try block.
- { VirtualFrame::RegisterAllocationScope scope(this);
- VisitStatements(node->try_block()->statements());
- }
-
- // Stop the introduced shadowing and count the number of required unlinks.
- // After shadowing stops, the original labels are unshadowed and the
- // LabelShadows represent the formerly shadowing labels.
- bool has_unlinks = false;
- for (int i = 0; i < shadows.length(); i++) {
- shadows[i]->StopShadowing();
- has_unlinks = has_unlinks || shadows[i]->is_linked();
- }
- function_return_is_shadowed_ = function_return_was_shadowed;
-
- // Get an external reference to the handler address.
- ExternalReference handler_address(Isolate::k_handler_address, isolate());
-
- // If we can fall off the end of the try block, unlink from try chain.
- if (has_valid_frame()) {
- // The next handler address is on top of the frame. Unlink from
- // the handler list and drop the rest of this handler from the
- // frame.
- STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
- frame_->EmitPop(r1); // r0 can contain the return value.
- __ mov(r3, Operand(handler_address));
- __ str(r1, MemOperand(r3));
- frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
- if (has_unlinks) {
- exit.Jump();
- }
- }
-
- // Generate unlink code for the (formerly) shadowing labels that have been
- // jumped to. Deallocate each shadow target.
- for (int i = 0; i < shadows.length(); i++) {
- if (shadows[i]->is_linked()) {
- // Unlink from try chain;
- shadows[i]->Bind();
- // Because we can be jumping here (to spilled code) from unspilled
- // code, we need to reestablish a spilled frame at this block.
- frame_->SpillAll();
-
- // Reload sp from the top handler, because some statements that we
- // break from (eg, for...in) may have left stuff on the stack.
- __ mov(r3, Operand(handler_address));
- __ ldr(sp, MemOperand(r3));
- frame_->Forget(frame_->height() - handler_height);
-
- STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
- frame_->EmitPop(r1); // r0 can contain the return value.
- __ str(r1, MemOperand(r3));
- frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
-
- if (!function_return_is_shadowed_ && i == kReturnShadowIndex) {
- frame_->PrepareForReturn();
- }
- shadows[i]->other_target()->Jump();
- }
- }
-
- exit.Bind();
- ASSERT(!has_valid_frame() || frame_->height() == original_height);
-}
-
-
-void CodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- VirtualFrame::SpilledScope spilled_scope(frame_);
- Comment cmnt(masm_, "[ TryFinallyStatement");
- CodeForStatementPosition(node);
-
- // State: Used to keep track of reason for entering the finally
- // block. Should probably be extended to hold information for
- // break/continue from within the try block.
- enum { FALLING, THROWING, JUMPING };
-
- JumpTarget try_block;
- JumpTarget finally_block;
-
- try_block.Call();
-
- frame_->EmitPush(r0); // save exception object on the stack
- // In case of thrown exceptions, this is where we continue.
- __ mov(r2, Operand(Smi::FromInt(THROWING)));
- finally_block.Jump();
-
- // --- Try block ---
- try_block.Bind();
-
- frame_->PushTryHandler(TRY_FINALLY_HANDLER);
- int handler_height = frame_->height();
-
- // Shadow the labels for all escapes from the try block, including
- // returns. Shadowing hides the original label as the LabelShadow and
- // operations on the original actually affect the shadowing label.
- //
- // We should probably try to unify the escaping labels and the return
- // label.
- int nof_escapes = node->escaping_targets()->length();
- List<ShadowTarget*> shadows(1 + nof_escapes);
-
- // Add the shadow target for the function return.
- static const int kReturnShadowIndex = 0;
- shadows.Add(new ShadowTarget(&function_return_));
- bool function_return_was_shadowed = function_return_is_shadowed_;
- function_return_is_shadowed_ = true;
- ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
-
- // Add the remaining shadow targets.
- for (int i = 0; i < nof_escapes; i++) {
- shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
- }
-
- // Generate code for the statements in the try block.
- { VirtualFrame::RegisterAllocationScope scope(this);
- VisitStatements(node->try_block()->statements());
- }
-
- // Stop the introduced shadowing and count the number of required unlinks.
- // After shadowing stops, the original labels are unshadowed and the
- // LabelShadows represent the formerly shadowing labels.
- int nof_unlinks = 0;
- for (int i = 0; i < shadows.length(); i++) {
- shadows[i]->StopShadowing();
- if (shadows[i]->is_linked()) nof_unlinks++;
- }
- function_return_is_shadowed_ = function_return_was_shadowed;
-
- // Get an external reference to the handler address.
- ExternalReference handler_address(Isolate::k_handler_address, isolate());
-
- // If we can fall off the end of the try block, unlink from the try
- // chain and set the state on the frame to FALLING.
- if (has_valid_frame()) {
- // The next handler address is on top of the frame.
- STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
- frame_->EmitPop(r1);
- __ mov(r3, Operand(handler_address));
- __ str(r1, MemOperand(r3));
- frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
-
- // Fake a top of stack value (unneeded when FALLING) and set the
- // state in r2, then jump around the unlink blocks if any.
- __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
- frame_->EmitPush(r0);
- __ mov(r2, Operand(Smi::FromInt(FALLING)));
- if (nof_unlinks > 0) {
- finally_block.Jump();
- }
- }
-
- // Generate code to unlink and set the state for the (formerly)
- // shadowing targets that have been jumped to.
- for (int i = 0; i < shadows.length(); i++) {
- if (shadows[i]->is_linked()) {
- // If we have come from the shadowed return, the return value is
- // in (a non-refcounted reference to) r0. We must preserve it
- // until it is pushed.
- //
- // Because we can be jumping here (to spilled code) from
- // unspilled code, we need to reestablish a spilled frame at
- // this block.
- shadows[i]->Bind();
- frame_->SpillAll();
-
- // Reload sp from the top handler, because some statements that
- // we break from (eg, for...in) may have left stuff on the
- // stack.
- __ mov(r3, Operand(handler_address));
- __ ldr(sp, MemOperand(r3));
- frame_->Forget(frame_->height() - handler_height);
-
- // Unlink this handler and drop it from the frame. The next
- // handler address is currently on top of the frame.
- STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
- frame_->EmitPop(r1);
- __ str(r1, MemOperand(r3));
- frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
-
- if (i == kReturnShadowIndex) {
- // If this label shadowed the function return, materialize the
- // return value on the stack.
- frame_->EmitPush(r0);
- } else {
- // Fake TOS for targets that shadowed breaks and continues.
- __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
- frame_->EmitPush(r0);
- }
- __ mov(r2, Operand(Smi::FromInt(JUMPING + i)));
- if (--nof_unlinks > 0) {
- // If this is not the last unlink block, jump around the next.
- finally_block.Jump();
- }
- }
- }
-
- // --- Finally block ---
- finally_block.Bind();
-
- // Push the state on the stack.
- frame_->EmitPush(r2);
-
- // We keep two elements on the stack - the (possibly faked) result
- // and the state - while evaluating the finally block.
- //
- // Generate code for the statements in the finally block.
- { VirtualFrame::RegisterAllocationScope scope(this);
- VisitStatements(node->finally_block()->statements());
- }
-
- if (has_valid_frame()) {
- // Restore state and return value or faked TOS.
- frame_->EmitPop(r2);
- frame_->EmitPop(r0);
- }
-
- // Generate code to jump to the right destination for all used
- // formerly shadowing targets. Deallocate each shadow target.
- for (int i = 0; i < shadows.length(); i++) {
- if (has_valid_frame() && shadows[i]->is_bound()) {
- JumpTarget* original = shadows[i]->other_target();
- __ cmp(r2, Operand(Smi::FromInt(JUMPING + i)));
- if (!function_return_is_shadowed_ && i == kReturnShadowIndex) {
- JumpTarget skip;
- skip.Branch(ne);
- frame_->PrepareForReturn();
- original->Jump();
- skip.Bind();
- } else {
- original->Branch(eq);
- }
- }
- }
-
- if (has_valid_frame()) {
- // Check if we need to rethrow the exception.
- JumpTarget exit;
- __ cmp(r2, Operand(Smi::FromInt(THROWING)));
- exit.Branch(ne);
-
- // Rethrow exception.
- frame_->EmitPush(r0);
- frame_->CallRuntime(Runtime::kReThrow, 1);
-
- // Done.
- exit.Bind();
- }
- ASSERT(!has_valid_frame() || frame_->height() == original_height);
-}
-
-
-void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ DebuggerStatament");
- CodeForStatementPosition(node);
-#ifdef ENABLE_DEBUGGER_SUPPORT
- frame_->DebugBreak();
-#endif
- // Ignore the return value.
- ASSERT(frame_->height() == original_height);
-}
-
-
-void CodeGenerator::InstantiateFunction(
- Handle<SharedFunctionInfo> function_info,
- bool pretenure) {
- // Use the fast case closure allocation code that allocates in new
- // space for nested functions that don't need literals cloning.
- if (!pretenure &&
- scope()->is_function_scope() &&
- function_info->num_literals() == 0) {
- FastNewClosureStub stub(
- function_info->strict_mode() ? kStrictMode : kNonStrictMode);
- frame_->EmitPush(Operand(function_info));
- frame_->SpillAll();
- frame_->CallStub(&stub, 1);
- frame_->EmitPush(r0);
- } else {
- // Create a new closure.
- frame_->EmitPush(cp);
- frame_->EmitPush(Operand(function_info));
- frame_->EmitPush(Operand(pretenure
- ? FACTORY->true_value()
- : FACTORY->false_value()));
- frame_->CallRuntime(Runtime::kNewClosure, 3);
- frame_->EmitPush(r0);
- }
-}
-
-
-void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ FunctionLiteral");
-
- // Build the function info and instantiate it.
- Handle<SharedFunctionInfo> function_info =
- Compiler::BuildFunctionInfo(node, script());
- if (function_info.is_null()) {
- SetStackOverflow();
- ASSERT(frame_->height() == original_height);
- return;
- }
- InstantiateFunction(function_info, node->pretenure());
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::VisitSharedFunctionInfoLiteral(
- SharedFunctionInfoLiteral* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ SharedFunctionInfoLiteral");
- InstantiateFunction(node->shared_function_info(), false);
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::VisitConditional(Conditional* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ Conditional");
- JumpTarget then;
- JumpTarget else_;
- LoadCondition(node->condition(), &then, &else_, true);
- if (has_valid_frame()) {
- Branch(false, &else_);
- }
- if (has_valid_frame() || then.is_linked()) {
- then.Bind();
- Load(node->then_expression());
- }
- if (else_.is_linked()) {
- JumpTarget exit;
- if (has_valid_frame()) exit.Jump();
- else_.Bind();
- Load(node->else_expression());
- if (exit.is_linked()) exit.Bind();
- }
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
- if (slot->type() == Slot::LOOKUP) {
- ASSERT(slot->var()->is_dynamic());
-
- // JumpTargets do not yet support merging frames so the frame must be
- // spilled when jumping to these targets.
- JumpTarget slow;
- JumpTarget done;
-
- // Generate fast case for loading from slots that correspond to
- // local/global variables or arguments unless they are shadowed by
- // eval-introduced bindings.
- EmitDynamicLoadFromSlotFastCase(slot,
- typeof_state,
- &slow,
- &done);
-
- slow.Bind();
- frame_->EmitPush(cp);
- frame_->EmitPush(Operand(slot->var()->name()));
-
- if (typeof_state == INSIDE_TYPEOF) {
- frame_->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
- } else {
- frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
- }
-
- done.Bind();
- frame_->EmitPush(r0);
-
- } else {
- Register scratch = VirtualFrame::scratch0();
- TypeInfo info = type_info(slot);
- frame_->EmitPush(SlotOperand(slot, scratch), info);
-
- if (slot->var()->mode() == Variable::CONST) {
- // Const slots may contain 'the hole' value (the constant hasn't been
- // initialized yet) which needs to be converted into the 'undefined'
- // value.
- Comment cmnt(masm_, "[ Unhole const");
- Register tos = frame_->PopToRegister();
- __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
- __ cmp(tos, ip);
- __ LoadRoot(tos, Heap::kUndefinedValueRootIndex, eq);
- frame_->EmitPush(tos);
- }
- }
-}
-
-
-void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
- TypeofState state) {
- VirtualFrame::RegisterAllocationScope scope(this);
- LoadFromSlot(slot, state);
-
- // Bail out quickly if we're not using lazy arguments allocation.
- if (ArgumentsMode() != LAZY_ARGUMENTS_ALLOCATION) return;
-
- // ... or if the slot isn't a non-parameter arguments slot.
- if (slot->type() == Slot::PARAMETER || !slot->is_arguments()) return;
-
- // Load the loaded value from the stack into a register but leave it on the
- // stack.
- Register tos = frame_->Peek();
-
- // If the loaded value is the sentinel that indicates that we
- // haven't loaded the arguments object yet, we need to do it now.
- JumpTarget exit;
- __ LoadRoot(ip, Heap::kArgumentsMarkerRootIndex);
- __ cmp(tos, ip);
- exit.Branch(ne);
- frame_->Drop();
- StoreArgumentsObject(false);
- exit.Bind();
-}
-
-
-void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) {
- ASSERT(slot != NULL);
- VirtualFrame::RegisterAllocationScope scope(this);
- if (slot->type() == Slot::LOOKUP) {
- ASSERT(slot->var()->is_dynamic());
-
- // For now, just do a runtime call.
- frame_->EmitPush(cp);
- frame_->EmitPush(Operand(slot->var()->name()));
-
- if (init_state == CONST_INIT) {
- // Same as the case for a normal store, but ignores attribute
- // (e.g. READ_ONLY) of context slot so that we can initialize
- // const properties (introduced via eval("const foo = (some
- // expr);")). Also, uses the current function context instead of
- // the top context.
- //
- // Note that we must declare the foo upon entry of eval(), via a
- // context slot declaration, but we cannot initialize it at the
- // same time, because the const declaration may be at the end of
- // the eval code (sigh...) and the const variable may have been
- // used before (where its value is 'undefined'). Thus, we can only
- // do the initialization when we actually encounter the expression
- // and when the expression operands are defined and valid, and
- // thus we need the split into 2 operations: declaration of the
- // context slot followed by initialization.
- frame_->CallRuntime(Runtime::kInitializeConstContextSlot, 3);
- } else {
- frame_->EmitPush(Operand(Smi::FromInt(strict_mode_flag())));
- frame_->CallRuntime(Runtime::kStoreContextSlot, 4);
- }
- // Storing a variable must keep the (new) value on the expression
- // stack. This is necessary for compiling assignment expressions.
- frame_->EmitPush(r0);
-
- } else {
- ASSERT(!slot->var()->is_dynamic());
- Register scratch = VirtualFrame::scratch0();
- Register scratch2 = VirtualFrame::scratch1();
-
- // The frame must be spilled when branching to this target.
- JumpTarget exit;
-
- if (init_state == CONST_INIT) {
- ASSERT(slot->var()->mode() == Variable::CONST);
- // Only the first const initialization must be executed (the slot
- // still contains 'the hole' value). When the assignment is
- // executed, the code is identical to a normal store (see below).
- Comment cmnt(masm_, "[ Init const");
- __ ldr(scratch, SlotOperand(slot, scratch));
- __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
- __ cmp(scratch, ip);
- exit.Branch(ne);
- }
-
- // We must execute the store. Storing a variable must keep the
- // (new) value on the stack. This is necessary for compiling
- // assignment expressions.
- //
- // Note: We will reach here even with slot->var()->mode() ==
- // Variable::CONST because of const declarations which will
- // initialize consts to 'the hole' value and by doing so, end up
- // calling this code. r2 may be loaded with context; used below in
- // RecordWrite.
- Register tos = frame_->Peek();
- __ str(tos, SlotOperand(slot, scratch));
- if (slot->type() == Slot::CONTEXT) {
- // Skip write barrier if the written value is a smi.
- __ tst(tos, Operand(kSmiTagMask));
- // We don't use tos any more after here.
- exit.Branch(eq);
- // scratch is loaded with context when calling SlotOperand above.
- int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
- // We need an extra register. Until we have a way to do that in the
- // virtual frame we will cheat and ask for a free TOS register.
- Register scratch3 = frame_->GetTOSRegister();
- __ RecordWrite(scratch, Operand(offset), scratch2, scratch3);
- }
- // If we definitely did not jump over the assignment, we do not need
- // to bind the exit label. Doing so can defeat peephole
- // optimization.
- if (init_state == CONST_INIT || slot->type() == Slot::CONTEXT) {
- exit.Bind();
- }
- }
-}
-
-
-void CodeGenerator::LoadFromGlobalSlotCheckExtensions(Slot* slot,
- TypeofState typeof_state,
- JumpTarget* slow) {
- // Check that no extension objects have been created by calls to
- // eval from the current scope to the global scope.
- Register tmp = frame_->scratch0();
- Register tmp2 = frame_->scratch1();
- Register context = cp;
- Scope* s = scope();
- while (s != NULL) {
- if (s->num_heap_slots() > 0) {
- if (s->calls_eval()) {
- frame_->SpillAll();
- // Check that extension is NULL.
- __ ldr(tmp2, ContextOperand(context, Context::EXTENSION_INDEX));
- __ tst(tmp2, tmp2);
- slow->Branch(ne);
- }
- // Load next context in chain.
- __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
- __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
- context = tmp;
- }
- // If no outer scope calls eval, we do not need to check more
- // context extensions.
- if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break;
- s = s->outer_scope();
- }
-
- if (s->is_eval_scope()) {
- frame_->SpillAll();
- Label next, fast;
- __ Move(tmp, context);
- __ bind(&next);
- // Terminate at global context.
- __ ldr(tmp2, FieldMemOperand(tmp, HeapObject::kMapOffset));
- __ LoadRoot(ip, Heap::kGlobalContextMapRootIndex);
- __ cmp(tmp2, ip);
- __ b(eq, &fast);
- // Check that extension is NULL.
- __ ldr(tmp2, ContextOperand(tmp, Context::EXTENSION_INDEX));
- __ tst(tmp2, tmp2);
- slow->Branch(ne);
- // Load next context in chain.
- __ ldr(tmp, ContextOperand(tmp, Context::CLOSURE_INDEX));
- __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset));
- __ b(&next);
- __ bind(&fast);
- }
-
- // Load the global object.
- LoadGlobal();
- // Setup the name register and call load IC.
- frame_->CallLoadIC(slot->var()->name(),
- typeof_state == INSIDE_TYPEOF
- ? RelocInfo::CODE_TARGET
- : RelocInfo::CODE_TARGET_CONTEXT);
-}
-
-
-void CodeGenerator::EmitDynamicLoadFromSlotFastCase(Slot* slot,
- TypeofState typeof_state,
- JumpTarget* slow,
- JumpTarget* done) {
- // Generate fast-case code for variables that might be shadowed by
- // eval-introduced variables. Eval is used a lot without
- // introducing variables. In those cases, we do not want to
- // perform a runtime call for all variables in the scope
- // containing the eval.
- if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
- LoadFromGlobalSlotCheckExtensions(slot, typeof_state, slow);
- frame_->SpillAll();
- done->Jump();
-
- } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
- frame_->SpillAll();
- Slot* potential_slot = slot->var()->local_if_not_shadowed()->AsSlot();
- Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite();
- if (potential_slot != NULL) {
- // Generate fast case for locals that rewrite to slots.
- __ ldr(r0,
- ContextSlotOperandCheckExtensions(potential_slot,
- r1,
- r2,
- slow));
- if (potential_slot->var()->mode() == Variable::CONST) {
- __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
- __ cmp(r0, ip);
- __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
- }
- done->Jump();
- } else if (rewrite != NULL) {
- // Generate fast case for argument loads.
- Property* property = rewrite->AsProperty();
- if (property != NULL) {
- VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
- Literal* key_literal = property->key()->AsLiteral();
- if (obj_proxy != NULL &&
- key_literal != NULL &&
- obj_proxy->IsArguments() &&
- key_literal->handle()->IsSmi()) {
- // Load arguments object if there are no eval-introduced
- // variables. Then load the argument from the arguments
- // object using keyed load.
- __ ldr(r0,
- ContextSlotOperandCheckExtensions(obj_proxy->var()->AsSlot(),
- r1,
- r2,
- slow));
- frame_->EmitPush(r0);
- __ mov(r1, Operand(key_literal->handle()));
- frame_->EmitPush(r1);
- EmitKeyedLoad();
- done->Jump();
- }
- }
- }
- }
-}
-
-
-void CodeGenerator::VisitSlot(Slot* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ Slot");
- LoadFromSlotCheckForArguments(node, NOT_INSIDE_TYPEOF);
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::VisitVariableProxy(VariableProxy* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ VariableProxy");
-
- Variable* var = node->var();
- Expression* expr = var->rewrite();
- if (expr != NULL) {
- Visit(expr);
- } else {
- ASSERT(var->is_global());
- Reference ref(this, node);
- ref.GetValue();
- }
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::VisitLiteral(Literal* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ Literal");
- Register reg = frame_->GetTOSRegister();
- bool is_smi = node->handle()->IsSmi();
- __ mov(reg, Operand(node->handle()));
- frame_->EmitPush(reg, is_smi ? TypeInfo::Smi() : TypeInfo::Unknown());
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ RexExp Literal");
-
- Register tmp = VirtualFrame::scratch0();
- // Free up a TOS register that can be used to push the literal.
- Register literal = frame_->GetTOSRegister();
-
- // Retrieve the literal array and check the allocated entry.
-
- // Load the function of this activation.
- __ ldr(tmp, frame_->Function());
-
- // Load the literals array of the function.
- __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kLiteralsOffset));
-
- // Load the literal at the ast saved index.
- int literal_offset =
- FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
- __ ldr(literal, FieldMemOperand(tmp, literal_offset));
-
- JumpTarget materialized;
- __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
- __ cmp(literal, ip);
- // This branch locks the virtual frame at the done label to match the
- // one we have here, where the literal register is not on the stack and
- // nothing is spilled.
- materialized.Branch(ne);
-
- // If the entry is undefined we call the runtime system to compute
- // the literal.
- // literal array (0)
- frame_->EmitPush(tmp);
- // literal index (1)
- frame_->EmitPush(Operand(Smi::FromInt(node->literal_index())));
- // RegExp pattern (2)
- frame_->EmitPush(Operand(node->pattern()));
- // RegExp flags (3)
- frame_->EmitPush(Operand(node->flags()));
- frame_->CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
- __ Move(literal, r0);
-
- materialized.Bind();
-
- frame_->EmitPush(literal);
- int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize;
- frame_->EmitPush(Operand(Smi::FromInt(size)));
- frame_->CallRuntime(Runtime::kAllocateInNewSpace, 1);
- // TODO(lrn): Use AllocateInNewSpace macro with fallback to runtime.
- // r0 is newly allocated space.
-
- // Reuse literal variable with (possibly) a new register, still holding
- // the materialized boilerplate.
- literal = frame_->PopToRegister(r0);
-
- __ CopyFields(r0, literal, tmp.bit(), size / kPointerSize);
-
- // Push the clone.
- frame_->EmitPush(r0);
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ ObjectLiteral");
-
- Register literal = frame_->GetTOSRegister();
- // Load the function of this activation.
- __ ldr(literal, frame_->Function());
- // Literal array.
- __ ldr(literal, FieldMemOperand(literal, JSFunction::kLiteralsOffset));
- frame_->EmitPush(literal);
- // Literal index.
- frame_->EmitPush(Operand(Smi::FromInt(node->literal_index())));
- // Constant properties.
- frame_->EmitPush(Operand(node->constant_properties()));
- // Should the object literal have fast elements?
- frame_->EmitPush(Operand(Smi::FromInt(node->fast_elements() ? 1 : 0)));
- if (node->depth() > 1) {
- frame_->CallRuntime(Runtime::kCreateObjectLiteral, 4);
- } else {
- frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
- }
- frame_->EmitPush(r0); // save the result
-
- // Mark all computed expressions that are bound to a key that
- // is shadowed by a later occurrence of the same key. For the
- // marked expressions, no store code is emitted.
- node->CalculateEmitStore();
-
- for (int i = 0; i < node->properties()->length(); i++) {
- // At the start of each iteration, the top of stack contains
- // the newly created object literal.
- ObjectLiteral::Property* property = node->properties()->at(i);
- Literal* key = property->key();
- Expression* value = property->value();
- switch (property->kind()) {
- case ObjectLiteral::Property::CONSTANT:
- break;
- case ObjectLiteral::Property::MATERIALIZED_LITERAL:
- if (CompileTimeValue::IsCompileTimeValue(property->value())) break;
- // else fall through
- case ObjectLiteral::Property::COMPUTED:
- if (key->handle()->IsSymbol()) {
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- Builtins::kStoreIC_Initialize));
- Load(value);
- if (property->emit_store()) {
- frame_->PopToR0();
- // Fetch the object literal.
- frame_->SpillAllButCopyTOSToR1();
- __ mov(r2, Operand(key->handle()));
- frame_->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0);
- } else {
- frame_->Drop();
- }
- break;
- }
- // else fall through
- case ObjectLiteral::Property::PROTOTYPE: {
- frame_->Dup();
- Load(key);
- Load(value);
- if (property->emit_store()) {
- frame_->EmitPush(Operand(Smi::FromInt(NONE))); // PropertyAttributes
- frame_->CallRuntime(Runtime::kSetProperty, 4);
- } else {
- frame_->Drop(3);
- }
- break;
- }
- case ObjectLiteral::Property::SETTER: {
- frame_->Dup();
- Load(key);
- frame_->EmitPush(Operand(Smi::FromInt(1)));
- Load(value);
- frame_->CallRuntime(Runtime::kDefineAccessor, 4);
- break;
- }
- case ObjectLiteral::Property::GETTER: {
- frame_->Dup();
- Load(key);
- frame_->EmitPush(Operand(Smi::FromInt(0)));
- Load(value);
- frame_->CallRuntime(Runtime::kDefineAccessor, 4);
- break;
- }
- }
- }
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ ArrayLiteral");
-
- Register tos = frame_->GetTOSRegister();
- // Load the function of this activation.
- __ ldr(tos, frame_->Function());
- // Load the literals array of the function.
- __ ldr(tos, FieldMemOperand(tos, JSFunction::kLiteralsOffset));
- frame_->EmitPush(tos);
- frame_->EmitPush(Operand(Smi::FromInt(node->literal_index())));
- frame_->EmitPush(Operand(node->constant_elements()));
- int length = node->values()->length();
- if (node->constant_elements()->map() == HEAP->fixed_cow_array_map()) {
- FastCloneShallowArrayStub stub(
- FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, length);
- frame_->CallStub(&stub, 3);
- __ IncrementCounter(masm_->isolate()->counters()->cow_arrays_created_stub(),
- 1, r1, r2);
- } else if (node->depth() > 1) {
- frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3);
- } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) {
- frame_->CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
- } else {
- FastCloneShallowArrayStub stub(
- FastCloneShallowArrayStub::CLONE_ELEMENTS, length);
- frame_->CallStub(&stub, 3);
- }
- frame_->EmitPush(r0); // save the result
- // r0: created object literal
-
- // Generate code to set the elements in the array that are not
- // literals.
- for (int i = 0; i < node->values()->length(); i++) {
- Expression* value = node->values()->at(i);
-
- // If value is a literal the property value is already set in the
- // boilerplate object.
- if (value->AsLiteral() != NULL) continue;
- // If value is a materialized literal the property value is already set
- // in the boilerplate object if it is simple.
- if (CompileTimeValue::IsCompileTimeValue(value)) continue;
-
- // The property must be set by generated code.
- Load(value);
- frame_->PopToR0();
- // Fetch the object literal.
- frame_->SpillAllButCopyTOSToR1();
-
- // Get the elements array.
- __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset));
-
- // Write to the indexed properties array.
- int offset = i * kPointerSize + FixedArray::kHeaderSize;
- __ str(r0, FieldMemOperand(r1, offset));
-
- // Update the write barrier for the array address.
- __ RecordWrite(r1, Operand(offset), r3, r2);
- }
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- // Call runtime routine to allocate the catch extension object and
- // assign the exception value to the catch variable.
- Comment cmnt(masm_, "[ CatchExtensionObject");
- Load(node->key());
- Load(node->value());
- frame_->CallRuntime(Runtime::kCreateCatchExtensionObject, 2);
- frame_->EmitPush(r0);
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::EmitSlotAssignment(Assignment* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm(), "[ Variable Assignment");
- Variable* var = node->target()->AsVariableProxy()->AsVariable();
- ASSERT(var != NULL);
- Slot* slot = var->AsSlot();
- ASSERT(slot != NULL);
-
- // Evaluate the right-hand side.
- if (node->is_compound()) {
- // For a compound assignment the right-hand side is a binary operation
- // between the current property value and the actual right-hand side.
- LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
-
- // Perform the binary operation.
- Literal* literal = node->value()->AsLiteral();
- bool overwrite_value = node->value()->ResultOverwriteAllowed();
- if (literal != NULL && literal->handle()->IsSmi()) {
- SmiOperation(node->binary_op(),
- literal->handle(),
- false,
- overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
- } else {
- GenerateInlineSmi inline_smi =
- loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
- if (literal != NULL) {
- ASSERT(!literal->handle()->IsSmi());
- inline_smi = DONT_GENERATE_INLINE_SMI;
- }
- Load(node->value());
- GenericBinaryOperation(node->binary_op(),
- overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE,
- inline_smi);
- }
- } else {
- Load(node->value());
- }
-
- // Perform the assignment.
- if (var->mode() != Variable::CONST || node->op() == Token::INIT_CONST) {
- CodeForSourcePosition(node->position());
- StoreToSlot(slot,
- node->op() == Token::INIT_CONST ? CONST_INIT : NOT_CONST_INIT);
- }
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm(), "[ Named Property Assignment");
- Variable* var = node->target()->AsVariableProxy()->AsVariable();
- Property* prop = node->target()->AsProperty();
- ASSERT(var == NULL || (prop == NULL && var->is_global()));
-
- // Initialize name and evaluate the receiver sub-expression if necessary. If
- // the receiver is trivial it is not placed on the stack at this point, but
- // loaded whenever actually needed.
- Handle<String> name;
- bool is_trivial_receiver = false;
- if (var != NULL) {
- name = var->name();
- } else {
- Literal* lit = prop->key()->AsLiteral();
- ASSERT_NOT_NULL(lit);
- name = Handle<String>::cast(lit->handle());
- // Do not materialize the receiver on the frame if it is trivial.
- is_trivial_receiver = prop->obj()->IsTrivial();
- if (!is_trivial_receiver) Load(prop->obj());
- }
-
- // Change to slow case in the beginning of an initialization block to
- // avoid the quadratic behavior of repeatedly adding fast properties.
- if (node->starts_initialization_block()) {
- // Initialization block consists of assignments of the form expr.x = ..., so
- // this will never be an assignment to a variable, so there must be a
- // receiver object.
- ASSERT_EQ(NULL, var);
- if (is_trivial_receiver) {
- Load(prop->obj());
- } else {
- frame_->Dup();
- }
- frame_->CallRuntime(Runtime::kToSlowProperties, 1);
- }
-
- // Change to fast case at the end of an initialization block. To prepare for
- // that add an extra copy of the receiver to the frame, so that it can be
- // converted back to fast case after the assignment.
- if (node->ends_initialization_block() && !is_trivial_receiver) {
- frame_->Dup();
- }
-
- // Stack layout:
- // [tos] : receiver (only materialized if non-trivial)
- // [tos+1] : receiver if at the end of an initialization block
-
- // Evaluate the right-hand side.
- if (node->is_compound()) {
- // For a compound assignment the right-hand side is a binary operation
- // between the current property value and the actual right-hand side.
- if (is_trivial_receiver) {
- Load(prop->obj());
- } else if (var != NULL) {
- LoadGlobal();
- } else {
- frame_->Dup();
- }
- EmitNamedLoad(name, var != NULL);
-
- // Perform the binary operation.
- Literal* literal = node->value()->AsLiteral();
- bool overwrite_value = node->value()->ResultOverwriteAllowed();
- if (literal != NULL && literal->handle()->IsSmi()) {
- SmiOperation(node->binary_op(),
- literal->handle(),
- false,
- overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
- } else {
- GenerateInlineSmi inline_smi =
- loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
- if (literal != NULL) {
- ASSERT(!literal->handle()->IsSmi());
- inline_smi = DONT_GENERATE_INLINE_SMI;
- }
- Load(node->value());
- GenericBinaryOperation(node->binary_op(),
- overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE,
- inline_smi);
- }
- } else {
- // For non-compound assignment just load the right-hand side.
- Load(node->value());
- }
-
- // Stack layout:
- // [tos] : value
- // [tos+1] : receiver (only materialized if non-trivial)
- // [tos+2] : receiver if at the end of an initialization block
-
- // Perform the assignment. It is safe to ignore constants here.
- ASSERT(var == NULL || var->mode() != Variable::CONST);
- ASSERT_NE(Token::INIT_CONST, node->op());
- if (is_trivial_receiver) {
- // Load the receiver and swap with the value.
- Load(prop->obj());
- Register t0 = frame_->PopToRegister();
- Register t1 = frame_->PopToRegister(t0);
- frame_->EmitPush(t0);
- frame_->EmitPush(t1);
- }
- CodeForSourcePosition(node->position());
- bool is_contextual = (var != NULL);
- EmitNamedStore(name, is_contextual);
- frame_->EmitPush(r0);
-
- // Change to fast case at the end of an initialization block.
- if (node->ends_initialization_block()) {
- ASSERT_EQ(NULL, var);
- // The argument to the runtime call is the receiver.
- if (is_trivial_receiver) {
- Load(prop->obj());
- } else {
- // A copy of the receiver is below the value of the assignment. Swap
- // the receiver and the value of the assignment expression.
- Register t0 = frame_->PopToRegister();
- Register t1 = frame_->PopToRegister(t0);
- frame_->EmitPush(t0);
- frame_->EmitPush(t1);
- }
- frame_->CallRuntime(Runtime::kToFastProperties, 1);
- }
-
- // Stack layout:
- // [tos] : result
-
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ Keyed Property Assignment");
- Property* prop = node->target()->AsProperty();
- ASSERT_NOT_NULL(prop);
-
- // Evaluate the receiver subexpression.
- Load(prop->obj());
-
- WriteBarrierCharacter wb_info;
-
- // Change to slow case in the beginning of an initialization block to
- // avoid the quadratic behavior of repeatedly adding fast properties.
- if (node->starts_initialization_block()) {
- frame_->Dup();
- frame_->CallRuntime(Runtime::kToSlowProperties, 1);
- }
-
- // Change to fast case at the end of an initialization block. To prepare for
- // that add an extra copy of the receiver to the frame, so that it can be
- // converted back to fast case after the assignment.
- if (node->ends_initialization_block()) {
- frame_->Dup();
- }
-
- // Evaluate the key subexpression.
- Load(prop->key());
-
- // Stack layout:
- // [tos] : key
- // [tos+1] : receiver
- // [tos+2] : receiver if at the end of an initialization block
- //
- // Evaluate the right-hand side.
- if (node->is_compound()) {
- // For a compound assignment the right-hand side is a binary operation
- // between the current property value and the actual right-hand side.
- // Duplicate receiver and key for loading the current property value.
- frame_->Dup2();
- EmitKeyedLoad();
- frame_->EmitPush(r0);
-
- // Perform the binary operation.
- Literal* literal = node->value()->AsLiteral();
- bool overwrite_value = node->value()->ResultOverwriteAllowed();
- if (literal != NULL && literal->handle()->IsSmi()) {
- SmiOperation(node->binary_op(),
- literal->handle(),
- false,
- overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
- } else {
- GenerateInlineSmi inline_smi =
- loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
- if (literal != NULL) {
- ASSERT(!literal->handle()->IsSmi());
- inline_smi = DONT_GENERATE_INLINE_SMI;
- }
- Load(node->value());
- GenericBinaryOperation(node->binary_op(),
- overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE,
- inline_smi);
- }
- wb_info = node->type()->IsLikelySmi() ? LIKELY_SMI : UNLIKELY_SMI;
- } else {
- // For non-compound assignment just load the right-hand side.
- Load(node->value());
- wb_info = node->value()->AsLiteral() != NULL ?
- NEVER_NEWSPACE :
- (node->value()->type()->IsLikelySmi() ? LIKELY_SMI : UNLIKELY_SMI);
- }
-
- // Stack layout:
- // [tos] : value
- // [tos+1] : key
- // [tos+2] : receiver
- // [tos+3] : receiver if at the end of an initialization block
-
- // Perform the assignment. It is safe to ignore constants here.
- ASSERT(node->op() != Token::INIT_CONST);
- CodeForSourcePosition(node->position());
- EmitKeyedStore(prop->key()->type(), wb_info);
- frame_->EmitPush(r0);
-
- // Stack layout:
- // [tos] : result
- // [tos+1] : receiver if at the end of an initialization block
-
- // Change to fast case at the end of an initialization block.
- if (node->ends_initialization_block()) {
- // The argument to the runtime call is the extra copy of the receiver,
- // which is below the value of the assignment. Swap the receiver and
- // the value of the assignment expression.
- Register t0 = frame_->PopToRegister();
- Register t1 = frame_->PopToRegister(t0);
- frame_->EmitPush(t1);
- frame_->EmitPush(t0);
- frame_->CallRuntime(Runtime::kToFastProperties, 1);
- }
-
- // Stack layout:
- // [tos] : result
-
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::VisitAssignment(Assignment* node) {
- VirtualFrame::RegisterAllocationScope scope(this);
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ Assignment");
-
- Variable* var = node->target()->AsVariableProxy()->AsVariable();
- Property* prop = node->target()->AsProperty();
-
- if (var != NULL && !var->is_global()) {
- EmitSlotAssignment(node);
-
- } else if ((prop != NULL && prop->key()->IsPropertyName()) ||
- (var != NULL && var->is_global())) {
- // Properties whose keys are property names and global variables are
- // treated as named property references. We do not need to consider
- // global 'this' because it is not a valid left-hand side.
- EmitNamedPropertyAssignment(node);
-
- } else if (prop != NULL) {
- // Other properties (including rewritten parameters for a function that
- // uses arguments) are keyed property assignments.
- EmitKeyedPropertyAssignment(node);
-
- } else {
- // Invalid left-hand side.
- Load(node->target());
- frame_->CallRuntime(Runtime::kThrowReferenceError, 1);
- // The runtime call doesn't actually return but the code generator will
- // still generate code and expects a certain frame height.
- frame_->EmitPush(r0);
- }
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::VisitThrow(Throw* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ Throw");
-
- Load(node->exception());
- CodeForSourcePosition(node->position());
- frame_->CallRuntime(Runtime::kThrow, 1);
- frame_->EmitPush(r0);
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::VisitProperty(Property* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ Property");
-
- { Reference property(this, node);
- property.GetValue();
- }
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::VisitCall(Call* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ Call");
-
- Expression* function = node->expression();
- ZoneList<Expression*>* args = node->arguments();
-
- // Standard function call.
- // Check if the function is a variable or a property.
- Variable* var = function->AsVariableProxy()->AsVariable();
- Property* property = function->AsProperty();
-
- // ------------------------------------------------------------------------
- // Fast-case: Use inline caching.
- // ---
- // According to ECMA-262, section 11.2.3, page 44, the function to call
- // must be resolved after the arguments have been evaluated. The IC code
- // automatically handles this by loading the arguments before the function
- // is resolved in cache misses (this also holds for megamorphic calls).
- // ------------------------------------------------------------------------
-
- if (var != NULL && var->is_possibly_eval()) {
- // ----------------------------------
- // JavaScript example: 'eval(arg)' // eval is not known to be shadowed
- // ----------------------------------
-
- // In a call to eval, we first call %ResolvePossiblyDirectEval to
- // resolve the function we need to call and the receiver of the
- // call. Then we call the resolved function using the given
- // arguments.
-
- // Prepare stack for call to resolved function.
- Load(function);
-
- // Allocate a frame slot for the receiver.
- frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
-
- // Load the arguments.
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- }
-
- VirtualFrame::SpilledScope spilled_scope(frame_);
-
- // If we know that eval can only be shadowed by eval-introduced
- // variables we attempt to load the global eval function directly
- // in generated code. If we succeed, there is no need to perform a
- // context lookup in the runtime system.
- JumpTarget done;
- if (var->AsSlot() != NULL && var->mode() == Variable::DYNAMIC_GLOBAL) {
- ASSERT(var->AsSlot()->type() == Slot::LOOKUP);
- JumpTarget slow;
- // Prepare the stack for the call to
- // ResolvePossiblyDirectEvalNoLookup by pushing the loaded
- // function, the first argument to the eval call and the
- // receiver.
- LoadFromGlobalSlotCheckExtensions(var->AsSlot(),
- NOT_INSIDE_TYPEOF,
- &slow);
- frame_->EmitPush(r0);
- if (arg_count > 0) {
- __ ldr(r1, MemOperand(sp, arg_count * kPointerSize));
- frame_->EmitPush(r1);
- } else {
- frame_->EmitPush(r2);
- }
- __ ldr(r1, frame_->Receiver());
- frame_->EmitPush(r1);
-
- // Push the strict mode flag.
- frame_->EmitPush(Operand(Smi::FromInt(strict_mode_flag())));
-
- frame_->CallRuntime(Runtime::kResolvePossiblyDirectEvalNoLookup, 4);
-
- done.Jump();
- slow.Bind();
- }
-
- // Prepare the stack for the call to ResolvePossiblyDirectEval by
- // pushing the loaded function, the first argument to the eval
- // call and the receiver.
- __ ldr(r1, MemOperand(sp, arg_count * kPointerSize + kPointerSize));
- frame_->EmitPush(r1);
- if (arg_count > 0) {
- __ ldr(r1, MemOperand(sp, arg_count * kPointerSize));
- frame_->EmitPush(r1);
- } else {
- frame_->EmitPush(r2);
- }
- __ ldr(r1, frame_->Receiver());
- frame_->EmitPush(r1);
-
- // Push the strict mode flag.
- frame_->EmitPush(Operand(Smi::FromInt(strict_mode_flag())));
-
- // Resolve the call.
- frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 4);
-
- // If we generated fast-case code bind the jump-target where fast
- // and slow case merge.
- if (done.is_linked()) done.Bind();
-
- // Touch up stack with the right values for the function and the receiver.
- __ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize));
- __ str(r1, MemOperand(sp, arg_count * kPointerSize));
-
- // Call the function.
- CodeForSourcePosition(node->position());
-
- InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub call_function(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
- frame_->CallStub(&call_function, arg_count + 1);
-
- __ ldr(cp, frame_->Context());
- // Remove the function from the stack.
- frame_->Drop();
- frame_->EmitPush(r0);
-
- } else if (var != NULL && !var->is_this() && var->is_global()) {
- // ----------------------------------
- // JavaScript example: 'foo(1, 2, 3)' // foo is global
- // ----------------------------------
- // Pass the global object as the receiver and let the IC stub
- // patch the stack to use the global proxy as 'this' in the
- // invoked function.
- LoadGlobal();
-
- // Load the arguments.
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- }
-
- VirtualFrame::SpilledScope spilled_scope(frame_);
- // Setup the name register and call the IC initialization code.
- __ mov(r2, Operand(var->name()));
- InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
- Handle<Code> stub =
- ISOLATE->stub_cache()->ComputeCallInitialize(arg_count, in_loop);
- CodeForSourcePosition(node->position());
- frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET_CONTEXT,
- arg_count + 1);
- __ ldr(cp, frame_->Context());
- frame_->EmitPush(r0);
-
- } else if (var != NULL && var->AsSlot() != NULL &&
- var->AsSlot()->type() == Slot::LOOKUP) {
- // ----------------------------------
- // JavaScript examples:
- //
- // with (obj) foo(1, 2, 3) // foo may be in obj.
- //
- // function f() {};
- // function g() {
- // eval(...);
- // f(); // f could be in extension object.
- // }
- // ----------------------------------
-
- JumpTarget slow, done;
-
- // Generate fast case for loading functions from slots that
- // correspond to local/global variables or arguments unless they
- // are shadowed by eval-introduced bindings.
- EmitDynamicLoadFromSlotFastCase(var->AsSlot(),
- NOT_INSIDE_TYPEOF,
- &slow,
- &done);
-
- slow.Bind();
- // Load the function
- frame_->EmitPush(cp);
- frame_->EmitPush(Operand(var->name()));
- frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
- // r0: slot value; r1: receiver
-
- // Load the receiver.
- frame_->EmitPush(r0); // function
- frame_->EmitPush(r1); // receiver
-
- // If fast case code has been generated, emit code to push the
- // function and receiver and have the slow path jump around this
- // code.
- if (done.is_linked()) {
- JumpTarget call;
- call.Jump();
- done.Bind();
- frame_->EmitPush(r0); // function
- LoadGlobalReceiver(VirtualFrame::scratch0()); // receiver
- call.Bind();
- }
-
- // Call the function. At this point, everything is spilled but the
- // function and receiver are in r0 and r1.
- CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
- frame_->EmitPush(r0);
-
- } else if (property != NULL) {
- // Check if the key is a literal string.
- Literal* literal = property->key()->AsLiteral();
-
- if (literal != NULL && literal->handle()->IsSymbol()) {
- // ------------------------------------------------------------------
- // JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
- // ------------------------------------------------------------------
-
- Handle<String> name = Handle<String>::cast(literal->handle());
-
- if (ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION &&
- name->IsEqualTo(CStrVector("apply")) &&
- args->length() == 2 &&
- args->at(1)->AsVariableProxy() != NULL &&
- args->at(1)->AsVariableProxy()->IsArguments()) {
- // Use the optimized Function.prototype.apply that avoids
- // allocating lazily allocated arguments objects.
- CallApplyLazy(property->obj(),
- args->at(0),
- args->at(1)->AsVariableProxy(),
- node->position());
-
- } else {
- Load(property->obj()); // Receiver.
- // Load the arguments.
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- }
-
- VirtualFrame::SpilledScope spilled_scope(frame_);
- // Set the name register and call the IC initialization code.
- __ mov(r2, Operand(name));
- InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
- Handle<Code> stub =
- ISOLATE->stub_cache()->ComputeCallInitialize(arg_count, in_loop);
- CodeForSourcePosition(node->position());
- frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
- __ ldr(cp, frame_->Context());
- frame_->EmitPush(r0);
- }
-
- } else {
- // -------------------------------------------
- // JavaScript example: 'array[index](1, 2, 3)'
- // -------------------------------------------
-
- // Load the receiver and name of the function.
- Load(property->obj());
- Load(property->key());
-
- if (property->is_synthetic()) {
- EmitKeyedLoad();
- // Put the function below the receiver.
- // Use the global receiver.
- frame_->EmitPush(r0); // Function.
- LoadGlobalReceiver(VirtualFrame::scratch0());
- // Call the function.
- CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
- frame_->EmitPush(r0);
- } else {
- // Swap the name of the function and the receiver on the stack to follow
- // the calling convention for call ICs.
- Register key = frame_->PopToRegister();
- Register receiver = frame_->PopToRegister(key);
- frame_->EmitPush(key);
- frame_->EmitPush(receiver);
-
- // Load the arguments.
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- }
-
- // Load the key into r2 and call the IC initialization code.
- InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
- Handle<Code> stub =
- ISOLATE->stub_cache()->ComputeKeyedCallInitialize(arg_count,
- in_loop);
- CodeForSourcePosition(node->position());
- frame_->SpillAll();
- __ ldr(r2, frame_->ElementAt(arg_count + 1));
- frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
- frame_->Drop(); // Drop the key still on the stack.
- __ ldr(cp, frame_->Context());
- frame_->EmitPush(r0);
- }
- }
-
- } else {
- // ----------------------------------
- // JavaScript example: 'foo(1, 2, 3)' // foo is not global
- // ----------------------------------
-
- // Load the function.
- Load(function);
-
- // Pass the global proxy as the receiver.
- LoadGlobalReceiver(VirtualFrame::scratch0());
-
- // Call the function.
- CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
- frame_->EmitPush(r0);
- }
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::VisitCallNew(CallNew* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ CallNew");
-
- // According to ECMA-262, section 11.2.2, page 44, the function
- // expression in new calls must be evaluated before the
- // arguments. This is different from ordinary calls, where the
- // actual function to call is resolved after the arguments have been
- // evaluated.
-
- // Push constructor on the stack. If it's not a function it's used as
- // receiver for CALL_NON_FUNCTION, otherwise the value on the stack is
- // ignored.
- Load(node->expression());
-
- // Push the arguments ("left-to-right") on the stack.
- ZoneList<Expression*>* args = node->arguments();
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- }
-
- // Spill everything from here to simplify the implementation.
- VirtualFrame::SpilledScope spilled_scope(frame_);
-
- // Load the argument count into r0 and the function into r1 as per
- // calling convention.
- __ mov(r0, Operand(arg_count));
- __ ldr(r1, frame_->ElementAt(arg_count));
-
- // Call the construct call builtin that handles allocation and
- // constructor invocation.
- CodeForSourcePosition(node->position());
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- Builtins::kJSConstructCall));
- frame_->CallCodeObject(ic, RelocInfo::CONSTRUCT_CALL, arg_count + 1);
- frame_->EmitPush(r0);
-
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::GenerateClassOf(ZoneList<Expression*>* args) {
- Register scratch = VirtualFrame::scratch0();
- JumpTarget null, function, leave, non_function_constructor;
-
- // Load the object into register.
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Register tos = frame_->PopToRegister();
-
- // If the object is a smi, we return null.
- __ tst(tos, Operand(kSmiTagMask));
- null.Branch(eq);
-
- // Check that the object is a JS object but take special care of JS
- // functions to make sure they have 'Function' as their class.
- __ CompareObjectType(tos, tos, scratch, FIRST_JS_OBJECT_TYPE);
- null.Branch(lt);
-
- // As long as JS_FUNCTION_TYPE is the last instance type and it is
- // right after LAST_JS_OBJECT_TYPE, we can avoid checking for
- // LAST_JS_OBJECT_TYPE.
- STATIC_ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
- STATIC_ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
- __ cmp(scratch, Operand(JS_FUNCTION_TYPE));
- function.Branch(eq);
-
- // Check if the constructor in the map is a function.
- __ ldr(tos, FieldMemOperand(tos, Map::kConstructorOffset));
- __ CompareObjectType(tos, scratch, scratch, JS_FUNCTION_TYPE);
- non_function_constructor.Branch(ne);
-
- // The tos register now contains the constructor function. Grab the
- // instance class name from there.
- __ ldr(tos, FieldMemOperand(tos, JSFunction::kSharedFunctionInfoOffset));
- __ ldr(tos,
- FieldMemOperand(tos, SharedFunctionInfo::kInstanceClassNameOffset));
- frame_->EmitPush(tos);
- leave.Jump();
-
- // Functions have class 'Function'.
- function.Bind();
- __ mov(tos, Operand(FACTORY->function_class_symbol()));
- frame_->EmitPush(tos);
- leave.Jump();
-
- // Objects with a non-function constructor have class 'Object'.
- non_function_constructor.Bind();
- __ mov(tos, Operand(FACTORY->Object_symbol()));
- frame_->EmitPush(tos);
- leave.Jump();
-
- // Non-JS objects have class null.
- null.Bind();
- __ LoadRoot(tos, Heap::kNullValueRootIndex);
- frame_->EmitPush(tos);
-
- // All done.
- leave.Bind();
-}
-
-
-void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) {
- Register scratch = VirtualFrame::scratch0();
- JumpTarget leave;
-
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Register tos = frame_->PopToRegister(); // tos contains object.
- // if (object->IsSmi()) return the object.
- __ tst(tos, Operand(kSmiTagMask));
- leave.Branch(eq);
- // It is a heap object - get map. If (!object->IsJSValue()) return the object.
- __ CompareObjectType(tos, scratch, scratch, JS_VALUE_TYPE);
- leave.Branch(ne);
- // Load the value.
- __ ldr(tos, FieldMemOperand(tos, JSValue::kValueOffset));
- leave.Bind();
- frame_->EmitPush(tos);
-}
-
-
-void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) {
- Register scratch1 = VirtualFrame::scratch0();
- Register scratch2 = VirtualFrame::scratch1();
- JumpTarget leave;
-
- ASSERT(args->length() == 2);
- Load(args->at(0)); // Load the object.
- Load(args->at(1)); // Load the value.
- Register value = frame_->PopToRegister();
- Register object = frame_->PopToRegister(value);
- // if (object->IsSmi()) return object.
- __ tst(object, Operand(kSmiTagMask));
- leave.Branch(eq);
- // It is a heap object - get map. If (!object->IsJSValue()) return the object.
- __ CompareObjectType(object, scratch1, scratch1, JS_VALUE_TYPE);
- leave.Branch(ne);
- // Store the value.
- __ str(value, FieldMemOperand(object, JSValue::kValueOffset));
- // Update the write barrier.
- __ RecordWrite(object,
- Operand(JSValue::kValueOffset - kHeapObjectTag),
- scratch1,
- scratch2);
- // Leave.
- leave.Bind();
- frame_->EmitPush(value);
-}
-
-
-void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Register reg = frame_->PopToRegister();
- __ tst(reg, Operand(kSmiTagMask));
- cc_reg_ = eq;
-}
-
-
-void CodeGenerator::GenerateLog(ZoneList<Expression*>* args) {
- // See comment in CodeGenerator::GenerateLog in codegen-ia32.cc.
- ASSERT_EQ(args->length(), 3);
-#ifdef ENABLE_LOGGING_AND_PROFILING
- if (ShouldGenerateLog(args->at(0))) {
- Load(args->at(1));
- Load(args->at(2));
- frame_->CallRuntime(Runtime::kLog, 2);
- }
-#endif
- frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
-}
-
-
-void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Register reg = frame_->PopToRegister();
- __ tst(reg, Operand(kSmiTagMask | 0x80000000u));
- cc_reg_ = eq;
-}
-
-
-// Generates the Math.pow method.
-void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 2);
- Load(args->at(0));
- Load(args->at(1));
-
- if (!Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
- frame_->CallRuntime(Runtime::kMath_pow, 2);
- frame_->EmitPush(r0);
- } else {
- CpuFeatures::Scope scope(VFP3);
- JumpTarget runtime, done;
- Label exponent_nonsmi, base_nonsmi, powi, not_minus_half, allocate_return;
-
- Register scratch1 = VirtualFrame::scratch0();
- Register scratch2 = VirtualFrame::scratch1();
-
- // Get base and exponent to registers.
- Register exponent = frame_->PopToRegister();
- Register base = frame_->PopToRegister(exponent);
- Register heap_number_map = no_reg;
-
- // Set the frame for the runtime jump target. The code below jumps to the
- // jump target label so the frame needs to be established before that.
- ASSERT(runtime.entry_frame() == NULL);
- runtime.set_entry_frame(frame_);
-
- __ JumpIfNotSmi(exponent, &exponent_nonsmi);
- __ JumpIfNotSmi(base, &base_nonsmi);
-
- heap_number_map = r6;
- __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
-
- // Exponent is a smi and base is a smi. Get the smi value into vfp register
- // d1.
- __ SmiToDoubleVFPRegister(base, d1, scratch1, s0);
- __ b(&powi);
-
- __ bind(&base_nonsmi);
- // Exponent is smi and base is non smi. Get the double value from the base
- // into vfp register d1.
- __ ObjectToDoubleVFPRegister(base, d1,
- scratch1, scratch2, heap_number_map, s0,
- runtime.entry_label());
-
- __ bind(&powi);
-
- // Load 1.0 into d0.
- __ vmov(d0, 1.0);
-
- // Get the absolute untagged value of the exponent and use that for the
- // calculation.
- __ mov(scratch1, Operand(exponent, ASR, kSmiTagSize), SetCC);
- // Negate if negative.
- __ rsb(scratch1, scratch1, Operand(0, RelocInfo::NONE), LeaveCC, mi);
- __ vmov(d2, d0, mi); // 1.0 needed in d2 later if exponent is negative.
-
- // Run through all the bits in the exponent. The result is calculated in d0
- // and d1 holds base^(bit^2).
- Label more_bits;
- __ bind(&more_bits);
- __ mov(scratch1, Operand(scratch1, LSR, 1), SetCC);
- __ vmul(d0, d0, d1, cs); // Multiply with base^(bit^2) if bit is set.
- __ vmul(d1, d1, d1, ne); // Don't bother calculating next d1 if done.
- __ b(ne, &more_bits);
-
- // If exponent is positive we are done.
- __ cmp(exponent, Operand(0, RelocInfo::NONE));
- __ b(ge, &allocate_return);
-
- // If exponent is negative result is 1/result (d2 already holds 1.0 in that
- // case). However if d0 has reached infinity this will not provide the
- // correct result, so call runtime if that is the case.
- __ mov(scratch2, Operand(0x7FF00000));
- __ mov(scratch1, Operand(0, RelocInfo::NONE));
- __ vmov(d1, scratch1, scratch2); // Load infinity into d1.
- __ VFPCompareAndSetFlags(d0, d1);
- runtime.Branch(eq); // d0 reached infinity.
- __ vdiv(d0, d2, d0);
- __ b(&allocate_return);
-
- __ bind(&exponent_nonsmi);
- // Special handling of raising to the power of -0.5 and 0.5. First check
- // that the value is a heap number and that the lower bits (which for both
- // values are zero).
- heap_number_map = r6;
- __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
- __ ldr(scratch1, FieldMemOperand(exponent, HeapObject::kMapOffset));
- __ ldr(scratch2, FieldMemOperand(exponent, HeapNumber::kMantissaOffset));
- __ cmp(scratch1, heap_number_map);
- runtime.Branch(ne);
- __ tst(scratch2, scratch2);
- runtime.Branch(ne);
-
- // Load the higher bits (which contains the floating point exponent).
- __ ldr(scratch1, FieldMemOperand(exponent, HeapNumber::kExponentOffset));
-
- // Compare exponent with -0.5.
- __ cmp(scratch1, Operand(0xbfe00000));
- __ b(ne, ¬_minus_half);
-
- // Get the double value from the base into vfp register d0.
- __ ObjectToDoubleVFPRegister(base, d0,
- scratch1, scratch2, heap_number_map, s0,
- runtime.entry_label(),
- AVOID_NANS_AND_INFINITIES);
-
- // Convert -0 into +0 by adding +0.
- __ vmov(d2, 0.0);
- __ vadd(d0, d2, d0);
- // Load 1.0 into d2.
- __ vmov(d2, 1.0);
-
- // Calculate the reciprocal of the square root.
- __ vsqrt(d0, d0);
- __ vdiv(d0, d2, d0);
-
- __ b(&allocate_return);
-
- __ bind(¬_minus_half);
- // Compare exponent with 0.5.
- __ cmp(scratch1, Operand(0x3fe00000));
- runtime.Branch(ne);
-
- // Get the double value from the base into vfp register d0.
- __ ObjectToDoubleVFPRegister(base, d0,
- scratch1, scratch2, heap_number_map, s0,
- runtime.entry_label(),
- AVOID_NANS_AND_INFINITIES);
- // Convert -0 into +0 by adding +0.
- __ vmov(d2, 0.0);
- __ vadd(d0, d2, d0);
- __ vsqrt(d0, d0);
-
- __ bind(&allocate_return);
- Register scratch3 = r5;
- __ AllocateHeapNumberWithValue(scratch3, d0, scratch1, scratch2,
- heap_number_map, runtime.entry_label());
- __ mov(base, scratch3);
- done.Jump();
-
- runtime.Bind();
-
- // Push back the arguments again for the runtime call.
- frame_->EmitPush(base);
- frame_->EmitPush(exponent);
- frame_->CallRuntime(Runtime::kMath_pow, 2);
- __ Move(base, r0);
-
- done.Bind();
- frame_->EmitPush(base);
- }
-}
-
-
-// Generates the Math.sqrt method.
-void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
-
- if (!Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
- frame_->CallRuntime(Runtime::kMath_sqrt, 1);
- frame_->EmitPush(r0);
- } else {
- CpuFeatures::Scope scope(VFP3);
- JumpTarget runtime, done;
-
- Register scratch1 = VirtualFrame::scratch0();
- Register scratch2 = VirtualFrame::scratch1();
-
- // Get the value from the frame.
- Register tos = frame_->PopToRegister();
-
- // Set the frame for the runtime jump target. The code below jumps to the
- // jump target label so the frame needs to be established before that.
- ASSERT(runtime.entry_frame() == NULL);
- runtime.set_entry_frame(frame_);
-
- Register heap_number_map = r6;
- Register new_heap_number = r5;
- __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
-
- // Get the double value from the heap number into vfp register d0.
- __ ObjectToDoubleVFPRegister(tos, d0,
- scratch1, scratch2, heap_number_map, s0,
- runtime.entry_label());
-
- // Calculate the square root of d0 and place result in a heap number object.
- __ vsqrt(d0, d0);
- __ AllocateHeapNumberWithValue(new_heap_number,
- d0,
- scratch1, scratch2,
- heap_number_map,
- runtime.entry_label());
- __ mov(tos, Operand(new_heap_number));
- done.Jump();
-
- runtime.Bind();
- // Push back the argument again for the runtime call.
- frame_->EmitPush(tos);
- frame_->CallRuntime(Runtime::kMath_sqrt, 1);
- __ Move(tos, r0);
-
- done.Bind();
- frame_->EmitPush(tos);
- }
-}
-
-
-class DeferredStringCharCodeAt : public DeferredCode {
- public:
- DeferredStringCharCodeAt(Register object,
- Register index,
- Register scratch,
- Register result)
- : result_(result),
- char_code_at_generator_(object,
- index,
- scratch,
- result,
- &need_conversion_,
- &need_conversion_,
- &index_out_of_range_,
- STRING_INDEX_IS_NUMBER) {}
-
- StringCharCodeAtGenerator* fast_case_generator() {
- return &char_code_at_generator_;
- }
-
- virtual void Generate() {
- VirtualFrameRuntimeCallHelper call_helper(frame_state());
- char_code_at_generator_.GenerateSlow(masm(), call_helper);
-
- __ bind(&need_conversion_);
- // Move the undefined value into the result register, which will
- // trigger conversion.
- __ LoadRoot(result_, Heap::kUndefinedValueRootIndex);
- __ jmp(exit_label());
-
- __ bind(&index_out_of_range_);
- // When the index is out of range, the spec requires us to return
- // NaN.
- __ LoadRoot(result_, Heap::kNanValueRootIndex);
- __ jmp(exit_label());
- }
-
- private:
- Register result_;
-
- Label need_conversion_;
- Label index_out_of_range_;
-
- StringCharCodeAtGenerator char_code_at_generator_;
-};
-
-
-// This generates code that performs a String.prototype.charCodeAt() call
-// or returns a smi in order to trigger conversion.
-void CodeGenerator::GenerateStringCharCodeAt(ZoneList<Expression*>* args) {
- Comment(masm_, "[ GenerateStringCharCodeAt");
- ASSERT(args->length() == 2);
-
- Load(args->at(0));
- Load(args->at(1));
-
- Register index = frame_->PopToRegister();
- Register object = frame_->PopToRegister(index);
-
- // We need two extra registers.
- Register scratch = VirtualFrame::scratch0();
- Register result = VirtualFrame::scratch1();
-
- DeferredStringCharCodeAt* deferred =
- new DeferredStringCharCodeAt(object,
- index,
- scratch,
- result);
- deferred->fast_case_generator()->GenerateFast(masm_);
- deferred->BindExit();
- frame_->EmitPush(result);
-}
-
-
-class DeferredStringCharFromCode : public DeferredCode {
- public:
- DeferredStringCharFromCode(Register code,
- Register result)
- : char_from_code_generator_(code, result) {}
-
- StringCharFromCodeGenerator* fast_case_generator() {
- return &char_from_code_generator_;
- }
-
- virtual void Generate() {
- VirtualFrameRuntimeCallHelper call_helper(frame_state());
- char_from_code_generator_.GenerateSlow(masm(), call_helper);
- }
-
- private:
- StringCharFromCodeGenerator char_from_code_generator_;
-};
-
-
-// Generates code for creating a one-char string from a char code.
-void CodeGenerator::GenerateStringCharFromCode(ZoneList<Expression*>* args) {
- Comment(masm_, "[ GenerateStringCharFromCode");
- ASSERT(args->length() == 1);
-
- Load(args->at(0));
-
- Register result = frame_->GetTOSRegister();
- Register code = frame_->PopToRegister(result);
-
- DeferredStringCharFromCode* deferred = new DeferredStringCharFromCode(
- code, result);
- deferred->fast_case_generator()->GenerateFast(masm_);
- deferred->BindExit();
- frame_->EmitPush(result);
-}
-
-
-class DeferredStringCharAt : public DeferredCode {
- public:
- DeferredStringCharAt(Register object,
- Register index,
- Register scratch1,
- Register scratch2,
- Register result)
- : result_(result),
- char_at_generator_(object,
- index,
- scratch1,
- scratch2,
- result,
- &need_conversion_,
- &need_conversion_,
- &index_out_of_range_,
- STRING_INDEX_IS_NUMBER) {}
-
- StringCharAtGenerator* fast_case_generator() {
- return &char_at_generator_;
- }
-
- virtual void Generate() {
- VirtualFrameRuntimeCallHelper call_helper(frame_state());
- char_at_generator_.GenerateSlow(masm(), call_helper);
-
- __ bind(&need_conversion_);
- // Move smi zero into the result register, which will trigger
- // conversion.
- __ mov(result_, Operand(Smi::FromInt(0)));
- __ jmp(exit_label());
-
- __ bind(&index_out_of_range_);
- // When the index is out of range, the spec requires us to return
- // the empty string.
- __ LoadRoot(result_, Heap::kEmptyStringRootIndex);
- __ jmp(exit_label());
- }
-
- private:
- Register result_;
-
- Label need_conversion_;
- Label index_out_of_range_;
-
- StringCharAtGenerator char_at_generator_;
-};
-
-
-// This generates code that performs a String.prototype.charAt() call
-// or returns a smi in order to trigger conversion.
-void CodeGenerator::GenerateStringCharAt(ZoneList<Expression*>* args) {
- Comment(masm_, "[ GenerateStringCharAt");
- ASSERT(args->length() == 2);
-
- Load(args->at(0));
- Load(args->at(1));
-
- Register index = frame_->PopToRegister();
- Register object = frame_->PopToRegister(index);
-
- // We need three extra registers.
- Register scratch1 = VirtualFrame::scratch0();
- Register scratch2 = VirtualFrame::scratch1();
- // Use r6 without notifying the virtual frame.
- Register result = r6;
-
- DeferredStringCharAt* deferred =
- new DeferredStringCharAt(object,
- index,
- scratch1,
- scratch2,
- result);
- deferred->fast_case_generator()->GenerateFast(masm_);
- deferred->BindExit();
- frame_->EmitPush(result);
-}
-
-
-void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- JumpTarget answer;
- // We need the CC bits to come out as not_equal in the case where the
- // object is a smi. This can't be done with the usual test opcode so
- // we use XOR to get the right CC bits.
- Register possible_array = frame_->PopToRegister();
- Register scratch = VirtualFrame::scratch0();
- __ and_(scratch, possible_array, Operand(kSmiTagMask));
- __ eor(scratch, scratch, Operand(kSmiTagMask), SetCC);
- answer.Branch(ne);
- // It is a heap object - get the map. Check if the object is a JS array.
- __ CompareObjectType(possible_array, scratch, scratch, JS_ARRAY_TYPE);
- answer.Bind();
- cc_reg_ = eq;
-}
-
-
-void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- JumpTarget answer;
- // We need the CC bits to come out as not_equal in the case where the
- // object is a smi. This can't be done with the usual test opcode so
- // we use XOR to get the right CC bits.
- Register possible_regexp = frame_->PopToRegister();
- Register scratch = VirtualFrame::scratch0();
- __ and_(scratch, possible_regexp, Operand(kSmiTagMask));
- __ eor(scratch, scratch, Operand(kSmiTagMask), SetCC);
- answer.Branch(ne);
- // It is a heap object - get the map. Check if the object is a regexp.
- __ CompareObjectType(possible_regexp, scratch, scratch, JS_REGEXP_TYPE);
- answer.Bind();
- cc_reg_ = eq;
-}
-
-
-void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
- // This generates a fast version of:
- // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Register possible_object = frame_->PopToRegister();
- __ tst(possible_object, Operand(kSmiTagMask));
- false_target()->Branch(eq);
-
- __ LoadRoot(ip, Heap::kNullValueRootIndex);
- __ cmp(possible_object, ip);
- true_target()->Branch(eq);
-
- Register map_reg = VirtualFrame::scratch0();
- __ ldr(map_reg, FieldMemOperand(possible_object, HeapObject::kMapOffset));
- // Undetectable objects behave like undefined when tested with typeof.
- __ ldrb(possible_object, FieldMemOperand(map_reg, Map::kBitFieldOffset));
- __ tst(possible_object, Operand(1 << Map::kIsUndetectable));
- false_target()->Branch(ne);
-
- __ ldrb(possible_object, FieldMemOperand(map_reg, Map::kInstanceTypeOffset));
- __ cmp(possible_object, Operand(FIRST_JS_OBJECT_TYPE));
- false_target()->Branch(lt);
- __ cmp(possible_object, Operand(LAST_JS_OBJECT_TYPE));
- cc_reg_ = le;
-}
-
-
-void CodeGenerator::GenerateIsSpecObject(ZoneList<Expression*>* args) {
- // This generates a fast version of:
- // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp' ||
- // typeof(arg) == function).
- // It includes undetectable objects (as opposed to IsObject).
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Register value = frame_->PopToRegister();
- __ tst(value, Operand(kSmiTagMask));
- false_target()->Branch(eq);
- // Check that this is an object.
- __ ldr(value, FieldMemOperand(value, HeapObject::kMapOffset));
- __ ldrb(value, FieldMemOperand(value, Map::kInstanceTypeOffset));
- __ cmp(value, Operand(FIRST_JS_OBJECT_TYPE));
- cc_reg_ = ge;
-}
-
-
-// Deferred code to check whether the String JavaScript object is safe for using
-// default value of. This code is called after the bit caching this information
-// in the map has been checked with the map for the object in the map_result_
-// register. On return the register map_result_ contains 1 for true and 0 for
-// false.
-class DeferredIsStringWrapperSafeForDefaultValueOf : public DeferredCode {
- public:
- DeferredIsStringWrapperSafeForDefaultValueOf(Register object,
- Register map_result,
- Register scratch1,
- Register scratch2)
- : object_(object),
- map_result_(map_result),
- scratch1_(scratch1),
- scratch2_(scratch2) { }
-
- virtual void Generate() {
- Label false_result;
-
- // Check that map is loaded as expected.
- if (FLAG_debug_code) {
- __ ldr(ip, FieldMemOperand(object_, HeapObject::kMapOffset));
- __ cmp(map_result_, ip);
- __ Assert(eq, "Map not in expected register");
- }
-
- // Check for fast case object. Generate false result for slow case object.
- __ ldr(scratch1_, FieldMemOperand(object_, JSObject::kPropertiesOffset));
- __ ldr(scratch1_, FieldMemOperand(scratch1_, HeapObject::kMapOffset));
- __ LoadRoot(ip, Heap::kHashTableMapRootIndex);
- __ cmp(scratch1_, ip);
- __ b(eq, &false_result);
-
- // Look for valueOf symbol in the descriptor array, and indicate false if
- // found. The type is not checked, so if it is a transition it is a false
- // negative.
- __ ldr(map_result_,
- FieldMemOperand(map_result_, Map::kInstanceDescriptorsOffset));
- __ ldr(scratch2_, FieldMemOperand(map_result_, FixedArray::kLengthOffset));
- // map_result_: descriptor array
- // scratch2_: length of descriptor array
- // Calculate the end of the descriptor array.
- STATIC_ASSERT(kSmiTag == 0);
- STATIC_ASSERT(kSmiTagSize == 1);
- STATIC_ASSERT(kPointerSize == 4);
- __ add(scratch1_,
- map_result_,
- Operand(FixedArray::kHeaderSize - kHeapObjectTag));
- __ add(scratch1_,
- scratch1_,
- Operand(scratch2_, LSL, kPointerSizeLog2 - kSmiTagSize));
-
- // Calculate location of the first key name.
- __ add(map_result_,
- map_result_,
- Operand(FixedArray::kHeaderSize - kHeapObjectTag +
- DescriptorArray::kFirstIndex * kPointerSize));
- // Loop through all the keys in the descriptor array. If one of these is the
- // symbol valueOf the result is false.
- Label entry, loop;
- // The use of ip to store the valueOf symbol asumes that it is not otherwise
- // used in the loop below.
- __ mov(ip, Operand(FACTORY->value_of_symbol()));
- __ jmp(&entry);
- __ bind(&loop);
- __ ldr(scratch2_, MemOperand(map_result_, 0));
- __ cmp(scratch2_, ip);
- __ b(eq, &false_result);
- __ add(map_result_, map_result_, Operand(kPointerSize));
- __ bind(&entry);
- __ cmp(map_result_, Operand(scratch1_));
- __ b(ne, &loop);
-
- // Reload map as register map_result_ was used as temporary above.
- __ ldr(map_result_, FieldMemOperand(object_, HeapObject::kMapOffset));
-
- // If a valueOf property is not found on the object check that it's
- // prototype is the un-modified String prototype. If not result is false.
- __ ldr(scratch1_, FieldMemOperand(map_result_, Map::kPrototypeOffset));
- __ tst(scratch1_, Operand(kSmiTagMask));
- __ b(eq, &false_result);
- __ ldr(scratch1_, FieldMemOperand(scratch1_, HeapObject::kMapOffset));
- __ ldr(scratch2_,
- ContextOperand(cp, Context::GLOBAL_INDEX));
- __ ldr(scratch2_,
- FieldMemOperand(scratch2_, GlobalObject::kGlobalContextOffset));
- __ ldr(scratch2_,
- ContextOperand(
- scratch2_, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX));
- __ cmp(scratch1_, scratch2_);
- __ b(ne, &false_result);
-
- // Set the bit in the map to indicate that it has been checked safe for
- // default valueOf and set true result.
- __ ldrb(scratch1_, FieldMemOperand(map_result_, Map::kBitField2Offset));
- __ orr(scratch1_,
- scratch1_,
- Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
- __ strb(scratch1_, FieldMemOperand(map_result_, Map::kBitField2Offset));
- __ mov(map_result_, Operand(1));
- __ jmp(exit_label());
- __ bind(&false_result);
- // Set false result.
- __ mov(map_result_, Operand(0, RelocInfo::NONE));
- }
-
- private:
- Register object_;
- Register map_result_;
- Register scratch1_;
- Register scratch2_;
-};
-
-
-void CodeGenerator::GenerateIsStringWrapperSafeForDefaultValueOf(
- ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Register obj = frame_->PopToRegister(); // Pop the string wrapper.
- if (FLAG_debug_code) {
- __ AbortIfSmi(obj);
- }
-
- // Check whether this map has already been checked to be safe for default
- // valueOf.
- Register map_result = VirtualFrame::scratch0();
- __ ldr(map_result, FieldMemOperand(obj, HeapObject::kMapOffset));
- __ ldrb(ip, FieldMemOperand(map_result, Map::kBitField2Offset));
- __ tst(ip, Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
- true_target()->Branch(ne);
-
- // We need an additional two scratch registers for the deferred code.
- Register scratch1 = VirtualFrame::scratch1();
- // Use r6 without notifying the virtual frame.
- Register scratch2 = r6;
-
- DeferredIsStringWrapperSafeForDefaultValueOf* deferred =
- new DeferredIsStringWrapperSafeForDefaultValueOf(
- obj, map_result, scratch1, scratch2);
- deferred->Branch(eq);
- deferred->BindExit();
- __ tst(map_result, Operand(map_result));
- cc_reg_ = ne;
-}
-
-
-void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
- // This generates a fast version of:
- // (%_ClassOf(arg) === 'Function')
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Register possible_function = frame_->PopToRegister();
- __ tst(possible_function, Operand(kSmiTagMask));
- false_target()->Branch(eq);
- Register map_reg = VirtualFrame::scratch0();
- Register scratch = VirtualFrame::scratch1();
- __ CompareObjectType(possible_function, map_reg, scratch, JS_FUNCTION_TYPE);
- cc_reg_ = eq;
-}
-
-
-void CodeGenerator::GenerateIsUndetectableObject(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Register possible_undetectable = frame_->PopToRegister();
- __ tst(possible_undetectable, Operand(kSmiTagMask));
- false_target()->Branch(eq);
- Register scratch = VirtualFrame::scratch0();
- __ ldr(scratch,
- FieldMemOperand(possible_undetectable, HeapObject::kMapOffset));
- __ ldrb(scratch, FieldMemOperand(scratch, Map::kBitFieldOffset));
- __ tst(scratch, Operand(1 << Map::kIsUndetectable));
- cc_reg_ = ne;
-}
-
-
-void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 0);
-
- Register scratch0 = VirtualFrame::scratch0();
- Register scratch1 = VirtualFrame::scratch1();
- // Get the frame pointer for the calling frame.
- __ ldr(scratch0, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
-
- // Skip the arguments adaptor frame if it exists.
- __ ldr(scratch1,
- MemOperand(scratch0, StandardFrameConstants::kContextOffset));
- __ cmp(scratch1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
- __ ldr(scratch0,
- MemOperand(scratch0, StandardFrameConstants::kCallerFPOffset), eq);
-
- // Check the marker in the calling frame.
- __ ldr(scratch1,
- MemOperand(scratch0, StandardFrameConstants::kMarkerOffset));
- __ cmp(scratch1, Operand(Smi::FromInt(StackFrame::CONSTRUCT)));
- cc_reg_ = eq;
-}
-
-
-void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 0);
-
- Register tos = frame_->GetTOSRegister();
- Register scratch0 = VirtualFrame::scratch0();
- Register scratch1 = VirtualFrame::scratch1();
-
- // Check if the calling frame is an arguments adaptor frame.
- __ ldr(scratch0,
- MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
- __ ldr(scratch1,
- MemOperand(scratch0, StandardFrameConstants::kContextOffset));
- __ cmp(scratch1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
-
- // Get the number of formal parameters.
- __ mov(tos, Operand(Smi::FromInt(scope()->num_parameters())), LeaveCC, ne);
-
- // Arguments adaptor case: Read the arguments length from the
- // adaptor frame.
- __ ldr(tos,
- MemOperand(scratch0, ArgumentsAdaptorFrameConstants::kLengthOffset),
- eq);
-
- frame_->EmitPush(tos);
-}
-
-
-void CodeGenerator::GenerateArguments(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
-
- // Satisfy contract with ArgumentsAccessStub:
- // Load the key into r1 and the formal parameters count into r0.
- Load(args->at(0));
- frame_->PopToR1();
- frame_->SpillAll();
- __ mov(r0, Operand(Smi::FromInt(scope()->num_parameters())));
-
- // Call the shared stub to get to arguments[key].
- ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
- frame_->CallStub(&stub, 0);
- frame_->EmitPush(r0);
-}
-
-
-void CodeGenerator::GenerateRandomHeapNumber(
- ZoneList<Expression*>* args) {
- VirtualFrame::SpilledScope spilled_scope(frame_);
- ASSERT(args->length() == 0);
-
- Label slow_allocate_heapnumber;
- Label heapnumber_allocated;
-
- __ LoadRoot(r6, Heap::kHeapNumberMapRootIndex);
- __ AllocateHeapNumber(r4, r1, r2, r6, &slow_allocate_heapnumber);
- __ jmp(&heapnumber_allocated);
-
- __ bind(&slow_allocate_heapnumber);
- // Allocate a heap number.
- __ CallRuntime(Runtime::kNumberAlloc, 0);
- __ mov(r4, Operand(r0));
-
- __ bind(&heapnumber_allocated);
-
- // Convert 32 random bits in r0 to 0.(32 random bits) in a double
- // by computing:
- // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
- __ PrepareCallCFunction(0, r1);
- __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 0);
-
- CpuFeatures::Scope scope(VFP3);
- // 0x41300000 is the top half of 1.0 x 2^20 as a double.
- // Create this constant using mov/orr to avoid PC relative load.
- __ mov(r1, Operand(0x41000000));
- __ orr(r1, r1, Operand(0x300000));
- // Move 0x41300000xxxxxxxx (x = random bits) to VFP.
- __ vmov(d7, r0, r1);
- // Move 0x4130000000000000 to VFP.
- __ mov(r0, Operand(0, RelocInfo::NONE));
- __ vmov(d8, r0, r1);
- // Subtract and store the result in the heap number.
- __ vsub(d7, d7, d8);
- __ sub(r0, r4, Operand(kHeapObjectTag));
- __ vstr(d7, r0, HeapNumber::kValueOffset);
- frame_->EmitPush(r4);
- } else {
- __ mov(r0, Operand(r4));
- __ PrepareCallCFunction(1, r1);
- __ CallCFunction(
- ExternalReference::fill_heap_number_with_random_function(isolate()), 1);
- frame_->EmitPush(r0);
- }
-}
-
-
-void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
- ASSERT_EQ(2, args->length());
-
- Load(args->at(0));
- Load(args->at(1));
-
- StringAddStub stub(NO_STRING_ADD_FLAGS);
- frame_->SpillAll();
- frame_->CallStub(&stub, 2);
- frame_->EmitPush(r0);
-}
-
-
-void CodeGenerator::GenerateSubString(ZoneList<Expression*>* args) {
- ASSERT_EQ(3, args->length());
-
- Load(args->at(0));
- Load(args->at(1));
- Load(args->at(2));
-
- SubStringStub stub;
- frame_->SpillAll();
- frame_->CallStub(&stub, 3);
- frame_->EmitPush(r0);
-}
-
-
-void CodeGenerator::GenerateStringCompare(ZoneList<Expression*>* args) {
- ASSERT_EQ(2, args->length());
-
- Load(args->at(0));
- Load(args->at(1));
-
- StringCompareStub stub;
- frame_->SpillAll();
- frame_->CallStub(&stub, 2);
- frame_->EmitPush(r0);
-}
-
-
-void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
- ASSERT_EQ(4, args->length());
-
- Load(args->at(0));
- Load(args->at(1));
- Load(args->at(2));
- Load(args->at(3));
- RegExpExecStub stub;
- frame_->SpillAll();
- frame_->CallStub(&stub, 4);
- frame_->EmitPush(r0);
-}
-
-
-void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
- ASSERT_EQ(3, args->length());
-
- Load(args->at(0)); // Size of array, smi.
- Load(args->at(1)); // "index" property value.
- Load(args->at(2)); // "input" property value.
- RegExpConstructResultStub stub;
- frame_->SpillAll();
- frame_->CallStub(&stub, 3);
- frame_->EmitPush(r0);
-}
-
-
-class DeferredSearchCache: public DeferredCode {
- public:
- DeferredSearchCache(Register dst, Register cache, Register key)
- : dst_(dst), cache_(cache), key_(key) {
- set_comment("[ DeferredSearchCache");
- }
-
- virtual void Generate();
-
- private:
- Register dst_, cache_, key_;
-};
-
-
-void DeferredSearchCache::Generate() {
- __ Push(cache_, key_);
- __ CallRuntime(Runtime::kGetFromCache, 2);
- __ Move(dst_, r0);
-}
-
-
-void CodeGenerator::GenerateGetFromCache(ZoneList<Expression*>* args) {
- ASSERT_EQ(2, args->length());
-
- ASSERT_NE(NULL, args->at(0)->AsLiteral());
- int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
-
- Handle<FixedArray> jsfunction_result_caches(
- Isolate::Current()->global_context()->jsfunction_result_caches());
- if (jsfunction_result_caches->length() <= cache_id) {
- __ Abort("Attempt to use undefined cache.");
- frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
- return;
- }
-
- Load(args->at(1));
-
- frame_->PopToR1();
- frame_->SpillAll();
- Register key = r1; // Just poped to r1
- Register result = r0; // Free, as frame has just been spilled.
- Register scratch1 = VirtualFrame::scratch0();
- Register scratch2 = VirtualFrame::scratch1();
-
- __ ldr(scratch1, ContextOperand(cp, Context::GLOBAL_INDEX));
- __ ldr(scratch1,
- FieldMemOperand(scratch1, GlobalObject::kGlobalContextOffset));
- __ ldr(scratch1,
- ContextOperand(scratch1, Context::JSFUNCTION_RESULT_CACHES_INDEX));
- __ ldr(scratch1,
- FieldMemOperand(scratch1, FixedArray::OffsetOfElementAt(cache_id)));
-
- DeferredSearchCache* deferred =
- new DeferredSearchCache(result, scratch1, key);
-
- const int kFingerOffset =
- FixedArray::OffsetOfElementAt(JSFunctionResultCache::kFingerIndex);
- STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
- __ ldr(result, FieldMemOperand(scratch1, kFingerOffset));
- // result now holds finger offset as a smi.
- __ add(scratch2, scratch1, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
- // scratch2 now points to the start of fixed array elements.
- __ ldr(result,
- MemOperand(
- scratch2, result, LSL, kPointerSizeLog2 - kSmiTagSize, PreIndex));
- // Note side effect of PreIndex: scratch2 now points to the key of the pair.
- __ cmp(key, result);
- deferred->Branch(ne);
-
- __ ldr(result, MemOperand(scratch2, kPointerSize));
-
- deferred->BindExit();
- frame_->EmitPush(result);
-}
-
-
-void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
- ASSERT_EQ(args->length(), 1);
-
- // Load the argument on the stack and jump to the runtime.
- Load(args->at(0));
-
- NumberToStringStub stub;
- frame_->SpillAll();
- frame_->CallStub(&stub, 1);
- frame_->EmitPush(r0);
-}
-
-
-class DeferredSwapElements: public DeferredCode {
- public:
- DeferredSwapElements(Register object, Register index1, Register index2)
- : object_(object), index1_(index1), index2_(index2) {
- set_comment("[ DeferredSwapElements");
- }
-
- virtual void Generate();
-
- private:
- Register object_, index1_, index2_;
-};
-
-
-void DeferredSwapElements::Generate() {
- __ push(object_);
- __ push(index1_);
- __ push(index2_);
- __ CallRuntime(Runtime::kSwapElements, 3);
-}
-
-
-void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) {
- Comment cmnt(masm_, "[ GenerateSwapElements");
-
- ASSERT_EQ(3, args->length());
-
- Load(args->at(0));
- Load(args->at(1));
- Load(args->at(2));
-
- VirtualFrame::SpilledScope spilled_scope(frame_);
-
- Register index2 = r2;
- Register index1 = r1;
- Register object = r0;
- Register tmp1 = r3;
- Register tmp2 = r4;
-
- frame_->EmitPop(index2);
- frame_->EmitPop(index1);
- frame_->EmitPop(object);
-
- DeferredSwapElements* deferred =
- new DeferredSwapElements(object, index1, index2);
-
- // Fetch the map and check if array is in fast case.
- // Check that object doesn't require security checks and
- // has no indexed interceptor.
- __ CompareObjectType(object, tmp1, tmp2, JS_ARRAY_TYPE);
- deferred->Branch(ne);
- __ ldrb(tmp2, FieldMemOperand(tmp1, Map::kBitFieldOffset));
- __ tst(tmp2, Operand(KeyedLoadIC::kSlowCaseBitFieldMask));
- deferred->Branch(ne);
-
- // Check the object's elements are in fast case and writable.
- __ ldr(tmp1, FieldMemOperand(object, JSObject::kElementsOffset));
- __ ldr(tmp2, FieldMemOperand(tmp1, HeapObject::kMapOffset));
- __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
- __ cmp(tmp2, ip);
- deferred->Branch(ne);
-
- // Smi-tagging is equivalent to multiplying by 2.
- STATIC_ASSERT(kSmiTag == 0);
- STATIC_ASSERT(kSmiTagSize == 1);
-
- // Check that both indices are smis.
- __ mov(tmp2, index1);
- __ orr(tmp2, tmp2, index2);
- __ tst(tmp2, Operand(kSmiTagMask));
- deferred->Branch(ne);
-
- // Check that both indices are valid.
- __ ldr(tmp2, FieldMemOperand(object, JSArray::kLengthOffset));
- __ cmp(tmp2, index1);
- __ cmp(tmp2, index2, hi);
- deferred->Branch(ls);
-
- // Bring the offsets into the fixed array in tmp1 into index1 and
- // index2.
- __ mov(tmp2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
- __ add(index1, tmp2, Operand(index1, LSL, kPointerSizeLog2 - kSmiTagSize));
- __ add(index2, tmp2, Operand(index2, LSL, kPointerSizeLog2 - kSmiTagSize));
-
- // Swap elements.
- Register tmp3 = object;
- object = no_reg;
- __ ldr(tmp3, MemOperand(tmp1, index1));
- __ ldr(tmp2, MemOperand(tmp1, index2));
- __ str(tmp3, MemOperand(tmp1, index2));
- __ str(tmp2, MemOperand(tmp1, index1));
-
- Label done;
- __ InNewSpace(tmp1, tmp2, eq, &done);
- // Possible optimization: do a check that both values are Smis
- // (or them and test against Smi mask.)
-
- __ mov(tmp2, tmp1);
- __ add(index1, index1, tmp1);
- __ add(index2, index2, tmp1);
- __ RecordWriteHelper(tmp1, index1, tmp3);
- __ RecordWriteHelper(tmp2, index2, tmp3);
- __ bind(&done);
-
- deferred->BindExit();
- __ LoadRoot(tmp1, Heap::kUndefinedValueRootIndex);
- frame_->EmitPush(tmp1);
-}
-
-
-void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) {
- Comment cmnt(masm_, "[ GenerateCallFunction");
-
- ASSERT(args->length() >= 2);
-
- int n_args = args->length() - 2; // for receiver and function.
- Load(args->at(0)); // receiver
- for (int i = 0; i < n_args; i++) {
- Load(args->at(i + 1));
- }
- Load(args->at(n_args + 1)); // function
- frame_->CallJSFunction(n_args);
- frame_->EmitPush(r0);
-}
-
-
-void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
- ASSERT_EQ(args->length(), 1);
- Load(args->at(0));
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
- TranscendentalCacheStub stub(TranscendentalCache::SIN,
- TranscendentalCacheStub::TAGGED);
- frame_->SpillAllButCopyTOSToR0();
- frame_->CallStub(&stub, 1);
- } else {
- frame_->CallRuntime(Runtime::kMath_sin, 1);
- }
- frame_->EmitPush(r0);
-}
-
-
-void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
- ASSERT_EQ(args->length(), 1);
- Load(args->at(0));
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
- TranscendentalCacheStub stub(TranscendentalCache::COS,
- TranscendentalCacheStub::TAGGED);
- frame_->SpillAllButCopyTOSToR0();
- frame_->CallStub(&stub, 1);
- } else {
- frame_->CallRuntime(Runtime::kMath_cos, 1);
- }
- frame_->EmitPush(r0);
-}
-
-
-void CodeGenerator::GenerateMathLog(ZoneList<Expression*>* args) {
- ASSERT_EQ(args->length(), 1);
- Load(args->at(0));
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
- TranscendentalCacheStub stub(TranscendentalCache::LOG,
- TranscendentalCacheStub::TAGGED);
- frame_->SpillAllButCopyTOSToR0();
- frame_->CallStub(&stub, 1);
- } else {
- frame_->CallRuntime(Runtime::kMath_log, 1);
- }
- frame_->EmitPush(r0);
-}
-
-
-void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 2);
-
- // Load the two objects into registers and perform the comparison.
- Load(args->at(0));
- Load(args->at(1));
- Register lhs = frame_->PopToRegister();
- Register rhs = frame_->PopToRegister(lhs);
- __ cmp(lhs, rhs);
- cc_reg_ = eq;
-}
-
-
-void CodeGenerator::GenerateIsRegExpEquivalent(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 2);
-
- // Load the two objects into registers and perform the comparison.
- Load(args->at(0));
- Load(args->at(1));
- Register right = frame_->PopToRegister();
- Register left = frame_->PopToRegister(right);
- Register tmp = frame_->scratch0();
- Register tmp2 = frame_->scratch1();
-
- // Jumps to done must have the eq flag set if the test is successful
- // and clear if the test has failed.
- Label done;
-
- // Fail if either is a non-HeapObject.
- __ cmp(left, Operand(right));
- __ b(eq, &done);
- __ and_(tmp, left, Operand(right));
- __ eor(tmp, tmp, Operand(kSmiTagMask));
- __ tst(tmp, Operand(kSmiTagMask));
- __ b(ne, &done);
- __ ldr(tmp, FieldMemOperand(left, HeapObject::kMapOffset));
- __ ldrb(tmp2, FieldMemOperand(tmp, Map::kInstanceTypeOffset));
- __ cmp(tmp2, Operand(JS_REGEXP_TYPE));
- __ b(ne, &done);
- __ ldr(tmp2, FieldMemOperand(right, HeapObject::kMapOffset));
- __ cmp(tmp, Operand(tmp2));
- __ b(ne, &done);
- __ ldr(tmp, FieldMemOperand(left, JSRegExp::kDataOffset));
- __ ldr(tmp2, FieldMemOperand(right, JSRegExp::kDataOffset));
- __ cmp(tmp, tmp2);
- __ bind(&done);
- cc_reg_ = eq;
-}
-
-
-void CodeGenerator::GenerateHasCachedArrayIndex(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Register value = frame_->PopToRegister();
- Register tmp = frame_->scratch0();
- __ ldr(tmp, FieldMemOperand(value, String::kHashFieldOffset));
- __ tst(tmp, Operand(String::kContainsCachedArrayIndexMask));
- cc_reg_ = eq;
-}
-
-
-void CodeGenerator::GenerateGetCachedArrayIndex(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Register value = frame_->PopToRegister();
-
- __ ldr(value, FieldMemOperand(value, String::kHashFieldOffset));
- __ IndexFromHash(value, value);
- frame_->EmitPush(value);
-}
-
-
-void CodeGenerator::GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 2);
- Load(args->at(0));
- Register value = frame_->PopToRegister();
- __ LoadRoot(value, Heap::kUndefinedValueRootIndex);
- frame_->EmitPush(value);
-}
-
-
-void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- if (CheckForInlineRuntimeCall(node)) {
- ASSERT((has_cc() && frame_->height() == original_height) ||
- (!has_cc() && frame_->height() == original_height + 1));
- return;
- }
-
- ZoneList<Expression*>* args = node->arguments();
- Comment cmnt(masm_, "[ CallRuntime");
- const Runtime::Function* function = node->function();
-
- if (function == NULL) {
- // Prepare stack for calling JS runtime function.
- // Push the builtins object found in the current global object.
- Register scratch = VirtualFrame::scratch0();
- __ ldr(scratch, GlobalObjectOperand());
- Register builtins = frame_->GetTOSRegister();
- __ ldr(builtins, FieldMemOperand(scratch, GlobalObject::kBuiltinsOffset));
- frame_->EmitPush(builtins);
- }
-
- // Push the arguments ("left-to-right").
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- }
-
- VirtualFrame::SpilledScope spilled_scope(frame_);
-
- if (function == NULL) {
- // Call the JS runtime function.
- __ mov(r2, Operand(node->name()));
- InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
- Handle<Code> stub =
- ISOLATE->stub_cache()->ComputeCallInitialize(arg_count, in_loop);
- frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
- __ ldr(cp, frame_->Context());
- frame_->EmitPush(r0);
- } else {
- // Call the C runtime function.
- frame_->CallRuntime(function, arg_count);
- frame_->EmitPush(r0);
- }
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ UnaryOperation");
-
- Token::Value op = node->op();
-
- if (op == Token::NOT) {
- LoadCondition(node->expression(), false_target(), true_target(), true);
- // LoadCondition may (and usually does) leave a test and branch to
- // be emitted by the caller. In that case, negate the condition.
- if (has_cc()) cc_reg_ = NegateCondition(cc_reg_);
-
- } else if (op == Token::DELETE) {
- Property* property = node->expression()->AsProperty();
- Variable* variable = node->expression()->AsVariableProxy()->AsVariable();
- if (property != NULL) {
- Load(property->obj());
- Load(property->key());
- frame_->EmitPush(Operand(Smi::FromInt(strict_mode_flag())));
- frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 3);
- frame_->EmitPush(r0);
-
- } else if (variable != NULL) {
- // Delete of an unqualified identifier is disallowed in strict mode
- // but "delete this" is.
- ASSERT(strict_mode_flag() == kNonStrictMode || variable->is_this());
- Slot* slot = variable->AsSlot();
- if (variable->is_global()) {
- LoadGlobal();
- frame_->EmitPush(Operand(variable->name()));
- frame_->EmitPush(Operand(Smi::FromInt(kNonStrictMode)));
- frame_->InvokeBuiltin(Builtins::DELETE, CALL_JS, 3);
- frame_->EmitPush(r0);
-
- } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
- // Delete from the context holding the named variable.
- frame_->EmitPush(cp);
- frame_->EmitPush(Operand(variable->name()));
- frame_->CallRuntime(Runtime::kDeleteContextSlot, 2);
- frame_->EmitPush(r0);
-
- } else {
- // Default: Result of deleting non-global, not dynamically
- // introduced variables is false.
- frame_->EmitPushRoot(Heap::kFalseValueRootIndex);
- }
-
- } else {
- // Default: Result of deleting expressions is true.
- Load(node->expression()); // may have side-effects
- frame_->Drop();
- frame_->EmitPushRoot(Heap::kTrueValueRootIndex);
- }
-
- } else if (op == Token::TYPEOF) {
- // Special case for loading the typeof expression; see comment on
- // LoadTypeofExpression().
- LoadTypeofExpression(node->expression());
- frame_->CallRuntime(Runtime::kTypeof, 1);
- frame_->EmitPush(r0); // r0 has result
-
- } else {
- bool can_overwrite = node->expression()->ResultOverwriteAllowed();
- UnaryOverwriteMode overwrite =
- can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
-
- bool no_negative_zero = node->expression()->no_negative_zero();
- Load(node->expression());
- switch (op) {
- case Token::NOT:
- case Token::DELETE:
- case Token::TYPEOF:
- UNREACHABLE(); // handled above
- break;
-
- case Token::SUB: {
- frame_->PopToR0();
- GenericUnaryOpStub stub(
- Token::SUB,
- overwrite,
- NO_UNARY_FLAGS,
- no_negative_zero ? kIgnoreNegativeZero : kStrictNegativeZero);
- frame_->CallStub(&stub, 0);
- frame_->EmitPush(r0); // r0 has result
- break;
- }
-
- case Token::BIT_NOT: {
- Register tos = frame_->PopToRegister();
- JumpTarget not_smi_label;
- JumpTarget continue_label;
- // Smi check.
- __ tst(tos, Operand(kSmiTagMask));
- not_smi_label.Branch(ne);
-
- __ mvn(tos, Operand(tos));
- __ bic(tos, tos, Operand(kSmiTagMask)); // Bit-clear inverted smi-tag.
- frame_->EmitPush(tos);
- // The fast case is the first to jump to the continue label, so it gets
- // to decide the virtual frame layout.
- continue_label.Jump();
-
- not_smi_label.Bind();
- frame_->SpillAll();
- __ Move(r0, tos);
- GenericUnaryOpStub stub(Token::BIT_NOT,
- overwrite,
- NO_UNARY_SMI_CODE_IN_STUB);
- frame_->CallStub(&stub, 0);
- frame_->EmitPush(r0);
-
- continue_label.Bind();
- break;
- }
-
- case Token::VOID:
- frame_->Drop();
- frame_->EmitPushRoot(Heap::kUndefinedValueRootIndex);
- break;
-
- case Token::ADD: {
- Register tos = frame_->Peek();
- // Smi check.
- JumpTarget continue_label;
- __ tst(tos, Operand(kSmiTagMask));
- continue_label.Branch(eq);
-
- frame_->InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, 1);
- frame_->EmitPush(r0);
-
- continue_label.Bind();
- break;
- }
- default:
- UNREACHABLE();
- }
- }
- ASSERT(!has_valid_frame() ||
- (has_cc() && frame_->height() == original_height) ||
- (!has_cc() && frame_->height() == original_height + 1));
-}
-
-
-class DeferredCountOperation: public DeferredCode {
- public:
- DeferredCountOperation(Register value,
- bool is_increment,
- bool is_postfix,
- int target_size)
- : value_(value),
- is_increment_(is_increment),
- is_postfix_(is_postfix),
- target_size_(target_size) {}
-
- virtual void Generate() {
- VirtualFrame copied_frame(*frame_state()->frame());
-
- Label slow;
- // Check for smi operand.
- __ tst(value_, Operand(kSmiTagMask));
- __ b(ne, &slow);
-
- // Revert optimistic increment/decrement.
- if (is_increment_) {
- __ sub(value_, value_, Operand(Smi::FromInt(1)));
- } else {
- __ add(value_, value_, Operand(Smi::FromInt(1)));
- }
-
- // Slow case: Convert to number. At this point the
- // value to be incremented is in the value register..
- __ bind(&slow);
-
- // Convert the operand to a number.
- copied_frame.EmitPush(value_);
-
- copied_frame.InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, 1);
-
- if (is_postfix_) {
- // Postfix: store to result (on the stack).
- __ str(r0, MemOperand(sp, target_size_ * kPointerSize));
- }
-
- copied_frame.EmitPush(r0);
- copied_frame.EmitPush(Operand(Smi::FromInt(1)));
-
- if (is_increment_) {
- copied_frame.CallRuntime(Runtime::kNumberAdd, 2);
- } else {
- copied_frame.CallRuntime(Runtime::kNumberSub, 2);
- }
-
- __ Move(value_, r0);
-
- copied_frame.MergeTo(frame_state()->frame());
- }
-
- private:
- Register value_;
- bool is_increment_;
- bool is_postfix_;
- int target_size_;
-};
-
-
-void CodeGenerator::VisitCountOperation(CountOperation* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ CountOperation");
- VirtualFrame::RegisterAllocationScope scope(this);
-
- bool is_postfix = node->is_postfix();
- bool is_increment = node->op() == Token::INC;
-
- Variable* var = node->expression()->AsVariableProxy()->AsVariable();
- bool is_const = (var != NULL && var->mode() == Variable::CONST);
- bool is_slot = (var != NULL && var->mode() == Variable::VAR);
-
- if (!is_const && is_slot && type_info(var->AsSlot()).IsSmi()) {
- // The type info declares that this variable is always a Smi. That
- // means it is a Smi both before and after the increment/decrement.
- // Lets make use of that to make a very minimal count.
- Reference target(this, node->expression(), !is_const);
- ASSERT(!target.is_illegal());
- target.GetValue(); // Pushes the value.
- Register value = frame_->PopToRegister();
- if (is_postfix) frame_->EmitPush(value);
- if (is_increment) {
- __ add(value, value, Operand(Smi::FromInt(1)));
- } else {
- __ sub(value, value, Operand(Smi::FromInt(1)));
- }
- frame_->EmitPush(value);
- target.SetValue(NOT_CONST_INIT, LIKELY_SMI);
- if (is_postfix) frame_->Pop();
- ASSERT_EQ(original_height + 1, frame_->height());
- return;
- }
-
- // If it's a postfix expression and its result is not ignored and the
- // reference is non-trivial, then push a placeholder on the stack now
- // to hold the result of the expression.
- bool placeholder_pushed = false;
- if (!is_slot && is_postfix) {
- frame_->EmitPush(Operand(Smi::FromInt(0)));
- placeholder_pushed = true;
- }
-
- // A constant reference is not saved to, so a constant reference is not a
- // compound assignment reference.
- { Reference target(this, node->expression(), !is_const);
- if (target.is_illegal()) {
- // Spoof the virtual frame to have the expected height (one higher
- // than on entry).
- if (!placeholder_pushed) frame_->EmitPush(Operand(Smi::FromInt(0)));
- ASSERT_EQ(original_height + 1, frame_->height());
- return;
- }
-
- // This pushes 0, 1 or 2 words on the object to be used later when updating
- // the target. It also pushes the current value of the target.
- target.GetValue();
-
- bool value_is_known_smi = frame_->KnownSmiAt(0);
- Register value = frame_->PopToRegister();
-
- // Postfix: Store the old value as the result.
- if (placeholder_pushed) {
- frame_->SetElementAt(value, target.size());
- } else if (is_postfix) {
- frame_->EmitPush(value);
- __ mov(VirtualFrame::scratch0(), value);
- value = VirtualFrame::scratch0();
- }
-
- // We can't use any type information here since the virtual frame from the
- // deferred code may have lost information and we can't merge a virtual
- // frame with less specific type knowledge to a virtual frame with more
- // specific knowledge that has already used that specific knowledge to
- // generate code.
- frame_->ForgetTypeInfo();
-
- // The constructor here will capture the current virtual frame and use it to
- // merge to after the deferred code has run. No virtual frame changes are
- // allowed from here until the 'BindExit' below.
- DeferredCode* deferred =
- new DeferredCountOperation(value,
- is_increment,
- is_postfix,
- target.size());
- if (!value_is_known_smi) {
- // Check for smi operand.
- __ tst(value, Operand(kSmiTagMask));
-
- deferred->Branch(ne);
- }
-
- // Perform optimistic increment/decrement.
- if (is_increment) {
- __ add(value, value, Operand(Smi::FromInt(1)), SetCC);
- } else {
- __ sub(value, value, Operand(Smi::FromInt(1)), SetCC);
- }
-
- // If increment/decrement overflows, go to deferred code.
- deferred->Branch(vs);
-
- deferred->BindExit();
-
- // Store the new value in the target if not const.
- // At this point the answer is in the value register.
- frame_->EmitPush(value);
- // Set the target with the result, leaving the result on
- // top of the stack. Removes the target from the stack if
- // it has a non-zero size.
- if (!is_const) target.SetValue(NOT_CONST_INIT, LIKELY_SMI);
- }
-
- // Postfix: Discard the new value and use the old.
- if (is_postfix) frame_->Pop();
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::GenerateLogicalBooleanOperation(BinaryOperation* node) {
- // According to ECMA-262 section 11.11, page 58, the binary logical
- // operators must yield the result of one of the two expressions
- // before any ToBoolean() conversions. This means that the value
- // produced by a && or || operator is not necessarily a boolean.
-
- // NOTE: If the left hand side produces a materialized value (not in
- // the CC register), we force the right hand side to do the
- // same. This is necessary because we may have to branch to the exit
- // after evaluating the left hand side (due to the shortcut
- // semantics), but the compiler must (statically) know if the result
- // of compiling the binary operation is materialized or not.
- if (node->op() == Token::AND) {
- JumpTarget is_true;
- LoadCondition(node->left(), &is_true, false_target(), false);
- if (has_valid_frame() && !has_cc()) {
- // The left-hand side result is on top of the virtual frame.
- JumpTarget pop_and_continue;
- JumpTarget exit;
-
- frame_->Dup();
- // Avoid popping the result if it converts to 'false' using the
- // standard ToBoolean() conversion as described in ECMA-262,
- // section 9.2, page 30.
- ToBoolean(&pop_and_continue, &exit);
- Branch(false, &exit);
-
- // Pop the result of evaluating the first part.
- pop_and_continue.Bind();
- frame_->Pop();
-
- // Evaluate right side expression.
- is_true.Bind();
- Load(node->right());
-
- // Exit (always with a materialized value).
- exit.Bind();
- } else if (has_cc() || is_true.is_linked()) {
- // The left-hand side is either (a) partially compiled to
- // control flow with a final branch left to emit or (b) fully
- // compiled to control flow and possibly true.
- if (has_cc()) {
- Branch(false, false_target());
- }
- is_true.Bind();
- LoadCondition(node->right(), true_target(), false_target(), false);
- } else {
- // Nothing to do.
- ASSERT(!has_valid_frame() && !has_cc() && !is_true.is_linked());
- }
-
- } else {
- ASSERT(node->op() == Token::OR);
- JumpTarget is_false;
- LoadCondition(node->left(), true_target(), &is_false, false);
- if (has_valid_frame() && !has_cc()) {
- // The left-hand side result is on top of the virtual frame.
- JumpTarget pop_and_continue;
- JumpTarget exit;
-
- frame_->Dup();
- // Avoid popping the result if it converts to 'true' using the
- // standard ToBoolean() conversion as described in ECMA-262,
- // section 9.2, page 30.
- ToBoolean(&exit, &pop_and_continue);
- Branch(true, &exit);
-
- // Pop the result of evaluating the first part.
- pop_and_continue.Bind();
- frame_->Pop();
-
- // Evaluate right side expression.
- is_false.Bind();
- Load(node->right());
-
- // Exit (always with a materialized value).
- exit.Bind();
- } else if (has_cc() || is_false.is_linked()) {
- // The left-hand side is either (a) partially compiled to
- // control flow with a final branch left to emit or (b) fully
- // compiled to control flow and possibly false.
- if (has_cc()) {
- Branch(true, true_target());
- }
- is_false.Bind();
- LoadCondition(node->right(), true_target(), false_target(), false);
- } else {
- // Nothing to do.
- ASSERT(!has_valid_frame() && !has_cc() && !is_false.is_linked());
- }
- }
-}
-
-
-void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ BinaryOperation");
-
- if (node->op() == Token::AND || node->op() == Token::OR) {
- GenerateLogicalBooleanOperation(node);
- } else {
- // Optimize for the case where (at least) one of the expressions
- // is a literal small integer.
- Literal* lliteral = node->left()->AsLiteral();
- Literal* rliteral = node->right()->AsLiteral();
- // NOTE: The code below assumes that the slow cases (calls to runtime)
- // never return a constant/immutable object.
- bool overwrite_left = node->left()->ResultOverwriteAllowed();
- bool overwrite_right = node->right()->ResultOverwriteAllowed();
-
- if (rliteral != NULL && rliteral->handle()->IsSmi()) {
- VirtualFrame::RegisterAllocationScope scope(this);
- Load(node->left());
- if (frame_->KnownSmiAt(0)) overwrite_left = false;
- SmiOperation(node->op(),
- rliteral->handle(),
- false,
- overwrite_left ? OVERWRITE_LEFT : NO_OVERWRITE);
- } else if (lliteral != NULL && lliteral->handle()->IsSmi()) {
- VirtualFrame::RegisterAllocationScope scope(this);
- Load(node->right());
- if (frame_->KnownSmiAt(0)) overwrite_right = false;
- SmiOperation(node->op(),
- lliteral->handle(),
- true,
- overwrite_right ? OVERWRITE_RIGHT : NO_OVERWRITE);
- } else {
- GenerateInlineSmi inline_smi =
- loop_nesting() > 0 ? GENERATE_INLINE_SMI : DONT_GENERATE_INLINE_SMI;
- if (lliteral != NULL) {
- ASSERT(!lliteral->handle()->IsSmi());
- inline_smi = DONT_GENERATE_INLINE_SMI;
- }
- if (rliteral != NULL) {
- ASSERT(!rliteral->handle()->IsSmi());
- inline_smi = DONT_GENERATE_INLINE_SMI;
- }
- VirtualFrame::RegisterAllocationScope scope(this);
- OverwriteMode overwrite_mode = NO_OVERWRITE;
- if (overwrite_left) {
- overwrite_mode = OVERWRITE_LEFT;
- } else if (overwrite_right) {
- overwrite_mode = OVERWRITE_RIGHT;
- }
- Load(node->left());
- Load(node->right());
- GenericBinaryOperation(node->op(), overwrite_mode, inline_smi);
- }
- }
- ASSERT(!has_valid_frame() ||
- (has_cc() && frame_->height() == original_height) ||
- (!has_cc() && frame_->height() == original_height + 1));
-}
-
-
-void CodeGenerator::VisitThisFunction(ThisFunction* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- frame_->EmitPush(MemOperand(frame_->Function()));
- ASSERT_EQ(original_height + 1, frame_->height());
-}
-
-
-void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ CompareOperation");
-
- VirtualFrame::RegisterAllocationScope nonspilled_scope(this);
-
- // Get the expressions from the node.
- Expression* left = node->left();
- Expression* right = node->right();
- Token::Value op = node->op();
-
- // To make typeof testing for natives implemented in JavaScript really
- // efficient, we generate special code for expressions of the form:
- // 'typeof <expression> == <string>'.
- UnaryOperation* operation = left->AsUnaryOperation();
- if ((op == Token::EQ || op == Token::EQ_STRICT) &&
- (operation != NULL && operation->op() == Token::TYPEOF) &&
- (right->AsLiteral() != NULL &&
- right->AsLiteral()->handle()->IsString())) {
- Handle<String> check(String::cast(*right->AsLiteral()->handle()));
-
- // Load the operand, move it to a register.
- LoadTypeofExpression(operation->expression());
- Register tos = frame_->PopToRegister();
-
- Register scratch = VirtualFrame::scratch0();
-
- if (check->Equals(HEAP->number_symbol())) {
- __ tst(tos, Operand(kSmiTagMask));
- true_target()->Branch(eq);
- __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
- __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
- __ cmp(tos, ip);
- cc_reg_ = eq;
-
- } else if (check->Equals(HEAP->string_symbol())) {
- __ tst(tos, Operand(kSmiTagMask));
- false_target()->Branch(eq);
-
- __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
-
- // It can be an undetectable string object.
- __ ldrb(scratch, FieldMemOperand(tos, Map::kBitFieldOffset));
- __ and_(scratch, scratch, Operand(1 << Map::kIsUndetectable));
- __ cmp(scratch, Operand(1 << Map::kIsUndetectable));
- false_target()->Branch(eq);
-
- __ ldrb(scratch, FieldMemOperand(tos, Map::kInstanceTypeOffset));
- __ cmp(scratch, Operand(FIRST_NONSTRING_TYPE));
- cc_reg_ = lt;
-
- } else if (check->Equals(HEAP->boolean_symbol())) {
- __ LoadRoot(ip, Heap::kTrueValueRootIndex);
- __ cmp(tos, ip);
- true_target()->Branch(eq);
- __ LoadRoot(ip, Heap::kFalseValueRootIndex);
- __ cmp(tos, ip);
- cc_reg_ = eq;
-
- } else if (check->Equals(HEAP->undefined_symbol())) {
- __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
- __ cmp(tos, ip);
- true_target()->Branch(eq);
-
- __ tst(tos, Operand(kSmiTagMask));
- false_target()->Branch(eq);
-
- // It can be an undetectable object.
- __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
- __ ldrb(scratch, FieldMemOperand(tos, Map::kBitFieldOffset));
- __ and_(scratch, scratch, Operand(1 << Map::kIsUndetectable));
- __ cmp(scratch, Operand(1 << Map::kIsUndetectable));
-
- cc_reg_ = eq;
-
- } else if (check->Equals(HEAP->function_symbol())) {
- __ tst(tos, Operand(kSmiTagMask));
- false_target()->Branch(eq);
- Register map_reg = scratch;
- __ CompareObjectType(tos, map_reg, tos, JS_FUNCTION_TYPE);
- true_target()->Branch(eq);
- // Regular expressions are callable so typeof == 'function'.
- __ CompareInstanceType(map_reg, tos, JS_REGEXP_TYPE);
- cc_reg_ = eq;
-
- } else if (check->Equals(HEAP->object_symbol())) {
- __ tst(tos, Operand(kSmiTagMask));
- false_target()->Branch(eq);
-
- __ LoadRoot(ip, Heap::kNullValueRootIndex);
- __ cmp(tos, ip);
- true_target()->Branch(eq);
-
- Register map_reg = scratch;
- __ CompareObjectType(tos, map_reg, tos, JS_REGEXP_TYPE);
- false_target()->Branch(eq);
-
- // It can be an undetectable object.
- __ ldrb(tos, FieldMemOperand(map_reg, Map::kBitFieldOffset));
- __ and_(tos, tos, Operand(1 << Map::kIsUndetectable));
- __ cmp(tos, Operand(1 << Map::kIsUndetectable));
- false_target()->Branch(eq);
-
- __ ldrb(tos, FieldMemOperand(map_reg, Map::kInstanceTypeOffset));
- __ cmp(tos, Operand(FIRST_JS_OBJECT_TYPE));
- false_target()->Branch(lt);
- __ cmp(tos, Operand(LAST_JS_OBJECT_TYPE));
- cc_reg_ = le;
-
- } else {
- // Uncommon case: typeof testing against a string literal that is
- // never returned from the typeof operator.
- false_target()->Jump();
- }
- ASSERT(!has_valid_frame() ||
- (has_cc() && frame_->height() == original_height));
- return;
- }
-
- switch (op) {
- case Token::EQ:
- Comparison(eq, left, right, false);
- break;
-
- case Token::LT:
- Comparison(lt, left, right);
- break;
-
- case Token::GT:
- Comparison(gt, left, right);
- break;
-
- case Token::LTE:
- Comparison(le, left, right);
- break;
-
- case Token::GTE:
- Comparison(ge, left, right);
- break;
-
- case Token::EQ_STRICT:
- Comparison(eq, left, right, true);
- break;
-
- case Token::IN: {
- Load(left);
- Load(right);
- frame_->InvokeBuiltin(Builtins::IN, CALL_JS, 2);
- frame_->EmitPush(r0);
- break;
- }
-
- case Token::INSTANCEOF: {
- Load(left);
- Load(right);
- InstanceofStub stub(InstanceofStub::kNoFlags);
- frame_->CallStub(&stub, 2);
- // At this point if instanceof succeeded then r0 == 0.
- __ tst(r0, Operand(r0));
- cc_reg_ = eq;
- break;
- }
-
- default:
- UNREACHABLE();
- }
- ASSERT((has_cc() && frame_->height() == original_height) ||
- (!has_cc() && frame_->height() == original_height + 1));
-}
-
-
-void CodeGenerator::VisitCompareToNull(CompareToNull* node) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- Comment cmnt(masm_, "[ CompareToNull");
-
- Load(node->expression());
- Register tos = frame_->PopToRegister();
- __ LoadRoot(ip, Heap::kNullValueRootIndex);
- __ cmp(tos, ip);
-
- // The 'null' value is only equal to 'undefined' if using non-strict
- // comparisons.
- if (!node->is_strict()) {
- true_target()->Branch(eq);
- __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
- __ cmp(tos, Operand(ip));
- true_target()->Branch(eq);
-
- __ tst(tos, Operand(kSmiTagMask));
- false_target()->Branch(eq);
-
- // It can be an undetectable object.
- __ ldr(tos, FieldMemOperand(tos, HeapObject::kMapOffset));
- __ ldrb(tos, FieldMemOperand(tos, Map::kBitFieldOffset));
- __ and_(tos, tos, Operand(1 << Map::kIsUndetectable));
- __ cmp(tos, Operand(1 << Map::kIsUndetectable));
- }
-
- cc_reg_ = eq;
- ASSERT(has_cc() && frame_->height() == original_height);
-}
-
-
-class DeferredReferenceGetNamedValue: public DeferredCode {
- public:
- explicit DeferredReferenceGetNamedValue(Register receiver,
- Handle<String> name,
- bool is_contextual)
- : receiver_(receiver),
- name_(name),
- is_contextual_(is_contextual),
- is_dont_delete_(false) {
- set_comment(is_contextual
- ? "[ DeferredReferenceGetNamedValue (contextual)"
- : "[ DeferredReferenceGetNamedValue");
- }
-
- virtual void Generate();
-
- void set_is_dont_delete(bool value) {
- ASSERT(is_contextual_);
- is_dont_delete_ = value;
- }
-
- private:
- Register receiver_;
- Handle<String> name_;
- bool is_contextual_;
- bool is_dont_delete_;
-};
-
-
-// Convention for this is that on entry the receiver is in a register that
-// is not used by the stack. On exit the answer is found in that same
-// register and the stack has the same height.
-void DeferredReferenceGetNamedValue::Generate() {
-#ifdef DEBUG
- int expected_height = frame_state()->frame()->height();
-#endif
- VirtualFrame copied_frame(*frame_state()->frame());
- copied_frame.SpillAll();
-
- Register scratch1 = VirtualFrame::scratch0();
- Register scratch2 = VirtualFrame::scratch1();
- ASSERT(!receiver_.is(scratch1) && !receiver_.is(scratch2));
- __ DecrementCounter(masm_->isolate()->counters()->named_load_inline(),
- 1, scratch1, scratch2);
- __ IncrementCounter(masm_->isolate()->counters()->named_load_inline_miss(),
- 1, scratch1, scratch2);
-
- // Ensure receiver in r0 and name in r2 to match load ic calling convention.
- __ Move(r0, receiver_);
- __ mov(r2, Operand(name_));
-
- // The rest of the instructions in the deferred code must be together.
- { Assembler::BlockConstPoolScope block_const_pool(masm_);
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- Builtins::kLoadIC_Initialize));
- RelocInfo::Mode mode = is_contextual_
- ? RelocInfo::CODE_TARGET_CONTEXT
- : RelocInfo::CODE_TARGET;
- __ Call(ic, mode);
- // We must mark the code just after the call with the correct marker.
- MacroAssembler::NopMarkerTypes code_marker;
- if (is_contextual_) {
- code_marker = is_dont_delete_
- ? MacroAssembler::PROPERTY_ACCESS_INLINED_CONTEXT_DONT_DELETE
- : MacroAssembler::PROPERTY_ACCESS_INLINED_CONTEXT;
- } else {
- code_marker = MacroAssembler::PROPERTY_ACCESS_INLINED;
- }
- __ MarkCode(code_marker);
-
- // At this point the answer is in r0. We move it to the expected register
- // if necessary.
- __ Move(receiver_, r0);
-
- // Now go back to the frame that we entered with. This will not overwrite
- // the receiver register since that register was not in use when we came
- // in. The instructions emitted by this merge are skipped over by the
- // inline load patching mechanism when looking for the branch instruction
- // that tells it where the code to patch is.
- copied_frame.MergeTo(frame_state()->frame());
-
- // Block the constant pool for one more instruction after leaving this
- // constant pool block scope to include the branch instruction ending the
- // deferred code.
- __ BlockConstPoolFor(1);
- }
- ASSERT_EQ(expected_height, frame_state()->frame()->height());
-}
-
-
-class DeferredReferenceGetKeyedValue: public DeferredCode {
- public:
- DeferredReferenceGetKeyedValue(Register key, Register receiver)
- : key_(key), receiver_(receiver) {
- set_comment("[ DeferredReferenceGetKeyedValue");
- }
-
- virtual void Generate();
-
- private:
- Register key_;
- Register receiver_;
-};
-
-
-// Takes key and register in r0 and r1 or vice versa. Returns result
-// in r0.
-void DeferredReferenceGetKeyedValue::Generate() {
- ASSERT((key_.is(r0) && receiver_.is(r1)) ||
- (key_.is(r1) && receiver_.is(r0)));
-
- VirtualFrame copied_frame(*frame_state()->frame());
- copied_frame.SpillAll();
-
- Register scratch1 = VirtualFrame::scratch0();
- Register scratch2 = VirtualFrame::scratch1();
- __ DecrementCounter(masm_->isolate()->counters()->keyed_load_inline(),
- 1, scratch1, scratch2);
- __ IncrementCounter(masm_->isolate()->counters()->keyed_load_inline_miss(),
- 1, scratch1, scratch2);
-
- // Ensure key in r0 and receiver in r1 to match keyed load ic calling
- // convention.
- if (key_.is(r1)) {
- __ Swap(r0, r1, ip);
- }
-
- // The rest of the instructions in the deferred code must be together.
- { Assembler::BlockConstPoolScope block_const_pool(masm_);
- // Call keyed load IC. It has the arguments key and receiver in r0 and r1.
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- Builtins::kKeyedLoadIC_Initialize));
- __ Call(ic, RelocInfo::CODE_TARGET);
- // The call must be followed by a nop instruction to indicate that the
- // keyed load has been inlined.
- __ MarkCode(MacroAssembler::PROPERTY_ACCESS_INLINED);
-
- // Now go back to the frame that we entered with. This will not overwrite
- // the receiver or key registers since they were not in use when we came
- // in. The instructions emitted by this merge are skipped over by the
- // inline load patching mechanism when looking for the branch instruction
- // that tells it where the code to patch is.
- copied_frame.MergeTo(frame_state()->frame());
-
- // Block the constant pool for one more instruction after leaving this
- // constant pool block scope to include the branch instruction ending the
- // deferred code.
- __ BlockConstPoolFor(1);
- }
-}
-
-
-class DeferredReferenceSetKeyedValue: public DeferredCode {
- public:
- DeferredReferenceSetKeyedValue(Register value,
- Register key,
- Register receiver,
- StrictModeFlag strict_mode)
- : value_(value),
- key_(key),
- receiver_(receiver),
- strict_mode_(strict_mode) {
- set_comment("[ DeferredReferenceSetKeyedValue");
- }
-
- virtual void Generate();
-
- private:
- Register value_;
- Register key_;
- Register receiver_;
- StrictModeFlag strict_mode_;
-};
-
-
-void DeferredReferenceSetKeyedValue::Generate() {
- Register scratch1 = VirtualFrame::scratch0();
- Register scratch2 = VirtualFrame::scratch1();
- __ DecrementCounter(masm_->isolate()->counters()->keyed_store_inline(),
- 1, scratch1, scratch2);
- __ IncrementCounter(masm_->isolate()->counters()->keyed_store_inline_miss(),
- 1, scratch1, scratch2);
-
- // Ensure value in r0, key in r1 and receiver in r2 to match keyed store ic
- // calling convention.
- if (value_.is(r1)) {
- __ Swap(r0, r1, ip);
- }
- ASSERT(receiver_.is(r2));
-
- // The rest of the instructions in the deferred code must be together.
- { Assembler::BlockConstPoolScope block_const_pool(masm_);
- // Call keyed store IC. It has the arguments value, key and receiver in r0,
- // r1 and r2.
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- (strict_mode_ == kStrictMode)
- ? Builtins::kKeyedStoreIC_Initialize_Strict
- : Builtins::kKeyedStoreIC_Initialize));
- __ Call(ic, RelocInfo::CODE_TARGET);
- // The call must be followed by a nop instruction to indicate that the
- // keyed store has been inlined.
- __ MarkCode(MacroAssembler::PROPERTY_ACCESS_INLINED);
-
- // Block the constant pool for one more instruction after leaving this
- // constant pool block scope to include the branch instruction ending the
- // deferred code.
- __ BlockConstPoolFor(1);
- }
-}
-
-
-class DeferredReferenceSetNamedValue: public DeferredCode {
- public:
- DeferredReferenceSetNamedValue(Register value,
- Register receiver,
- Handle<String> name,
- StrictModeFlag strict_mode)
- : value_(value),
- receiver_(receiver),
- name_(name),
- strict_mode_(strict_mode) {
- set_comment("[ DeferredReferenceSetNamedValue");
- }
-
- virtual void Generate();
-
- private:
- Register value_;
- Register receiver_;
- Handle<String> name_;
- StrictModeFlag strict_mode_;
-};
-
-
-// Takes value in r0, receiver in r1 and returns the result (the
-// value) in r0.
-void DeferredReferenceSetNamedValue::Generate() {
- // Record the entry frame and spill.
- VirtualFrame copied_frame(*frame_state()->frame());
- copied_frame.SpillAll();
-
- // Ensure value in r0, receiver in r1 to match store ic calling
- // convention.
- ASSERT(value_.is(r0) && receiver_.is(r1));
- __ mov(r2, Operand(name_));
-
- // The rest of the instructions in the deferred code must be together.
- { Assembler::BlockConstPoolScope block_const_pool(masm_);
- // Call keyed store IC. It has the arguments value, key and receiver in r0,
- // r1 and r2.
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- (strict_mode_ == kStrictMode) ? Builtins::kStoreIC_Initialize_Strict
- : Builtins::kStoreIC_Initialize));
- __ Call(ic, RelocInfo::CODE_TARGET);
- // The call must be followed by a nop instruction to indicate that the
- // named store has been inlined.
- __ MarkCode(MacroAssembler::PROPERTY_ACCESS_INLINED);
-
- // Go back to the frame we entered with. The instructions
- // generated by this merge are skipped over by the inline store
- // patching mechanism when looking for the branch instruction that
- // tells it where the code to patch is.
- copied_frame.MergeTo(frame_state()->frame());
-
- // Block the constant pool for one more instruction after leaving this
- // constant pool block scope to include the branch instruction ending the
- // deferred code.
- __ BlockConstPoolFor(1);
- }
-}
-
-
-// Consumes the top of stack (the receiver) and pushes the result instead.
-void CodeGenerator::EmitNamedLoad(Handle<String> name, bool is_contextual) {
- bool contextual_load_in_builtin =
- is_contextual &&
- (ISOLATE->bootstrapper()->IsActive() ||
- (!info_->closure().is_null() && info_->closure()->IsBuiltin()));
-
- if (scope()->is_global_scope() ||
- loop_nesting() == 0 ||
- contextual_load_in_builtin) {
- Comment cmnt(masm(), "[ Load from named Property");
- // Setup the name register and call load IC.
- frame_->CallLoadIC(name,
- is_contextual
- ? RelocInfo::CODE_TARGET_CONTEXT
- : RelocInfo::CODE_TARGET);
- frame_->EmitPush(r0); // Push answer.
- } else {
- // Inline the in-object property case.
- Comment cmnt(masm(), is_contextual
- ? "[ Inlined contextual property load"
- : "[ Inlined named property load");
-
- // Counter will be decremented in the deferred code. Placed here to avoid
- // having it in the instruction stream below where patching will occur.
- if (is_contextual) {
- __ IncrementCounter(
- masm_->isolate()->counters()->named_load_global_inline(),
- 1, frame_->scratch0(), frame_->scratch1());
- } else {
- __ IncrementCounter(masm_->isolate()->counters()->named_load_inline(),
- 1, frame_->scratch0(), frame_->scratch1());
- }
-
- // The following instructions are the inlined load of an in-object property.
- // Parts of this code is patched, so the exact instructions generated needs
- // to be fixed. Therefore the instruction pool is blocked when generating
- // this code
-
- // Load the receiver from the stack.
- Register receiver = frame_->PopToRegister();
-
- DeferredReferenceGetNamedValue* deferred =
- new DeferredReferenceGetNamedValue(receiver, name, is_contextual);
-
- bool is_dont_delete = false;
- if (is_contextual) {
- if (!info_->closure().is_null()) {
- // When doing lazy compilation we can check if the global cell
- // already exists and use its "don't delete" status as a hint.
- AssertNoAllocation no_gc;
- v8::internal::GlobalObject* global_object =
- info_->closure()->context()->global();
- LookupResult lookup;
- global_object->LocalLookupRealNamedProperty(*name, &lookup);
- if (lookup.IsProperty() && lookup.type() == NORMAL) {
- ASSERT(lookup.holder() == global_object);
- ASSERT(global_object->property_dictionary()->ValueAt(
- lookup.GetDictionaryEntry())->IsJSGlobalPropertyCell());
- is_dont_delete = lookup.IsDontDelete();
- }
- }
- if (is_dont_delete) {
- __ IncrementCounter(
- masm_->isolate()->counters()->dont_delete_hint_hit(),
- 1, frame_->scratch0(), frame_->scratch1());
- }
- }
-
- { Assembler::BlockConstPoolScope block_const_pool(masm_);
- if (!is_contextual) {
- // Check that the receiver is a heap object.
- __ tst(receiver, Operand(kSmiTagMask));
- deferred->Branch(eq);
- }
-
- // Check for the_hole_value if necessary.
- // Below we rely on the number of instructions generated, and we can't
- // cope with the Check macro which does not generate a fixed number of
- // instructions.
- Label skip, check_the_hole, cont;
- if (FLAG_debug_code && is_contextual && is_dont_delete) {
- __ b(&skip);
- __ bind(&check_the_hole);
- __ Check(ne, "DontDelete cells can't contain the hole");
- __ b(&cont);
- __ bind(&skip);
- }
-
-#ifdef DEBUG
- int InlinedNamedLoadInstructions = 5;
- Label check_inlined_codesize;
- masm_->bind(&check_inlined_codesize);
-#endif
-
- Register scratch = VirtualFrame::scratch0();
- Register scratch2 = VirtualFrame::scratch1();
-
- // Check the map. The null map used below is patched by the inline cache
- // code. Therefore we can't use a LoadRoot call.
- __ ldr(scratch, FieldMemOperand(receiver, HeapObject::kMapOffset));
- __ mov(scratch2, Operand(FACTORY->null_value()));
- __ cmp(scratch, scratch2);
- deferred->Branch(ne);
-
- if (is_contextual) {
-#ifdef DEBUG
- InlinedNamedLoadInstructions += 1;
-#endif
- // Load the (initially invalid) cell and get its value.
- masm()->mov(receiver, Operand(FACTORY->null_value()));
- __ ldr(receiver,
- FieldMemOperand(receiver, JSGlobalPropertyCell::kValueOffset));
-
- deferred->set_is_dont_delete(is_dont_delete);
-
- if (!is_dont_delete) {
-#ifdef DEBUG
- InlinedNamedLoadInstructions += 3;
-#endif
- __ cmp(receiver, Operand(FACTORY->the_hole_value()));
- deferred->Branch(eq);
- } else if (FLAG_debug_code) {
-#ifdef DEBUG
- InlinedNamedLoadInstructions += 3;
-#endif
- __ cmp(receiver, Operand(FACTORY->the_hole_value()));
- __ b(&check_the_hole, eq);
- __ bind(&cont);
- }
- } else {
- // Initially use an invalid index. The index will be patched by the
- // inline cache code.
- __ ldr(receiver, MemOperand(receiver, 0));
- }
-
- // Make sure that the expected number of instructions are generated.
- // If the code before is updated, the offsets in ic-arm.cc
- // LoadIC::PatchInlinedContextualLoad and PatchInlinedLoad need
- // to be updated.
- ASSERT_EQ(InlinedNamedLoadInstructions,
- masm_->InstructionsGeneratedSince(&check_inlined_codesize));
- }
-
- deferred->BindExit();
- // At this point the receiver register has the result, either from the
- // deferred code or from the inlined code.
- frame_->EmitPush(receiver);
- }
-}
-
-
-void CodeGenerator::EmitNamedStore(Handle<String> name, bool is_contextual) {
-#ifdef DEBUG
- int expected_height = frame()->height() - (is_contextual ? 1 : 2);
-#endif
-
- Result result;
- if (is_contextual || scope()->is_global_scope() || loop_nesting() == 0) {
- frame()->CallStoreIC(name, is_contextual, strict_mode_flag());
- } else {
- // Inline the in-object property case.
- JumpTarget slow, done;
-
- // Get the value and receiver from the stack.
- frame()->PopToR0();
- Register value = r0;
- frame()->PopToR1();
- Register receiver = r1;
-
- DeferredReferenceSetNamedValue* deferred =
- new DeferredReferenceSetNamedValue(
- value, receiver, name, strict_mode_flag());
-
- // Check that the receiver is a heap object.
- __ tst(receiver, Operand(kSmiTagMask));
- deferred->Branch(eq);
-
- // The following instructions are the part of the inlined
- // in-object property store code which can be patched. Therefore
- // the exact number of instructions generated must be fixed, so
- // the constant pool is blocked while generating this code.
- { Assembler::BlockConstPoolScope block_const_pool(masm_);
- Register scratch0 = VirtualFrame::scratch0();
- Register scratch1 = VirtualFrame::scratch1();
-
- // Check the map. Initially use an invalid map to force a
- // failure. The map check will be patched in the runtime system.
- __ ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset));
-
-#ifdef DEBUG
- Label check_inlined_codesize;
- masm_->bind(&check_inlined_codesize);
-#endif
- __ mov(scratch0, Operand(FACTORY->null_value()));
- __ cmp(scratch0, scratch1);
- deferred->Branch(ne);
-
- int offset = 0;
- __ str(value, MemOperand(receiver, offset));
-
- // Update the write barrier and record its size. We do not use
- // the RecordWrite macro here because we want the offset
- // addition instruction first to make it easy to patch.
- Label record_write_start, record_write_done;
- __ bind(&record_write_start);
- // Add offset into the object.
- __ add(scratch0, receiver, Operand(offset));
- // Test that the object is not in the new space. We cannot set
- // region marks for new space pages.
- __ InNewSpace(receiver, scratch1, eq, &record_write_done);
- // Record the actual write.
- __ RecordWriteHelper(receiver, scratch0, scratch1);
- __ bind(&record_write_done);
- // Clobber all input registers when running with the debug-code flag
- // turned on to provoke errors.
- if (FLAG_debug_code) {
- __ mov(receiver, Operand(BitCast<int32_t>(kZapValue)));
- __ mov(scratch0, Operand(BitCast<int32_t>(kZapValue)));
- __ mov(scratch1, Operand(BitCast<int32_t>(kZapValue)));
- }
- // Check that this is the first inlined write barrier or that
- // this inlined write barrier has the same size as all the other
- // inlined write barriers.
- ASSERT((Isolate::Current()->inlined_write_barrier_size() == -1) ||
- (Isolate::Current()->inlined_write_barrier_size() ==
- masm()->InstructionsGeneratedSince(&record_write_start)));
- Isolate::Current()->set_inlined_write_barrier_size(
- masm()->InstructionsGeneratedSince(&record_write_start));
-
- // Make sure that the expected number of instructions are generated.
- ASSERT_EQ(GetInlinedNamedStoreInstructionsAfterPatch(),
- masm()->InstructionsGeneratedSince(&check_inlined_codesize));
- }
- deferred->BindExit();
- }
- ASSERT_EQ(expected_height, frame()->height());
-}
-
-
-void CodeGenerator::EmitKeyedLoad() {
- if (loop_nesting() == 0) {
- Comment cmnt(masm_, "[ Load from keyed property");
- frame_->CallKeyedLoadIC();
- } else {
- // Inline the keyed load.
- Comment cmnt(masm_, "[ Inlined load from keyed property");
-
- // Counter will be decremented in the deferred code. Placed here to avoid
- // having it in the instruction stream below where patching will occur.
- __ IncrementCounter(masm_->isolate()->counters()->keyed_load_inline(),
- 1, frame_->scratch0(), frame_->scratch1());
-
- // Load the key and receiver from the stack.
- bool key_is_known_smi = frame_->KnownSmiAt(0);
- Register key = frame_->PopToRegister();
- Register receiver = frame_->PopToRegister(key);
-
- // The deferred code expects key and receiver in registers.
- DeferredReferenceGetKeyedValue* deferred =
- new DeferredReferenceGetKeyedValue(key, receiver);
-
- // Check that the receiver is a heap object.
- __ tst(receiver, Operand(kSmiTagMask));
- deferred->Branch(eq);
-
- // The following instructions are the part of the inlined load keyed
- // property code which can be patched. Therefore the exact number of
- // instructions generated need to be fixed, so the constant pool is blocked
- // while generating this code.
- { Assembler::BlockConstPoolScope block_const_pool(masm_);
- Register scratch1 = VirtualFrame::scratch0();
- Register scratch2 = VirtualFrame::scratch1();
- // Check the map. The null map used below is patched by the inline cache
- // code.
- __ ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset));
-
- // Check that the key is a smi.
- if (!key_is_known_smi) {
- __ tst(key, Operand(kSmiTagMask));
- deferred->Branch(ne);
- }
-
-#ifdef DEBUG
- Label check_inlined_codesize;
- masm_->bind(&check_inlined_codesize);
-#endif
- __ mov(scratch2, Operand(FACTORY->null_value()));
- __ cmp(scratch1, scratch2);
- deferred->Branch(ne);
-
- // Get the elements array from the receiver.
- __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset));
- __ AssertFastElements(scratch1);
-
- // Check that key is within bounds. Use unsigned comparison to handle
- // negative keys.
- __ ldr(scratch2, FieldMemOperand(scratch1, FixedArray::kLengthOffset));
- __ cmp(scratch2, key);
- deferred->Branch(ls); // Unsigned less equal.
-
- // Load and check that the result is not the hole (key is a smi).
- __ LoadRoot(scratch2, Heap::kTheHoleValueRootIndex);
- __ add(scratch1,
- scratch1,
- Operand(FixedArray::kHeaderSize - kHeapObjectTag));
- __ ldr(scratch1,
- MemOperand(scratch1, key, LSL,
- kPointerSizeLog2 - (kSmiTagSize + kSmiShiftSize)));
- __ cmp(scratch1, scratch2);
- deferred->Branch(eq);
-
- __ mov(r0, scratch1);
- // Make sure that the expected number of instructions are generated.
- ASSERT_EQ(GetInlinedKeyedLoadInstructionsAfterPatch(),
- masm_->InstructionsGeneratedSince(&check_inlined_codesize));
- }
-
- deferred->BindExit();
- }
-}
-
-
-void CodeGenerator::EmitKeyedStore(StaticType* key_type,
- WriteBarrierCharacter wb_info) {
- // Generate inlined version of the keyed store if the code is in a loop
- // and the key is likely to be a smi.
- if (loop_nesting() > 0 && key_type->IsLikelySmi()) {
- // Inline the keyed store.
- Comment cmnt(masm_, "[ Inlined store to keyed property");
-
- Register scratch1 = VirtualFrame::scratch0();
- Register scratch2 = VirtualFrame::scratch1();
- Register scratch3 = r3;
-
- // Counter will be decremented in the deferred code. Placed here to avoid
- // having it in the instruction stream below where patching will occur.
- __ IncrementCounter(masm_->isolate()->counters()->keyed_store_inline(),
- 1, scratch1, scratch2);
-
-
- // Load the value, key and receiver from the stack.
- bool value_is_harmless = frame_->KnownSmiAt(0);
- if (wb_info == NEVER_NEWSPACE) value_is_harmless = true;
- bool key_is_smi = frame_->KnownSmiAt(1);
- Register value = frame_->PopToRegister();
- Register key = frame_->PopToRegister(value);
- VirtualFrame::SpilledScope spilled(frame_);
- Register receiver = r2;
- frame_->EmitPop(receiver);
-
-#ifdef DEBUG
- bool we_remembered_the_write_barrier = value_is_harmless;
-#endif
-
- // The deferred code expects value, key and receiver in registers.
- DeferredReferenceSetKeyedValue* deferred =
- new DeferredReferenceSetKeyedValue(
- value, key, receiver, strict_mode_flag());
-
- // Check that the value is a smi. As this inlined code does not set the
- // write barrier it is only possible to store smi values.
- if (!value_is_harmless) {
- // If the value is not likely to be a Smi then let's test the fixed array
- // for new space instead. See below.
- if (wb_info == LIKELY_SMI) {
- __ tst(value, Operand(kSmiTagMask));
- deferred->Branch(ne);
-#ifdef DEBUG
- we_remembered_the_write_barrier = true;
-#endif
- }
- }
-
- if (!key_is_smi) {
- // Check that the key is a smi.
- __ tst(key, Operand(kSmiTagMask));
- deferred->Branch(ne);
- }
-
- // Check that the receiver is a heap object.
- __ tst(receiver, Operand(kSmiTagMask));
- deferred->Branch(eq);
-
- // Check that the receiver is a JSArray.
- __ CompareObjectType(receiver, scratch1, scratch1, JS_ARRAY_TYPE);
- deferred->Branch(ne);
-
- // Get the elements array from the receiver.
- __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset));
- if (!value_is_harmless && wb_info != LIKELY_SMI) {
- Label ok;
- __ and_(scratch2,
- scratch1,
- Operand(ExternalReference::new_space_mask(isolate())));
- __ cmp(scratch2, Operand(ExternalReference::new_space_start(isolate())));
- __ tst(value, Operand(kSmiTagMask), ne);
- deferred->Branch(ne);
-#ifdef DEBUG
- we_remembered_the_write_barrier = true;
-#endif
- }
- // Check that the elements array is not a dictionary.
- __ ldr(scratch2, FieldMemOperand(scratch1, JSObject::kMapOffset));
-
- // The following instructions are the part of the inlined store keyed
- // property code which can be patched. Therefore the exact number of
- // instructions generated need to be fixed, so the constant pool is blocked
- // while generating this code.
- { Assembler::BlockConstPoolScope block_const_pool(masm_);
-#ifdef DEBUG
- Label check_inlined_codesize;
- masm_->bind(&check_inlined_codesize);
-#endif
-
- // Read the fixed array map from the constant pool (not from the root
- // array) so that the value can be patched. When debugging, we patch this
- // comparison to always fail so that we will hit the IC call in the
- // deferred code which will allow the debugger to break for fast case
- // stores.
- __ mov(scratch3, Operand(FACTORY->fixed_array_map()));
- __ cmp(scratch2, scratch3);
- deferred->Branch(ne);
-
- // Check that the key is within bounds. Both the key and the length of
- // the JSArray are smis (because the fixed array check above ensures the
- // elements are in fast case). Use unsigned comparison to handle negative
- // keys.
- __ ldr(scratch3, FieldMemOperand(receiver, JSArray::kLengthOffset));
- __ cmp(scratch3, key);
- deferred->Branch(ls); // Unsigned less equal.
-
- // Store the value.
- __ add(scratch1, scratch1,
- Operand(FixedArray::kHeaderSize - kHeapObjectTag));
- __ str(value,
- MemOperand(scratch1, key, LSL,
- kPointerSizeLog2 - (kSmiTagSize + kSmiShiftSize)));
-
- // Make sure that the expected number of instructions are generated.
- ASSERT_EQ(kInlinedKeyedStoreInstructionsAfterPatch,
- masm_->InstructionsGeneratedSince(&check_inlined_codesize));
- }
-
- ASSERT(we_remembered_the_write_barrier);
-
- deferred->BindExit();
- } else {
- frame()->CallKeyedStoreIC(strict_mode_flag());
- }
-}
-
-
-#ifdef DEBUG
-bool CodeGenerator::HasValidEntryRegisters() { return true; }
-#endif
-
-
-#undef __
-#define __ ACCESS_MASM(masm)
-
-Handle<String> Reference::GetName() {
- ASSERT(type_ == NAMED);
- Property* property = expression_->AsProperty();
- if (property == NULL) {
- // Global variable reference treated as a named property reference.
- VariableProxy* proxy = expression_->AsVariableProxy();
- ASSERT(proxy->AsVariable() != NULL);
- ASSERT(proxy->AsVariable()->is_global());
- return proxy->name();
- } else {
- Literal* raw_name = property->key()->AsLiteral();
- ASSERT(raw_name != NULL);
- return Handle<String>(String::cast(*raw_name->handle()));
- }
-}
-
-
-void Reference::DupIfPersist() {
- if (persist_after_get_) {
- switch (type_) {
- case KEYED:
- cgen_->frame()->Dup2();
- break;
- case NAMED:
- cgen_->frame()->Dup();
- // Fall through.
- case UNLOADED:
- case ILLEGAL:
- case SLOT:
- // Do nothing.
- ;
- }
- } else {
- set_unloaded();
- }
-}
-
-
-void Reference::GetValue() {
- ASSERT(cgen_->HasValidEntryRegisters());
- ASSERT(!is_illegal());
- ASSERT(!cgen_->has_cc());
- MacroAssembler* masm = cgen_->masm();
- Property* property = expression_->AsProperty();
- if (property != NULL) {
- cgen_->CodeForSourcePosition(property->position());
- }
-
- switch (type_) {
- case SLOT: {
- Comment cmnt(masm, "[ Load from Slot");
- Slot* slot = expression_->AsVariableProxy()->AsVariable()->AsSlot();
- ASSERT(slot != NULL);
- DupIfPersist();
- cgen_->LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
- break;
- }
-
- case NAMED: {
- Variable* var = expression_->AsVariableProxy()->AsVariable();
- bool is_global = var != NULL;
- ASSERT(!is_global || var->is_global());
- Handle<String> name = GetName();
- DupIfPersist();
- cgen_->EmitNamedLoad(name, is_global);
- break;
- }
-
- case KEYED: {
- ASSERT(property != NULL);
- DupIfPersist();
- cgen_->EmitKeyedLoad();
- cgen_->frame()->EmitPush(r0);
- break;
- }
-
- default:
- UNREACHABLE();
- }
-}
-
-
-void Reference::SetValue(InitState init_state, WriteBarrierCharacter wb_info) {
- ASSERT(!is_illegal());
- ASSERT(!cgen_->has_cc());
- MacroAssembler* masm = cgen_->masm();
- VirtualFrame* frame = cgen_->frame();
- Property* property = expression_->AsProperty();
- if (property != NULL) {
- cgen_->CodeForSourcePosition(property->position());
- }
-
- switch (type_) {
- case SLOT: {
- Comment cmnt(masm, "[ Store to Slot");
- Slot* slot = expression_->AsVariableProxy()->AsVariable()->AsSlot();
- cgen_->StoreToSlot(slot, init_state);
- set_unloaded();
- break;
- }
-
- case NAMED: {
- Comment cmnt(masm, "[ Store to named Property");
- cgen_->EmitNamedStore(GetName(), false);
- frame->EmitPush(r0);
- set_unloaded();
- break;
- }
-
- case KEYED: {
- Comment cmnt(masm, "[ Store to keyed Property");
- Property* property = expression_->AsProperty();
- ASSERT(property != NULL);
- cgen_->CodeForSourcePosition(property->position());
- cgen_->EmitKeyedStore(property->key()->type(), wb_info);
- frame->EmitPush(r0);
- set_unloaded();
- break;
- }
-
- default:
- UNREACHABLE();
- }
-}
-
-
-const char* GenericBinaryOpStub::GetName() {
- if (name_ != NULL) return name_;
- const int len = 100;
- name_ = Isolate::Current()->bootstrapper()->AllocateAutoDeletedArray(len);
- if (name_ == NULL) return "OOM";
- const char* op_name = Token::Name(op_);
- const char* overwrite_name;
- switch (mode_) {
- case NO_OVERWRITE: overwrite_name = "Alloc"; break;
- case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break;
- case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break;
- default: overwrite_name = "UnknownOverwrite"; break;
- }
-
- OS::SNPrintF(Vector<char>(name_, len),
- "GenericBinaryOpStub_%s_%s%s_%s",
- op_name,
- overwrite_name,
- specialized_on_rhs_ ? "_ConstantRhs" : "",
- BinaryOpIC::GetName(runtime_operands_type_));
- return name_;
-}
-
-#undef __
-
} } // namespace v8::internal
#endif // V8_TARGET_ARCH_ARM
diff --git a/src/arm/codegen-arm.h b/src/arm/codegen-arm.h
index 9b1f103..01aa805 100644
--- a/src/arm/codegen-arm.h
+++ b/src/arm/codegen-arm.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -37,162 +37,8 @@
// Forward declarations
class CompilationInfo;
-class DeferredCode;
-class JumpTarget;
-class RegisterAllocator;
-class RegisterFile;
-enum InitState { CONST_INIT, NOT_CONST_INIT };
enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
-enum GenerateInlineSmi { DONT_GENERATE_INLINE_SMI, GENERATE_INLINE_SMI };
-enum WriteBarrierCharacter { UNLIKELY_SMI, LIKELY_SMI, NEVER_NEWSPACE };
-
-
-// -------------------------------------------------------------------------
-// Reference support
-
-// A reference is a C++ stack-allocated object that puts a
-// reference on the virtual frame. The reference may be consumed
-// by GetValue, TakeValue, SetValue, and Codegen::UnloadReference.
-// When the lifetime (scope) of a valid reference ends, it must have
-// been consumed, and be in state UNLOADED.
-class Reference BASE_EMBEDDED {
- public:
- // The values of the types is important, see size().
- enum Type { UNLOADED = -2, ILLEGAL = -1, SLOT = 0, NAMED = 1, KEYED = 2 };
- Reference(CodeGenerator* cgen,
- Expression* expression,
- bool persist_after_get = false);
- ~Reference();
-
- Expression* expression() const { return expression_; }
- Type type() const { return type_; }
- void set_type(Type value) {
- ASSERT_EQ(ILLEGAL, type_);
- type_ = value;
- }
-
- void set_unloaded() {
- ASSERT_NE(ILLEGAL, type_);
- ASSERT_NE(UNLOADED, type_);
- type_ = UNLOADED;
- }
- // The size the reference takes up on the stack.
- int size() const {
- return (type_ < SLOT) ? 0 : type_;
- }
-
- bool is_illegal() const { return type_ == ILLEGAL; }
- bool is_slot() const { return type_ == SLOT; }
- bool is_property() const { return type_ == NAMED || type_ == KEYED; }
- bool is_unloaded() const { return type_ == UNLOADED; }
-
- // Return the name. Only valid for named property references.
- Handle<String> GetName();
-
- // Generate code to push the value of the reference on top of the
- // expression stack. The reference is expected to be already on top of
- // the expression stack, and it is consumed by the call unless the
- // reference is for a compound assignment.
- // If the reference is not consumed, it is left in place under its value.
- void GetValue();
-
- // Generate code to store the value on top of the expression stack in the
- // reference. The reference is expected to be immediately below the value
- // on the expression stack. The value is stored in the location specified
- // by the reference, and is left on top of the stack, after the reference
- // is popped from beneath it (unloaded).
- void SetValue(InitState init_state, WriteBarrierCharacter wb);
-
- // This is in preparation for something that uses the reference on the stack.
- // If we need this reference afterwards get then dup it now. Otherwise mark
- // it as used.
- inline void DupIfPersist();
-
- private:
- CodeGenerator* cgen_;
- Expression* expression_;
- Type type_;
- // Keep the reference on the stack after get, so it can be used by set later.
- bool persist_after_get_;
-};
-
-
-// -------------------------------------------------------------------------
-// Code generation state
-
-// The state is passed down the AST by the code generator (and back up, in
-// the form of the state of the label pair). It is threaded through the
-// call stack. Constructing a state implicitly pushes it on the owning code
-// generator's stack of states, and destroying one implicitly pops it.
-
-class CodeGenState BASE_EMBEDDED {
- public:
- // Create an initial code generator state. Destroying the initial state
- // leaves the code generator with a NULL state.
- explicit CodeGenState(CodeGenerator* owner);
-
- // Destroy a code generator state and restore the owning code generator's
- // previous state.
- virtual ~CodeGenState();
-
- virtual JumpTarget* true_target() const { return NULL; }
- virtual JumpTarget* false_target() const { return NULL; }
-
- protected:
- inline CodeGenerator* owner() { return owner_; }
- inline CodeGenState* previous() const { return previous_; }
-
- private:
- CodeGenerator* owner_;
- CodeGenState* previous_;
-};
-
-
-class ConditionCodeGenState : public CodeGenState {
- public:
- // Create a code generator state based on a code generator's current
- // state. The new state has its own pair of branch labels.
- ConditionCodeGenState(CodeGenerator* owner,
- JumpTarget* true_target,
- JumpTarget* false_target);
-
- virtual JumpTarget* true_target() const { return true_target_; }
- virtual JumpTarget* false_target() const { return false_target_; }
-
- private:
- JumpTarget* true_target_;
- JumpTarget* false_target_;
-};
-
-
-class TypeInfoCodeGenState : public CodeGenState {
- public:
- TypeInfoCodeGenState(CodeGenerator* owner,
- Slot* slot_number,
- TypeInfo info);
- ~TypeInfoCodeGenState();
-
- virtual JumpTarget* true_target() const { return previous()->true_target(); }
- virtual JumpTarget* false_target() const {
- return previous()->false_target();
- }
-
- private:
- Slot* slot_;
- TypeInfo old_type_info_;
-};
-
-
-// -------------------------------------------------------------------------
-// Arguments allocation mode
-
-enum ArgumentsAllocationMode {
- NO_ARGUMENTS_ALLOCATION,
- EAGER_ARGUMENTS_ALLOCATION,
- LAZY_ARGUMENTS_ALLOCATION
-};
-
// -------------------------------------------------------------------------
// CodeGenerator
@@ -225,45 +71,6 @@
int pos,
bool right_here = false);
- // Accessors
- MacroAssembler* masm() { return masm_; }
- VirtualFrame* frame() const { return frame_; }
- inline Handle<Script> script();
-
- bool has_valid_frame() const { return frame_ != NULL; }
-
- // Set the virtual frame to be new_frame, with non-frame register
- // reference counts given by non_frame_registers. The non-frame
- // register reference counts of the old frame are returned in
- // non_frame_registers.
- void SetFrame(VirtualFrame* new_frame, RegisterFile* non_frame_registers);
-
- void DeleteFrame();
-
- RegisterAllocator* allocator() const { return allocator_; }
-
- CodeGenState* state() { return state_; }
- void set_state(CodeGenState* state) { state_ = state; }
-
- TypeInfo type_info(Slot* slot) {
- int index = NumberOfSlot(slot);
- if (index == kInvalidSlotNumber) return TypeInfo::Unknown();
- return (*type_info_)[index];
- }
-
- TypeInfo set_type_info(Slot* slot, TypeInfo info) {
- int index = NumberOfSlot(slot);
- ASSERT(index >= kInvalidSlotNumber);
- if (index != kInvalidSlotNumber) {
- TypeInfo previous_value = (*type_info_)[index];
- (*type_info_)[index] = info;
- return previous_value;
- }
- return TypeInfo::Unknown();
- }
-
- void AddDeferred(DeferredCode* code) { deferred_.Add(code); }
-
// Constants related to patching of inlined load/store.
static int GetInlinedKeyedLoadInstructionsAfterPatch() {
return FLAG_debug_code ? 32 : 13;
@@ -275,317 +82,6 @@
}
private:
- // Type of a member function that generates inline code for a native function.
- typedef void (CodeGenerator::*InlineFunctionGenerator)
- (ZoneList<Expression*>*);
-
- static const InlineFunctionGenerator kInlineFunctionGenerators[];
-
- // Construction/Destruction
- explicit CodeGenerator(MacroAssembler* masm);
-
- // Accessors
- inline bool is_eval();
- inline Scope* scope();
- inline bool is_strict_mode();
- inline StrictModeFlag strict_mode_flag();
-
- // Generating deferred code.
- void ProcessDeferred();
-
- static const int kInvalidSlotNumber = -1;
-
- int NumberOfSlot(Slot* slot);
-
- // State
- bool has_cc() const { return cc_reg_ != al; }
- JumpTarget* true_target() const { return state_->true_target(); }
- JumpTarget* false_target() const { return state_->false_target(); }
-
- // Track loop nesting level.
- int loop_nesting() const { return loop_nesting_; }
- void IncrementLoopNesting() { loop_nesting_++; }
- void DecrementLoopNesting() { loop_nesting_--; }
-
- // Node visitors.
- void VisitStatements(ZoneList<Statement*>* statements);
-
- virtual void VisitSlot(Slot* node);
-#define DEF_VISIT(type) \
- virtual void Visit##type(type* node);
- AST_NODE_LIST(DEF_VISIT)
-#undef DEF_VISIT
-
- // Main code generation function
- void Generate(CompilationInfo* info);
-
- // Generate the return sequence code. Should be called no more than
- // once per compiled function, immediately after binding the return
- // target (which can not be done more than once). The return value should
- // be in r0.
- void GenerateReturnSequence();
-
- // Returns the arguments allocation mode.
- ArgumentsAllocationMode ArgumentsMode();
-
- // Store the arguments object and allocate it if necessary.
- void StoreArgumentsObject(bool initial);
-
- // The following are used by class Reference.
- void LoadReference(Reference* ref);
- void UnloadReference(Reference* ref);
-
- MemOperand SlotOperand(Slot* slot, Register tmp);
-
- MemOperand ContextSlotOperandCheckExtensions(Slot* slot,
- Register tmp,
- Register tmp2,
- JumpTarget* slow);
-
- // Expressions
- void LoadCondition(Expression* x,
- JumpTarget* true_target,
- JumpTarget* false_target,
- bool force_cc);
- void Load(Expression* expr);
- void LoadGlobal();
- void LoadGlobalReceiver(Register scratch);
-
- // Read a value from a slot and leave it on top of the expression stack.
- void LoadFromSlot(Slot* slot, TypeofState typeof_state);
- void LoadFromSlotCheckForArguments(Slot* slot, TypeofState state);
-
- // Store the value on top of the stack to a slot.
- void StoreToSlot(Slot* slot, InitState init_state);
-
- // Support for compiling assignment expressions.
- void EmitSlotAssignment(Assignment* node);
- void EmitNamedPropertyAssignment(Assignment* node);
- void EmitKeyedPropertyAssignment(Assignment* node);
-
- // Load a named property, returning it in r0. The receiver is passed on the
- // stack, and remains there.
- void EmitNamedLoad(Handle<String> name, bool is_contextual);
-
- // Store to a named property. If the store is contextual, value is passed on
- // the frame and consumed. Otherwise, receiver and value are passed on the
- // frame and consumed. The result is returned in r0.
- void EmitNamedStore(Handle<String> name, bool is_contextual);
-
- // Load a keyed property, leaving it in r0. The receiver and key are
- // passed on the stack, and remain there.
- void EmitKeyedLoad();
-
- // Store a keyed property. Key and receiver are on the stack and the value is
- // in r0. Result is returned in r0.
- void EmitKeyedStore(StaticType* key_type, WriteBarrierCharacter wb_info);
-
- void LoadFromGlobalSlotCheckExtensions(Slot* slot,
- TypeofState typeof_state,
- JumpTarget* slow);
-
- // Support for loading from local/global variables and arguments
- // whose location is known unless they are shadowed by
- // eval-introduced bindings. Generates no code for unsupported slot
- // types and therefore expects to fall through to the slow jump target.
- void EmitDynamicLoadFromSlotFastCase(Slot* slot,
- TypeofState typeof_state,
- JumpTarget* slow,
- JumpTarget* done);
-
- // Special code for typeof expressions: Unfortunately, we must
- // be careful when loading the expression in 'typeof'
- // expressions. We are not allowed to throw reference errors for
- // non-existing properties of the global object, so we must make it
- // look like an explicit property access, instead of an access
- // through the context chain.
- void LoadTypeofExpression(Expression* x);
-
- void ToBoolean(JumpTarget* true_target, JumpTarget* false_target);
-
- // Generate code that computes a shortcutting logical operation.
- void GenerateLogicalBooleanOperation(BinaryOperation* node);
-
- void GenericBinaryOperation(Token::Value op,
- OverwriteMode overwrite_mode,
- GenerateInlineSmi inline_smi,
- int known_rhs =
- GenericBinaryOpStub::kUnknownIntValue);
- void Comparison(Condition cc,
- Expression* left,
- Expression* right,
- bool strict = false);
-
- void SmiOperation(Token::Value op,
- Handle<Object> value,
- bool reversed,
- OverwriteMode mode);
-
- void CallWithArguments(ZoneList<Expression*>* arguments,
- CallFunctionFlags flags,
- int position);
-
- // An optimized implementation of expressions of the form
- // x.apply(y, arguments). We call x the applicand and y the receiver.
- // The optimization avoids allocating an arguments object if possible.
- void CallApplyLazy(Expression* applicand,
- Expression* receiver,
- VariableProxy* arguments,
- int position);
-
- // Control flow
- void Branch(bool if_true, JumpTarget* target);
- void CheckStack();
-
- bool CheckForInlineRuntimeCall(CallRuntime* node);
-
- static Handle<Code> ComputeLazyCompile(int argc);
- void ProcessDeclarations(ZoneList<Declaration*>* declarations);
-
- // Declare global variables and functions in the given array of
- // name/value pairs.
- void DeclareGlobals(Handle<FixedArray> pairs);
-
- // Instantiate the function based on the shared function info.
- void InstantiateFunction(Handle<SharedFunctionInfo> function_info,
- bool pretenure);
-
- // Support for type checks.
- void GenerateIsSmi(ZoneList<Expression*>* args);
- void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
- void GenerateIsArray(ZoneList<Expression*>* args);
- void GenerateIsRegExp(ZoneList<Expression*>* args);
- void GenerateIsObject(ZoneList<Expression*>* args);
- void GenerateIsSpecObject(ZoneList<Expression*>* args);
- void GenerateIsFunction(ZoneList<Expression*>* args);
- void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
- void GenerateIsStringWrapperSafeForDefaultValueOf(
- ZoneList<Expression*>* args);
-
- // Support for construct call checks.
- void GenerateIsConstructCall(ZoneList<Expression*>* args);
-
- // Support for arguments.length and arguments[?].
- void GenerateArgumentsLength(ZoneList<Expression*>* args);
- void GenerateArguments(ZoneList<Expression*>* args);
-
- // Support for accessing the class and value fields of an object.
- void GenerateClassOf(ZoneList<Expression*>* args);
- void GenerateValueOf(ZoneList<Expression*>* args);
- void GenerateSetValueOf(ZoneList<Expression*>* args);
-
- // Fast support for charCodeAt(n).
- void GenerateStringCharCodeAt(ZoneList<Expression*>* args);
-
- // Fast support for string.charAt(n) and string[n].
- void GenerateStringCharFromCode(ZoneList<Expression*>* args);
-
- // Fast support for string.charAt(n) and string[n].
- void GenerateStringCharAt(ZoneList<Expression*>* args);
-
- // Fast support for object equality testing.
- void GenerateObjectEquals(ZoneList<Expression*>* args);
-
- void GenerateLog(ZoneList<Expression*>* args);
-
- // Fast support for Math.random().
- void GenerateRandomHeapNumber(ZoneList<Expression*>* args);
-
- // Fast support for StringAdd.
- void GenerateStringAdd(ZoneList<Expression*>* args);
-
- // Fast support for SubString.
- void GenerateSubString(ZoneList<Expression*>* args);
-
- // Fast support for StringCompare.
- void GenerateStringCompare(ZoneList<Expression*>* args);
-
- // Support for direct calls from JavaScript to native RegExp code.
- void GenerateRegExpExec(ZoneList<Expression*>* args);
-
- void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
-
- // Support for fast native caches.
- void GenerateGetFromCache(ZoneList<Expression*>* args);
-
- // Fast support for number to string.
- void GenerateNumberToString(ZoneList<Expression*>* args);
-
- // Fast swapping of elements.
- void GenerateSwapElements(ZoneList<Expression*>* args);
-
- // Fast call for custom callbacks.
- void GenerateCallFunction(ZoneList<Expression*>* args);
-
- // Fast call to math functions.
- void GenerateMathPow(ZoneList<Expression*>* args);
- void GenerateMathSin(ZoneList<Expression*>* args);
- void GenerateMathCos(ZoneList<Expression*>* args);
- void GenerateMathSqrt(ZoneList<Expression*>* args);
- void GenerateMathLog(ZoneList<Expression*>* args);
-
- void GenerateIsRegExpEquivalent(ZoneList<Expression*>* args);
-
- void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args);
- void GenerateGetCachedArrayIndex(ZoneList<Expression*>* args);
- void GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args);
-
- // Simple condition analysis.
- enum ConditionAnalysis {
- ALWAYS_TRUE,
- ALWAYS_FALSE,
- DONT_KNOW
- };
- ConditionAnalysis AnalyzeCondition(Expression* cond);
-
- // Methods used to indicate which source code is generated for. Source
- // positions are collected by the assembler and emitted with the relocation
- // information.
- void CodeForFunctionPosition(FunctionLiteral* fun);
- void CodeForReturnPosition(FunctionLiteral* fun);
- void CodeForStatementPosition(Statement* node);
- void CodeForDoWhileConditionPosition(DoWhileStatement* stmt);
- void CodeForSourcePosition(int pos);
-
-#ifdef DEBUG
- // True if the registers are valid for entry to a block.
- bool HasValidEntryRegisters();
-#endif
-
- List<DeferredCode*> deferred_;
-
- // Assembler
- MacroAssembler* masm_; // to generate code
-
- CompilationInfo* info_;
-
- // Code generation state
- VirtualFrame* frame_;
- RegisterAllocator* allocator_;
- Condition cc_reg_;
- CodeGenState* state_;
- int loop_nesting_;
-
- Vector<TypeInfo>* type_info_;
-
- // Jump targets
- BreakTarget function_return_;
-
- // True if the function return is shadowed (ie, jumping to the target
- // function_return_ does not jump to the true function return, but rather
- // to some unlinking code).
- bool function_return_is_shadowed_;
-
- friend class VirtualFrame;
- friend class Isolate;
- friend class JumpTarget;
- friend class Reference;
- friend class FastCodeGenerator;
- friend class FullCodeGenerator;
- friend class FullCodeGenSyntaxChecker;
- friend class InlineRuntimeFunctionsTable;
- friend class LCodeGen;
-
DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
};
diff --git a/src/arm/constants-arm.h b/src/arm/constants-arm.h
index 0ac567c..823c6ff 100644
--- a/src/arm/constants-arm.h
+++ b/src/arm/constants-arm.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -28,12 +28,9 @@
#ifndef V8_ARM_CONSTANTS_ARM_H_
#define V8_ARM_CONSTANTS_ARM_H_
-// The simulator emulates the EABI so we define the USE_ARM_EABI macro if we
-// are not running on real ARM hardware. One reason for this is that the
-// old ABI uses fp registers in the calling convention and the simulator does
-// not simulate fp registers or coroutine instructions.
-#if defined(__ARM_EABI__) || !defined(__arm__)
-# define USE_ARM_EABI 1
+// ARM EABI is required.
+#if defined(__arm__) && !defined(__ARM_EABI__)
+#error ARM EABI support is required.
#endif
// This means that interwork-compatible jump instructions are generated. We
@@ -346,7 +343,9 @@
da_x = (0|0|0) << 21, // Decrement after.
ia_x = (0|4|0) << 21, // Increment after.
db_x = (8|0|0) << 21, // Decrement before.
- ib_x = (8|4|0) << 21 // Increment before.
+ ib_x = (8|4|0) << 21, // Increment before.
+
+ kBlockAddrModeMask = (8|4|1) << 21
};
diff --git a/src/arm/cpu-arm.cc b/src/arm/cpu-arm.cc
index 0f5bf56..51cfeb6 100644
--- a/src/arm/cpu-arm.cc
+++ b/src/arm/cpu-arm.cc
@@ -42,11 +42,12 @@
namespace internal {
void CPU::Setup() {
- CpuFeatures* cpu_features = Isolate::Current()->cpu_features();
- cpu_features->Probe(true);
- if (!cpu_features->IsSupported(VFP3) || Serializer::enabled()) {
- V8::DisableCrankshaft();
- }
+ CpuFeatures::Probe();
+}
+
+
+bool CPU::SupportsCrankshaft() {
+ return CpuFeatures::IsSupported(VFP3);
}
@@ -74,62 +75,33 @@
register uint32_t end asm("a2") =
reinterpret_cast<uint32_t>(start) + size;
register uint32_t flg asm("a3") = 0;
- #ifdef __ARM_EABI__
- #if defined (__arm__) && !defined(__thumb__)
- // __arm__ may be defined in thumb mode.
- register uint32_t scno asm("r7") = __ARM_NR_cacheflush;
- asm volatile(
- "svc 0x0"
- : "=r" (beg)
- : "0" (beg), "r" (end), "r" (flg), "r" (scno));
- #else
- // r7 is reserved by the EABI in thumb mode.
- asm volatile(
- "@ Enter ARM Mode \n\t"
- "adr r3, 1f \n\t"
- "bx r3 \n\t"
- ".ALIGN 4 \n\t"
- ".ARM \n"
- "1: push {r7} \n\t"
- "mov r7, %4 \n\t"
- "svc 0x0 \n\t"
- "pop {r7} \n\t"
- "@ Enter THUMB Mode\n\t"
- "adr r3, 2f+1 \n\t"
- "bx r3 \n\t"
- ".THUMB \n"
- "2: \n\t"
- : "=r" (beg)
- : "0" (beg), "r" (end), "r" (flg), "r" (__ARM_NR_cacheflush)
- : "r3");
- #endif
+ #if defined (__arm__) && !defined(__thumb__)
+ // __arm__ may be defined in thumb mode.
+ register uint32_t scno asm("r7") = __ARM_NR_cacheflush;
+ asm volatile(
+ "svc 0x0"
+ : "=r" (beg)
+ : "0" (beg), "r" (end), "r" (flg), "r" (scno));
#else
- #if defined (__arm__) && !defined(__thumb__)
- // __arm__ may be defined in thumb mode.
- asm volatile(
- "svc %1"
- : "=r" (beg)
- : "i" (__ARM_NR_cacheflush), "0" (beg), "r" (end), "r" (flg));
- #else
- // Do not use the value of __ARM_NR_cacheflush in the inline assembly
- // below, because the thumb mode value would be used, which would be
- // wrong, since we switch to ARM mode before executing the svc instruction
- asm volatile(
- "@ Enter ARM Mode \n\t"
- "adr r3, 1f \n\t"
- "bx r3 \n\t"
- ".ALIGN 4 \n\t"
- ".ARM \n"
- "1: svc 0x9f0002 \n"
- "@ Enter THUMB Mode\n\t"
- "adr r3, 2f+1 \n\t"
- "bx r3 \n\t"
- ".THUMB \n"
- "2: \n\t"
- : "=r" (beg)
- : "0" (beg), "r" (end), "r" (flg)
- : "r3");
- #endif
+ // r7 is reserved by the EABI in thumb mode.
+ asm volatile(
+ "@ Enter ARM Mode \n\t"
+ "adr r3, 1f \n\t"
+ "bx r3 \n\t"
+ ".ALIGN 4 \n\t"
+ ".ARM \n"
+ "1: push {r7} \n\t"
+ "mov r7, %4 \n\t"
+ "svc 0x0 \n\t"
+ "pop {r7} \n\t"
+ "@ Enter THUMB Mode\n\t"
+ "adr r3, 2f+1 \n\t"
+ "bx r3 \n\t"
+ ".THUMB \n"
+ "2: \n\t"
+ : "=r" (beg)
+ : "0" (beg), "r" (end), "r" (flg), "r" (__ARM_NR_cacheflush)
+ : "r3");
#endif
#endif
}
diff --git a/src/arm/debug-arm.cc b/src/arm/debug-arm.cc
index e6ad98c..07a2272 100644
--- a/src/arm/debug-arm.cc
+++ b/src/arm/debug-arm.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -29,7 +29,7 @@
#if defined(V8_TARGET_ARCH_ARM)
-#include "codegen-inl.h"
+#include "codegen.h"
#include "debug.h"
namespace v8 {
diff --git a/src/arm/deoptimizer-arm.cc b/src/arm/deoptimizer-arm.cc
index 3a3dcf0..f0a6937 100644
--- a/src/arm/deoptimizer-arm.cc
+++ b/src/arm/deoptimizer-arm.cc
@@ -586,14 +586,16 @@
// Allocate a new deoptimizer object.
// Pass four arguments in r0 to r3 and fifth argument on stack.
- __ PrepareCallCFunction(5, r5);
+ __ PrepareCallCFunction(6, r5);
__ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
__ mov(r1, Operand(type())); // bailout type,
// r2: bailout id already loaded.
// r3: code address or 0 already loaded.
__ str(r4, MemOperand(sp, 0 * kPointerSize)); // Fp-to-sp delta.
+ __ mov(r5, Operand(ExternalReference::isolate_address()));
+ __ str(r5, MemOperand(sp, 1 * kPointerSize)); // Isolate.
// Call Deoptimizer::New().
- __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate), 5);
+ __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate), 6);
// Preserve "deoptimizer" object in register r0 and get the input
// frame descriptor pointer to r1 (deoptimizer->input_);
diff --git a/src/arm/disasm-arm.cc b/src/arm/disasm-arm.cc
index 899b88a..a3775b5 100644
--- a/src/arm/disasm-arm.cc
+++ b/src/arm/disasm-arm.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -371,25 +371,34 @@
int Decoder::FormatVFPRegister(Instruction* instr, const char* format) {
ASSERT((format[0] == 'S') || (format[0] == 'D'));
+ VFPRegPrecision precision =
+ format[0] == 'D' ? kDoublePrecision : kSinglePrecision;
+
+ int retval = 2;
+ int reg = -1;
if (format[1] == 'n') {
- int reg = instr->VnValue();
- if (format[0] == 'S') PrintSRegister(((reg << 1) | instr->NValue()));
- if (format[0] == 'D') PrintDRegister(reg);
- return 2;
+ reg = instr->VFPNRegValue(precision);
} else if (format[1] == 'm') {
- int reg = instr->VmValue();
- if (format[0] == 'S') PrintSRegister(((reg << 1) | instr->MValue()));
- if (format[0] == 'D') PrintDRegister(reg);
- return 2;
+ reg = instr->VFPMRegValue(precision);
} else if (format[1] == 'd') {
- int reg = instr->VdValue();
- if (format[0] == 'S') PrintSRegister(((reg << 1) | instr->DValue()));
- if (format[0] == 'D') PrintDRegister(reg);
- return 2;
+ reg = instr->VFPDRegValue(precision);
+ if (format[2] == '+') {
+ int immed8 = instr->Immed8Value();
+ if (format[0] == 'S') reg += immed8 - 1;
+ if (format[0] == 'D') reg += (immed8 / 2 - 1);
+ }
+ if (format[2] == '+') retval = 3;
+ } else {
+ UNREACHABLE();
}
- UNREACHABLE();
- return -1;
+ if (precision == kSinglePrecision) {
+ PrintSRegister(reg);
+ } else {
+ PrintDRegister(reg);
+ }
+
+ return retval;
}
@@ -1273,9 +1282,22 @@
Format(instr, "vstr'cond 'Sd, ['rn + 4*'imm08@00]");
}
break;
+ case 0x4:
+ case 0x5:
+ case 0x6:
+ case 0x7:
+ case 0x9:
+ case 0xB: {
+ bool to_vfp_register = (instr->VLValue() == 0x1);
+ if (to_vfp_register) {
+ Format(instr, "vldm'cond'pu 'rn'w, {'Sd-'Sd+}");
+ } else {
+ Format(instr, "vstm'cond'pu 'rn'w, {'Sd-'Sd+}");
+ }
+ break;
+ }
default:
Unknown(instr); // Not used by V8.
- break;
}
} else if (instr->CoprocessorValue() == 0xB) {
switch (instr->OpcodeValue()) {
@@ -1303,9 +1325,19 @@
Format(instr, "vstr'cond 'Dd, ['rn + 4*'imm08@00]");
}
break;
+ case 0x4:
+ case 0x5:
+ case 0x9: {
+ bool to_vfp_register = (instr->VLValue() == 0x1);
+ if (to_vfp_register) {
+ Format(instr, "vldm'cond'pu 'rn'w, {'Dd-'Dd+}");
+ } else {
+ Format(instr, "vstm'cond'pu 'rn'w, {'Dd-'Dd+}");
+ }
+ break;
+ }
default:
Unknown(instr); // Not used by V8.
- break;
}
} else {
Unknown(instr); // Not used by V8.
diff --git a/src/arm/frames-arm.h b/src/arm/frames-arm.h
index 4aa8d6a..d6846c8 100644
--- a/src/arm/frames-arm.h
+++ b/src/arm/frames-arm.h
@@ -136,7 +136,7 @@
public:
// FP-relative.
static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset;
- static const int kSavedRegistersOffset = +2 * kPointerSize;
+ static const int kLastParameterOffset = +2 * kPointerSize;
static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset;
// Caller SP-relative.
diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc
index 088ba58..85e4262 100644
--- a/src/arm/full-codegen-arm.cc
+++ b/src/arm/full-codegen-arm.cc
@@ -30,7 +30,7 @@
#if defined(V8_TARGET_ARCH_ARM)
#include "code-stubs.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "compiler.h"
#include "debug.h"
#include "full-codegen.h"
@@ -245,7 +245,7 @@
}
{ Comment cmnt(masm_, "[ Stack check");
- PrepareForBailout(info->function(), NO_REGISTERS);
+ PrepareForBailoutForId(AstNode::kFunctionEntryId, NO_REGISTERS);
Label ok;
__ LoadRoot(ip, Heap::kStackLimitRootIndex);
__ cmp(sp, Operand(ip));
@@ -431,8 +431,7 @@
if (true_label_ != fall_through_) __ b(true_label_);
} else if (lit->IsString()) {
if (String::cast(*lit)->length() == 0) {
- if (false_label_ != fall_through_) __ b(false_label_);
- __ b(false_label_);
+ if (false_label_ != fall_through_) __ b(false_label_);
} else {
if (true_label_ != fall_through_) __ b(true_label_);
}
@@ -562,7 +561,7 @@
void FullCodeGenerator::DoTest(Label* if_true,
Label* if_false,
Label* fall_through) {
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
// Emit the inlined tests assumed by the stub.
__ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
@@ -824,7 +823,7 @@
// Compile all the tests with branches to their bodies.
for (int i = 0; i < clauses->length(); i++) {
CaseClause* clause = clauses->at(i);
- clause->body_target()->entry_label()->Unuse();
+ clause->body_target()->Unuse();
// The default is not a test, but remember it as final fall through.
if (clause->is_default()) {
@@ -851,7 +850,7 @@
__ cmp(r1, r0);
__ b(ne, &next_test);
__ Drop(1); // Switch value is no longer needed.
- __ b(clause->body_target()->entry_label());
+ __ b(clause->body_target());
__ bind(&slow_case);
}
@@ -862,7 +861,7 @@
__ cmp(r0, Operand(0));
__ b(ne, &next_test);
__ Drop(1); // Switch value is no longer needed.
- __ b(clause->body_target()->entry_label());
+ __ b(clause->body_target());
}
// Discard the test value and jump to the default if present, otherwise to
@@ -872,14 +871,14 @@
if (default_clause == NULL) {
__ b(nested_statement.break_target());
} else {
- __ b(default_clause->body_target()->entry_label());
+ __ b(default_clause->body_target());
}
// Compile all the case bodies.
for (int i = 0; i < clauses->length(); i++) {
Comment cmnt(masm_, "[ Case body");
CaseClause* clause = clauses->at(i);
- __ bind(clause->body_target()->entry_label());
+ __ bind(clause->body_target());
PrepareForBailoutForId(clause->EntryId(), NO_REGISTERS);
VisitStatements(clause->statements());
}
@@ -1622,27 +1621,26 @@
break;
}
+ // For compound assignments we need another deoptimization point after the
+ // variable/property load.
if (expr->is_compound()) {
{ AccumulatorValueContext context(this);
switch (assign_type) {
case VARIABLE:
EmitVariableLoad(expr->target()->AsVariableProxy()->var());
+ PrepareForBailout(expr->target(), TOS_REG);
break;
case NAMED_PROPERTY:
EmitNamedPropertyLoad(property);
+ PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
break;
case KEYED_PROPERTY:
EmitKeyedPropertyLoad(property);
+ PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
break;
}
}
- // For property compound assignments we need another deoptimization
- // point after the property load.
- if (property != NULL) {
- PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
- }
-
Token::Value op = expr->binary_op();
__ push(r0); // Left operand goes on the stack.
VisitForAccumulatorValue(expr->value());
@@ -2352,16 +2350,6 @@
}
}
} else {
- // Call to some other expression. If the expression is an anonymous
- // function literal not called in a loop, mark it as one that should
- // also use the fast code generator.
- FunctionLiteral* lit = fun->AsFunctionLiteral();
- if (lit != NULL &&
- lit->name()->Equals(isolate()->heap()->empty_string()) &&
- loop_depth() == 0) {
- lit->set_try_full_codegen(true);
- }
-
{ PreservePositionScope scope(masm()->positions_recorder());
VisitForStackValue(fun);
}
@@ -2543,11 +2531,75 @@
context()->PrepareTest(&materialize_true, &materialize_false,
&if_true, &if_false, &fall_through);
- // Just indicate false, as %_IsStringWrapperSafeForDefaultValueOf() is only
- // used in a few functions in runtime.js which should not normally be hit by
- // this compiler.
+ if (FLAG_debug_code) __ AbortIfSmi(r0);
+
+ __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ ldrb(ip, FieldMemOperand(r1, Map::kBitField2Offset));
+ __ tst(ip, Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
+ __ b(ne, if_true);
+
+ // Check for fast case object. Generate false result for slow case object.
+ __ ldr(r2, FieldMemOperand(r0, JSObject::kPropertiesOffset));
+ __ ldr(r2, FieldMemOperand(r2, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kHashTableMapRootIndex);
+ __ cmp(r2, ip);
+ __ b(eq, if_false);
+
+ // Look for valueOf symbol in the descriptor array, and indicate false if
+ // found. The type is not checked, so if it is a transition it is a false
+ // negative.
+ __ ldr(r4, FieldMemOperand(r1, Map::kInstanceDescriptorsOffset));
+ __ ldr(r3, FieldMemOperand(r4, FixedArray::kLengthOffset));
+ // r4: descriptor array
+ // r3: length of descriptor array
+ // Calculate the end of the descriptor array.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize == 1);
+ STATIC_ASSERT(kPointerSize == 4);
+ __ add(r2, r4, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ add(r2, r2, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize));
+
+ // Calculate location of the first key name.
+ __ add(r4,
+ r4,
+ Operand(FixedArray::kHeaderSize - kHeapObjectTag +
+ DescriptorArray::kFirstIndex * kPointerSize));
+ // Loop through all the keys in the descriptor array. If one of these is the
+ // symbol valueOf the result is false.
+ Label entry, loop;
+ // The use of ip to store the valueOf symbol asumes that it is not otherwise
+ // used in the loop below.
+ __ mov(ip, Operand(FACTORY->value_of_symbol()));
+ __ jmp(&entry);
+ __ bind(&loop);
+ __ ldr(r3, MemOperand(r4, 0));
+ __ cmp(r3, ip);
+ __ b(eq, if_false);
+ __ add(r4, r4, Operand(kPointerSize));
+ __ bind(&entry);
+ __ cmp(r4, Operand(r2));
+ __ b(ne, &loop);
+
+ // If a valueOf property is not found on the object check that it's
+ // prototype is the un-modified String prototype. If not result is false.
+ __ ldr(r2, FieldMemOperand(r1, Map::kPrototypeOffset));
+ __ tst(r2, Operand(kSmiTagMask));
+ __ b(eq, if_false);
+ __ ldr(r2, FieldMemOperand(r2, HeapObject::kMapOffset));
+ __ ldr(r3, ContextOperand(cp, Context::GLOBAL_INDEX));
+ __ ldr(r3, FieldMemOperand(r3, GlobalObject::kGlobalContextOffset));
+ __ ldr(r3, ContextOperand(r3, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX));
+ __ cmp(r2, r3);
+ __ b(ne, if_false);
+
+ // Set the bit in the map to indicate that it has been checked safe for
+ // default valueOf and set true result.
+ __ ldrb(r2, FieldMemOperand(r1, Map::kBitField2Offset));
+ __ orr(r2, r2, Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
+ __ strb(r2, FieldMemOperand(r1, Map::kBitField2Offset));
+ __ jmp(if_true);
+
PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
- __ jmp(if_false);
context()->Plug(if_true, if_false);
}
@@ -2802,9 +2854,10 @@
// Convert 32 random bits in r0 to 0.(32 random bits) in a double
// by computing:
// ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
- if (isolate()->cpu_features()->IsSupported(VFP3)) {
- __ PrepareCallCFunction(0, r1);
- __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 0);
+ if (CpuFeatures::IsSupported(VFP3)) {
+ __ PrepareCallCFunction(1, r0);
+ __ mov(r0, Operand(ExternalReference::isolate_address()));
+ __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1);
CpuFeatures::Scope scope(VFP3);
// 0x41300000 is the top half of 1.0 x 2^20 as a double.
@@ -2822,10 +2875,11 @@
__ vstr(d7, r0, HeapNumber::kValueOffset);
__ mov(r0, r4);
} else {
+ __ PrepareCallCFunction(2, r0);
__ mov(r0, Operand(r4));
- __ PrepareCallCFunction(1, r1);
+ __ mov(r1, Operand(ExternalReference::isolate_address()));
__ CallCFunction(
- ExternalReference::fill_heap_number_with_random_function(isolate()), 1);
+ ExternalReference::fill_heap_number_with_random_function(isolate()), 2);
}
context()->Plug(r0);
@@ -3107,15 +3161,14 @@
void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) {
ASSERT(args->length() >= 2);
- int arg_count = args->length() - 2; // For receiver and function.
- VisitForStackValue(args->at(0)); // Receiver.
- for (int i = 0; i < arg_count; i++) {
- VisitForStackValue(args->at(i + 1));
+ int arg_count = args->length() - 2; // 2 ~ receiver and function.
+ for (int i = 0; i < arg_count + 1; i++) {
+ VisitForStackValue(args->at(i));
}
- VisitForAccumulatorValue(args->at(arg_count + 1)); // Function.
+ VisitForAccumulatorValue(args->last()); // Function.
- // InvokeFunction requires function in r1. Move it in there.
- if (!result_register().is(r1)) __ mov(r1, result_register());
+ // InvokeFunction requires the function in r1. Move it in there.
+ __ mov(r1, result_register());
ParameterCount count(arg_count);
__ InvokeFunction(r1, count, CALL_FUNCTION);
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
@@ -3827,7 +3880,11 @@
// We need a second deoptimization point after loading the value
// in case evaluating the property load my have a side effect.
- PrepareForBailout(expr->increment(), TOS_REG);
+ if (assign_type == VARIABLE) {
+ PrepareForBailout(expr->expression(), TOS_REG);
+ } else {
+ PrepareForBailoutForId(expr->CountId(), TOS_REG);
+ }
// Call ToNumber only if operand is not a smi.
Label no_conversion;
@@ -4237,7 +4294,6 @@
default:
break;
}
-
__ Call(ic, mode);
}
@@ -4259,7 +4315,6 @@
default:
break;
}
-
__ Call(ic, RelocInfo::CODE_TARGET);
if (patch_site != NULL && patch_site->is_bound()) {
patch_site->EmitPatchInfo();
diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc
index dc4f761..db04f33 100644
--- a/src/arm/ic-arm.cc
+++ b/src/arm/ic-arm.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -31,7 +31,7 @@
#include "assembler-arm.h"
#include "code-stubs.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "disasm.h"
#include "ic-inl.h"
#include "runtime.h"
@@ -926,217 +926,6 @@
__ TailCallExternalReference(ref, 2, 1);
}
-// Returns the code marker, or the 0 if the code is not marked.
-static inline int InlinedICSiteMarker(Address address,
- Address* inline_end_address) {
- if (V8::UseCrankshaft()) return false;
-
- // If the instruction after the call site is not the pseudo instruction nop1
- // then this is not related to an inlined in-object property load. The nop1
- // instruction is located just after the call to the IC in the deferred code
- // handling the miss in the inlined code. After the nop1 instruction there is
- // a branch instruction for jumping back from the deferred code.
- Address address_after_call = address + Assembler::kCallTargetAddressOffset;
- Instr instr_after_call = Assembler::instr_at(address_after_call);
- int code_marker = MacroAssembler::GetCodeMarker(instr_after_call);
-
- // A negative result means the code is not marked.
- if (code_marker <= 0) return 0;
-
- Address address_after_nop = address_after_call + Assembler::kInstrSize;
- Instr instr_after_nop = Assembler::instr_at(address_after_nop);
- // There may be some reg-reg move and frame merging code to skip over before
- // the branch back from the DeferredReferenceGetKeyedValue code to the inlined
- // code.
- while (!Assembler::IsBranch(instr_after_nop)) {
- address_after_nop += Assembler::kInstrSize;
- instr_after_nop = Assembler::instr_at(address_after_nop);
- }
-
- // Find the end of the inlined code for handling the load.
- int b_offset =
- Assembler::GetBranchOffset(instr_after_nop) + Assembler::kPcLoadDelta;
- ASSERT(b_offset < 0); // Jumping back from deferred code.
- *inline_end_address = address_after_nop + b_offset;
-
- return code_marker;
-}
-
-
-bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) {
- if (V8::UseCrankshaft()) return false;
-
- // Find the end of the inlined code for handling the load if this is an
- // inlined IC call site.
- Address inline_end_address = 0;
- if (InlinedICSiteMarker(address, &inline_end_address)
- != Assembler::PROPERTY_ACCESS_INLINED) {
- return false;
- }
-
- // Patch the offset of the property load instruction (ldr r0, [r1, #+XXX]).
- // The immediate must be representable in 12 bits.
- ASSERT((JSObject::kMaxInstanceSize - JSObject::kHeaderSize) < (1 << 12));
- Address ldr_property_instr_address =
- inline_end_address - Assembler::kInstrSize;
- ASSERT(Assembler::IsLdrRegisterImmediate(
- Assembler::instr_at(ldr_property_instr_address)));
- Instr ldr_property_instr = Assembler::instr_at(ldr_property_instr_address);
- ldr_property_instr = Assembler::SetLdrRegisterImmediateOffset(
- ldr_property_instr, offset - kHeapObjectTag);
- Assembler::instr_at_put(ldr_property_instr_address, ldr_property_instr);
-
- // Indicate that code has changed.
- CPU::FlushICache(ldr_property_instr_address, 1 * Assembler::kInstrSize);
-
- // Patch the map check.
- // For PROPERTY_ACCESS_INLINED, the load map instruction is generated
- // 4 instructions before the end of the inlined code.
- // See codgen-arm.cc CodeGenerator::EmitNamedLoad.
- int ldr_map_offset = -4;
- Address ldr_map_instr_address =
- inline_end_address + ldr_map_offset * Assembler::kInstrSize;
- Assembler::set_target_address_at(ldr_map_instr_address,
- reinterpret_cast<Address>(map));
- return true;
-}
-
-
-bool LoadIC::PatchInlinedContextualLoad(Address address,
- Object* map,
- Object* cell,
- bool is_dont_delete) {
- // Find the end of the inlined code for handling the contextual load if
- // this is inlined IC call site.
- Address inline_end_address = 0;
- int marker = InlinedICSiteMarker(address, &inline_end_address);
- if (!((marker == Assembler::PROPERTY_ACCESS_INLINED_CONTEXT) ||
- (marker == Assembler::PROPERTY_ACCESS_INLINED_CONTEXT_DONT_DELETE))) {
- return false;
- }
- // On ARM we don't rely on the is_dont_delete argument as the hint is already
- // embedded in the code marker.
- bool marker_is_dont_delete =
- marker == Assembler::PROPERTY_ACCESS_INLINED_CONTEXT_DONT_DELETE;
-
- // These are the offsets from the end of the inlined code.
- // See codgen-arm.cc CodeGenerator::EmitNamedLoad.
- int ldr_map_offset = marker_is_dont_delete ? -5: -8;
- int ldr_cell_offset = marker_is_dont_delete ? -2: -5;
- if (FLAG_debug_code && marker_is_dont_delete) {
- // Three extra instructions were generated to check for the_hole_value.
- ldr_map_offset -= 3;
- ldr_cell_offset -= 3;
- }
- Address ldr_map_instr_address =
- inline_end_address + ldr_map_offset * Assembler::kInstrSize;
- Address ldr_cell_instr_address =
- inline_end_address + ldr_cell_offset * Assembler::kInstrSize;
-
- // Patch the map check.
- Assembler::set_target_address_at(ldr_map_instr_address,
- reinterpret_cast<Address>(map));
- // Patch the cell address.
- Assembler::set_target_address_at(ldr_cell_instr_address,
- reinterpret_cast<Address>(cell));
-
- return true;
-}
-
-
-bool StoreIC::PatchInlinedStore(Address address, Object* map, int offset) {
- if (V8::UseCrankshaft()) return false;
-
- // Find the end of the inlined code for the store if there is an
- // inlined version of the store.
- Address inline_end_address = 0;
- if (InlinedICSiteMarker(address, &inline_end_address)
- != Assembler::PROPERTY_ACCESS_INLINED) {
- return false;
- }
-
- // Compute the address of the map load instruction.
- Address ldr_map_instr_address =
- inline_end_address -
- (CodeGenerator::GetInlinedNamedStoreInstructionsAfterPatch() *
- Assembler::kInstrSize);
-
- // Update the offsets if initializing the inlined store. No reason
- // to update the offsets when clearing the inlined version because
- // it will bail out in the map check.
- if (map != HEAP->null_value()) {
- // Patch the offset in the actual store instruction.
- Address str_property_instr_address =
- ldr_map_instr_address + 3 * Assembler::kInstrSize;
- Instr str_property_instr = Assembler::instr_at(str_property_instr_address);
- ASSERT(Assembler::IsStrRegisterImmediate(str_property_instr));
- str_property_instr = Assembler::SetStrRegisterImmediateOffset(
- str_property_instr, offset - kHeapObjectTag);
- Assembler::instr_at_put(str_property_instr_address, str_property_instr);
-
- // Patch the offset in the add instruction that is part of the
- // write barrier.
- Address add_offset_instr_address =
- str_property_instr_address + Assembler::kInstrSize;
- Instr add_offset_instr = Assembler::instr_at(add_offset_instr_address);
- ASSERT(Assembler::IsAddRegisterImmediate(add_offset_instr));
- add_offset_instr = Assembler::SetAddRegisterImmediateOffset(
- add_offset_instr, offset - kHeapObjectTag);
- Assembler::instr_at_put(add_offset_instr_address, add_offset_instr);
-
- // Indicate that code has changed.
- CPU::FlushICache(str_property_instr_address, 2 * Assembler::kInstrSize);
- }
-
- // Patch the map check.
- Assembler::set_target_address_at(ldr_map_instr_address,
- reinterpret_cast<Address>(map));
-
- return true;
-}
-
-
-bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) {
- if (V8::UseCrankshaft()) return false;
-
- Address inline_end_address = 0;
- if (InlinedICSiteMarker(address, &inline_end_address)
- != Assembler::PROPERTY_ACCESS_INLINED) {
- return false;
- }
-
- // Patch the map check.
- Address ldr_map_instr_address =
- inline_end_address -
- (CodeGenerator::GetInlinedKeyedLoadInstructionsAfterPatch() *
- Assembler::kInstrSize);
- Assembler::set_target_address_at(ldr_map_instr_address,
- reinterpret_cast<Address>(map));
- return true;
-}
-
-
-bool KeyedStoreIC::PatchInlinedStore(Address address, Object* map) {
- if (V8::UseCrankshaft()) return false;
-
- // Find the end of the inlined code for handling the store if this is an
- // inlined IC call site.
- Address inline_end_address = 0;
- if (InlinedICSiteMarker(address, &inline_end_address)
- != Assembler::PROPERTY_ACCESS_INLINED) {
- return false;
- }
-
- // Patch the map check.
- Address ldr_map_instr_address =
- inline_end_address -
- (CodeGenerator::kInlinedKeyedStoreInstructionsAfterPatch *
- Assembler::kInstrSize);
- Assembler::set_target_address_at(ldr_map_instr_address,
- reinterpret_cast<Address>(map));
- return true;
-}
-
Object* KeyedLoadIC_Miss(Arguments args);
diff --git a/src/arm/jump-target-arm.cc b/src/arm/jump-target-arm.cc
deleted file mode 100644
index df370c4..0000000
--- a/src/arm/jump-target-arm.cc
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "v8.h"
-
-#if defined(V8_TARGET_ARCH_ARM)
-
-#include "codegen-inl.h"
-#include "jump-target-inl.h"
-#include "register-allocator-inl.h"
-#include "virtual-frame-inl.h"
-
-namespace v8 {
-namespace internal {
-
-// -------------------------------------------------------------------------
-// JumpTarget implementation.
-
-#define __ ACCESS_MASM(cgen()->masm())
-
-void JumpTarget::DoJump() {
- ASSERT(cgen()->has_valid_frame());
- // Live non-frame registers are not allowed at unconditional jumps
- // because we have no way of invalidating the corresponding results
- // which are still live in the C++ code.
- ASSERT(cgen()->HasValidEntryRegisters());
-
- if (entry_frame_set_) {
- if (entry_label_.is_bound()) {
- // If we already bound and generated code at the destination then it
- // is too late to ask for less optimistic type assumptions.
- ASSERT(entry_frame_.IsCompatibleWith(cgen()->frame()));
- }
- // There already a frame expectation at the target.
- cgen()->frame()->MergeTo(&entry_frame_);
- cgen()->DeleteFrame();
- } else {
- // Clone the current frame to use as the expected one at the target.
- set_entry_frame(cgen()->frame());
- // Zap the fall-through frame since the jump was unconditional.
- RegisterFile empty;
- cgen()->SetFrame(NULL, &empty);
- }
- if (entry_label_.is_bound()) {
- // You can't jump backwards to an already bound label unless you admitted
- // up front that this was a bidirectional jump target. Bidirectional jump
- // targets will zap their type info when bound in case some later virtual
- // frame with less precise type info branches to them.
- ASSERT(direction_ != FORWARD_ONLY);
- }
- __ jmp(&entry_label_);
-}
-
-
-void JumpTarget::DoBranch(Condition cond, Hint ignored) {
- ASSERT(cgen()->has_valid_frame());
-
- if (entry_frame_set_) {
- if (entry_label_.is_bound()) {
- // If we already bound and generated code at the destination then it
- // is too late to ask for less optimistic type assumptions.
- ASSERT(entry_frame_.IsCompatibleWith(cgen()->frame()));
- }
- // We have an expected frame to merge to on the backward edge.
- cgen()->frame()->MergeTo(&entry_frame_, cond);
- } else {
- // Clone the current frame to use as the expected one at the target.
- set_entry_frame(cgen()->frame());
- }
- if (entry_label_.is_bound()) {
- // You can't branch backwards to an already bound label unless you admitted
- // up front that this was a bidirectional jump target. Bidirectional jump
- // targets will zap their type info when bound in case some later virtual
- // frame with less precise type info branches to them.
- ASSERT(direction_ != FORWARD_ONLY);
- }
- __ b(cond, &entry_label_);
- if (cond == al) {
- cgen()->DeleteFrame();
- }
-}
-
-
-void JumpTarget::Call() {
- // Call is used to push the address of the catch block on the stack as
- // a return address when compiling try/catch and try/finally. We
- // fully spill the frame before making the call. The expected frame
- // at the label (which should be the only one) is the spilled current
- // frame plus an in-memory return address. The "fall-through" frame
- // at the return site is the spilled current frame.
- ASSERT(cgen()->has_valid_frame());
- // There are no non-frame references across the call.
- ASSERT(cgen()->HasValidEntryRegisters());
- ASSERT(!is_linked());
-
- // Calls are always 'forward' so we use a copy of the current frame (plus
- // one for a return address) as the expected frame.
- ASSERT(!entry_frame_set_);
- VirtualFrame target_frame = *cgen()->frame();
- target_frame.Adjust(1);
- set_entry_frame(&target_frame);
-
- __ bl(&entry_label_);
-}
-
-
-void JumpTarget::DoBind() {
- ASSERT(!is_bound());
-
- // Live non-frame registers are not allowed at the start of a basic
- // block.
- ASSERT(!cgen()->has_valid_frame() || cgen()->HasValidEntryRegisters());
-
- if (cgen()->has_valid_frame()) {
- if (direction_ != FORWARD_ONLY) cgen()->frame()->ForgetTypeInfo();
- // If there is a current frame we can use it on the fall through.
- if (!entry_frame_set_) {
- entry_frame_ = *cgen()->frame();
- entry_frame_set_ = true;
- } else {
- cgen()->frame()->MergeTo(&entry_frame_);
- // On fall through we may have to merge both ways.
- if (direction_ != FORWARD_ONLY) {
- // This will not need to adjust the virtual frame entries that are
- // register allocated since that was done above and they now match.
- // But it does need to adjust the entry_frame_ of this jump target
- // to make it potentially less optimistic. Later code can branch back
- // to this jump target and we need to assert that that code does not
- // have weaker assumptions about types.
- entry_frame_.MergeTo(cgen()->frame());
- }
- }
- } else {
- // If there is no current frame we must have an entry frame which we can
- // copy.
- ASSERT(entry_frame_set_);
- RegisterFile empty;
- cgen()->SetFrame(new VirtualFrame(&entry_frame_), &empty);
- }
-
- __ bind(&entry_label_);
-}
-
-
-#undef __
-
-
-} } // namespace v8::internal
-
-#endif // V8_TARGET_ARCH_ARM
diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc
index 5d31473..faf6404 100644
--- a/src/arm/lithium-arm.cc
+++ b/src/arm/lithium-arm.cc
@@ -61,22 +61,21 @@
#ifdef DEBUG
void LInstruction::VerifyCall() {
- // Call instructions can use only fixed registers as
- // temporaries and outputs because all registers
- // are blocked by the calling convention.
- // Inputs must use a fixed register.
+ // Call instructions can use only fixed registers as temporaries and
+ // outputs because all registers are blocked by the calling convention.
+ // Inputs operands must use a fixed register or use-at-start policy or
+ // a non-register policy.
ASSERT(Output() == NULL ||
LUnallocated::cast(Output())->HasFixedPolicy() ||
!LUnallocated::cast(Output())->HasRegisterPolicy());
for (UseIterator it(this); it.HasNext(); it.Advance()) {
- LOperand* operand = it.Next();
- ASSERT(LUnallocated::cast(operand)->HasFixedPolicy() ||
- !LUnallocated::cast(operand)->HasRegisterPolicy());
+ LUnallocated* operand = LUnallocated::cast(it.Next());
+ ASSERT(operand->HasFixedPolicy() ||
+ operand->IsUsedAtStart());
}
for (TempIterator it(this); it.HasNext(); it.Advance()) {
- LOperand* operand = it.Next();
- ASSERT(LUnallocated::cast(operand)->HasFixedPolicy() ||
- !LUnallocated::cast(operand)->HasRegisterPolicy());
+ LUnallocated* operand = LUnallocated::cast(it.Next());
+ ASSERT(operand->HasFixedPolicy() ||!operand->HasRegisterPolicy());
}
}
#endif
@@ -301,6 +300,13 @@
}
+void LInvokeFunction::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ InputAt(0)->PrintTo(stream);
+ stream->Add(" #%d / ", arity());
+}
+
+
void LCallKeyed::PrintDataTo(StringStream* stream) {
stream->Add("[r2] #%d / ", arity());
}
@@ -1114,9 +1120,9 @@
return new LIsConstructCallAndBranch(TempRegister());
} else {
if (v->IsConstant()) {
- if (HConstant::cast(v)->handle()->IsTrue()) {
+ if (HConstant::cast(v)->ToBoolean()) {
return new LGoto(instr->FirstSuccessor()->block_id());
- } else if (HConstant::cast(v)->handle()->IsFalse()) {
+ } else {
return new LGoto(instr->SecondSuccessor()->block_id());
}
}
@@ -1212,6 +1218,14 @@
}
+LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
+ LOperand* function = UseFixed(instr->function(), r1);
+ argument_count_ -= instr->argument_count();
+ LInvokeFunction* result = new LInvokeFunction(function);
+ return MarkAsCall(DefineFixed(result, r0), instr, CANNOT_DEOPTIMIZE_EAGERLY);
+}
+
+
LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
BuiltinFunctionId op = instr->op();
if (op == kMathLog || op == kMathSin || op == kMathCos) {
@@ -1329,7 +1343,7 @@
return DoArithmeticD(Token::DIV, instr);
} else if (instr->representation().IsInteger32()) {
// TODO(1042) The fixed register allocation
- // is needed because we call GenericBinaryOpStub from
+ // is needed because we call TypeRecordingBinaryOpStub from
// the generated code, which requires registers r0
// and r1 to be used. We should remove that
// when we provide a native implementation.
@@ -1723,26 +1737,42 @@
}
-LInstruction* LChunkBuilder::DoLoadGlobal(HLoadGlobal* instr) {
- LLoadGlobal* result = new LLoadGlobal();
+LInstruction* LChunkBuilder::DoLoadGlobalCell(HLoadGlobalCell* instr) {
+ LLoadGlobalCell* result = new LLoadGlobalCell;
return instr->check_hole_value()
? AssignEnvironment(DefineAsRegister(result))
: DefineAsRegister(result);
}
-LInstruction* LChunkBuilder::DoStoreGlobal(HStoreGlobal* instr) {
+LInstruction* LChunkBuilder::DoLoadGlobalGeneric(HLoadGlobalGeneric* instr) {
+ LOperand* global_object = UseFixed(instr->global_object(), r0);
+ LLoadGlobalGeneric* result = new LLoadGlobalGeneric(global_object);
+ return MarkAsCall(DefineFixed(result, r0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoStoreGlobalCell(HStoreGlobalCell* instr) {
if (instr->check_hole_value()) {
LOperand* temp = TempRegister();
LOperand* value = UseRegister(instr->value());
- return AssignEnvironment(new LStoreGlobal(value, temp));
+ return AssignEnvironment(new LStoreGlobalCell(value, temp));
} else {
LOperand* value = UseRegisterAtStart(instr->value());
- return new LStoreGlobal(value, NULL);
+ return new LStoreGlobalCell(value, NULL);
}
}
+LInstruction* LChunkBuilder::DoStoreGlobalGeneric(HStoreGlobalGeneric* instr) {
+ LOperand* global_object = UseFixed(instr->global_object(), r1);
+ LOperand* value = UseFixed(instr->value(), r0);
+ LStoreGlobalGeneric* result =
+ new LStoreGlobalGeneric(global_object, value);
+ return MarkAsCall(result, instr);
+}
+
+
LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) {
LOperand* context = UseRegisterAtStart(instr->value());
return DefineAsRegister(new LLoadContextSlot(context));
@@ -1824,21 +1854,20 @@
LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement(
HLoadKeyedSpecializedArrayElement* instr) {
- // TODO(danno): Add support for other external array types.
- if (instr->array_type() != kExternalPixelArray) {
- Abort("unsupported load for external array type.");
- return NULL;
- }
-
- ASSERT(instr->representation().IsInteger32());
+ ExternalArrayType array_type = instr->array_type();
+ Representation representation(instr->representation());
+ ASSERT((representation.IsInteger32() && array_type != kExternalFloatArray) ||
+ (representation.IsDouble() && array_type == kExternalFloatArray));
ASSERT(instr->key()->representation().IsInteger32());
- LOperand* external_pointer =
- UseRegisterAtStart(instr->external_pointer());
- LOperand* key = UseRegisterAtStart(instr->key());
+ LOperand* external_pointer = UseRegister(instr->external_pointer());
+ LOperand* key = UseRegister(instr->key());
LLoadKeyedSpecializedArrayElement* result =
- new LLoadKeyedSpecializedArrayElement(external_pointer,
- key);
- return DefineAsRegister(result);
+ new LLoadKeyedSpecializedArrayElement(external_pointer, key);
+ LInstruction* load_instr = DefineAsRegister(result);
+ // An unsigned int array load might overflow and cause a deopt, make sure it
+ // has an environment.
+ return (array_type == kExternalUnsignedIntArray) ?
+ AssignEnvironment(load_instr) : load_instr;
}
@@ -1873,23 +1902,24 @@
LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement(
HStoreKeyedSpecializedArrayElement* instr) {
- // TODO(danno): Add support for other external array types.
- if (instr->array_type() != kExternalPixelArray) {
- Abort("unsupported store for external array type.");
- return NULL;
- }
-
- ASSERT(instr->value()->representation().IsInteger32());
+ Representation representation(instr->value()->representation());
+ ExternalArrayType array_type = instr->array_type();
+ ASSERT((representation.IsInteger32() && array_type != kExternalFloatArray) ||
+ (representation.IsDouble() && array_type == kExternalFloatArray));
ASSERT(instr->external_pointer()->representation().IsExternal());
ASSERT(instr->key()->representation().IsInteger32());
LOperand* external_pointer = UseRegister(instr->external_pointer());
- LOperand* value = UseTempRegister(instr->value()); // changed by clamp.
+ bool val_is_temp_register = array_type == kExternalPixelArray ||
+ array_type == kExternalFloatArray;
+ LOperand* val = val_is_temp_register
+ ? UseTempRegister(instr->value())
+ : UseRegister(instr->value());
LOperand* key = UseRegister(instr->key());
return new LStoreKeyedSpecializedArrayElement(external_pointer,
key,
- value);
+ val);
}
@@ -1930,6 +1960,13 @@
}
+LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) {
+ LOperand* left = UseRegisterAtStart(instr->left());
+ LOperand* right = UseRegisterAtStart(instr->right());
+ return MarkAsCall(DefineFixed(new LStringAdd(left, right), r0), instr);
+}
+
+
LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
LOperand* string = UseRegister(instr->string());
LOperand* index = UseRegisterOrConstant(instr->index());
@@ -2061,8 +2098,6 @@
}
}
- ASSERT(env->length() == instr->environment_length());
-
// If there is an instruction pending deoptimization environment create a
// lazy bailout instruction to capture the environment.
if (pending_deoptimization_ast_id_ == instr->ast_id()) {
diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h
index 77aabaf..4add6bf 100644
--- a/src/arm/lithium-arm.h
+++ b/src/arm/lithium-arm.h
@@ -106,6 +106,7 @@
V(InstanceOfAndBranch) \
V(InstanceOfKnownGlobal) \
V(Integer32ToDouble) \
+ V(InvokeFunction) \
V(IsNull) \
V(IsNullAndBranch) \
V(IsObject) \
@@ -119,7 +120,8 @@
V(LoadElements) \
V(LoadExternalArrayPointer) \
V(LoadFunctionPrototype) \
- V(LoadGlobal) \
+ V(LoadGlobalCell) \
+ V(LoadGlobalGeneric) \
V(LoadKeyedFastElement) \
V(LoadKeyedGeneric) \
V(LoadKeyedSpecializedArrayElement) \
@@ -144,12 +146,14 @@
V(SmiUntag) \
V(StackCheck) \
V(StoreContextSlot) \
- V(StoreGlobal) \
+ V(StoreGlobalCell) \
+ V(StoreGlobalGeneric) \
V(StoreKeyedFastElement) \
V(StoreKeyedGeneric) \
V(StoreKeyedSpecializedArrayElement) \
V(StoreNamedField) \
V(StoreNamedGeneric) \
+ V(StringAdd) \
V(StringCharCodeAt) \
V(StringCharFromCode) \
V(StringLength) \
@@ -1259,22 +1263,55 @@
};
-class LLoadGlobal: public LTemplateInstruction<1, 0, 0> {
+class LLoadGlobalCell: public LTemplateInstruction<1, 0, 0> {
public:
- DECLARE_CONCRETE_INSTRUCTION(LoadGlobal, "load-global")
- DECLARE_HYDROGEN_ACCESSOR(LoadGlobal)
+ DECLARE_CONCRETE_INSTRUCTION(LoadGlobalCell, "load-global-cell")
+ DECLARE_HYDROGEN_ACCESSOR(LoadGlobalCell)
};
-class LStoreGlobal: public LTemplateInstruction<0, 1, 1> {
+class LLoadGlobalGeneric: public LTemplateInstruction<1, 1, 0> {
public:
- LStoreGlobal(LOperand* value, LOperand* temp) {
+ explicit LLoadGlobalGeneric(LOperand* global_object) {
+ inputs_[0] = global_object;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadGlobalGeneric, "load-global-generic")
+ DECLARE_HYDROGEN_ACCESSOR(LoadGlobalGeneric)
+
+ LOperand* global_object() { return inputs_[0]; }
+ Handle<Object> name() const { return hydrogen()->name(); }
+ bool for_typeof() const { return hydrogen()->for_typeof(); }
+};
+
+
+class LStoreGlobalCell: public LTemplateInstruction<0, 1, 1> {
+ public:
+ LStoreGlobalCell(LOperand* value, LOperand* temp) {
inputs_[0] = value;
temps_[0] = temp;
}
- DECLARE_CONCRETE_INSTRUCTION(StoreGlobal, "store-global")
- DECLARE_HYDROGEN_ACCESSOR(StoreGlobal)
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalCell, "store-global-cell")
+ DECLARE_HYDROGEN_ACCESSOR(StoreGlobalCell)
+};
+
+
+class LStoreGlobalGeneric: public LTemplateInstruction<0, 2, 0> {
+ public:
+ explicit LStoreGlobalGeneric(LOperand* global_object,
+ LOperand* value) {
+ inputs_[0] = global_object;
+ inputs_[1] = value;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalGeneric, "store-global-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreGlobalGeneric)
+
+ LOperand* global_object() { return InputAt(0); }
+ Handle<Object> name() const { return hydrogen()->name(); }
+ LOperand* value() { return InputAt(1); }
+ bool strict_mode() { return hydrogen()->strict_mode(); }
};
@@ -1377,6 +1414,23 @@
};
+class LInvokeFunction: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LInvokeFunction(LOperand* function) {
+ inputs_[0] = function;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(InvokeFunction, "invoke-function")
+ DECLARE_HYDROGEN_ACCESSOR(InvokeFunction)
+
+ LOperand* function() { return inputs_[0]; }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
class LCallKeyed: public LTemplateInstruction<1, 1, 0> {
public:
explicit LCallKeyed(LOperand* key) {
@@ -1605,6 +1659,7 @@
LOperand* object() { return inputs_[0]; }
LOperand* value() { return inputs_[1]; }
Handle<Object> name() const { return hydrogen()->name(); }
+ bool strict_mode() { return hydrogen()->strict_mode(); }
};
@@ -1644,6 +1699,7 @@
LOperand* object() { return inputs_[0]; }
LOperand* key() { return inputs_[1]; }
LOperand* value() { return inputs_[2]; }
+ bool strict_mode() { return hydrogen()->strict_mode(); }
};
class LStoreKeyedSpecializedArrayElement: public LTemplateInstruction<0, 3, 0> {
@@ -1669,6 +1725,22 @@
};
+class LStringAdd: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LStringAdd(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string-add")
+ DECLARE_HYDROGEN_ACCESSOR(StringAdd)
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+};
+
+
+
class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> {
public:
LStringCharCodeAt(LOperand* string, LOperand* index) {
diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc
index 75406cf..2d415cb 100644
--- a/src/arm/lithium-codegen-arm.cc
+++ b/src/arm/lithium-codegen-arm.cc
@@ -91,7 +91,7 @@
void LCodeGen::FinishCode(Handle<Code> code) {
ASSERT(is_done());
- code->set_stack_slots(StackSlotCount());
+ code->set_stack_slots(GetStackSlotCount());
code->set_safepoint_table_offset(safepoints_.GetCodeOffset());
PopulateDeoptimizationData(code);
Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(code);
@@ -149,7 +149,7 @@
__ add(fp, sp, Operand(2 * kPointerSize)); // Adjust FP to point to saved FP.
// Reserve space for the stack slots needed by the code.
- int slots = StackSlotCount();
+ int slots = GetStackSlotCount();
if (slots > 0) {
if (FLAG_debug_code) {
__ mov(r0, Operand(slots));
@@ -263,7 +263,7 @@
bool LCodeGen::GenerateSafepointTable() {
ASSERT(is_done());
- safepoints_.Emit(masm(), StackSlotCount());
+ safepoints_.Emit(masm(), GetStackSlotCount());
return !is_aborted();
}
@@ -459,7 +459,7 @@
translation->StoreDoubleStackSlot(op->index());
} else if (op->IsArgument()) {
ASSERT(is_tagged);
- int src_index = StackSlotCount() + op->index();
+ int src_index = GetStackSlotCount() + op->index();
translation->StoreStackSlot(src_index);
} else if (op->IsRegister()) {
Register reg = ToRegister(op);
@@ -484,11 +484,19 @@
void LCodeGen::CallCode(Handle<Code> code,
RelocInfo::Mode mode,
LInstruction* instr) {
+ CallCodeGeneric(code, mode, instr, RECORD_SIMPLE_SAFEPOINT);
+}
+
+
+void LCodeGen::CallCodeGeneric(Handle<Code> code,
+ RelocInfo::Mode mode,
+ LInstruction* instr,
+ SafepointMode safepoint_mode) {
ASSERT(instr != NULL);
LPointerMap* pointers = instr->pointer_map();
RecordPosition(pointers->position());
__ Call(code, mode);
- RegisterLazyDeoptimization(instr);
+ RegisterLazyDeoptimization(instr, safepoint_mode);
}
@@ -501,11 +509,21 @@
RecordPosition(pointers->position());
__ CallRuntime(function, num_arguments);
- RegisterLazyDeoptimization(instr);
+ RegisterLazyDeoptimization(instr, RECORD_SIMPLE_SAFEPOINT);
}
-void LCodeGen::RegisterLazyDeoptimization(LInstruction* instr) {
+void LCodeGen::CallRuntimeFromDeferred(Runtime::FunctionId id,
+ int argc,
+ LInstruction* instr) {
+ __ CallRuntimeSaveDoubles(id);
+ RecordSafepointWithRegisters(
+ instr->pointer_map(), argc, Safepoint::kNoDeoptimizationIndex);
+}
+
+
+void LCodeGen::RegisterLazyDeoptimization(LInstruction* instr,
+ SafepointMode safepoint_mode) {
// Create the environment to bailout to. If the call has side effects
// execution has to continue after the call otherwise execution can continue
// from a previous bailout point repeating the call.
@@ -517,8 +535,16 @@
}
RegisterEnvironmentForDeoptimization(deoptimization_environment);
- RecordSafepoint(instr->pointer_map(),
- deoptimization_environment->deoptimization_index());
+ if (safepoint_mode == RECORD_SIMPLE_SAFEPOINT) {
+ RecordSafepoint(instr->pointer_map(),
+ deoptimization_environment->deoptimization_index());
+ } else {
+ ASSERT(safepoint_mode == RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS);
+ RecordSafepointWithRegisters(
+ instr->pointer_map(),
+ 0,
+ deoptimization_environment->deoptimization_index());
+ }
}
@@ -650,6 +676,8 @@
Safepoint::Kind kind,
int arguments,
int deoptimization_index) {
+ ASSERT(expected_safepoint_kind_ == kind);
+
const ZoneList<LOperand*>* operands = pointers->operands();
Safepoint safepoint = safepoints_.DefineSafepoint(masm(),
kind, arguments, deoptimization_index);
@@ -1015,7 +1043,7 @@
Register left = ToRegister(instr->InputAt(0));
Register right = ToRegister(instr->InputAt(1));
- __ PushSafepointRegistersAndDoubles();
+ PushSafepointRegistersScope scope(this, Safepoint::kWithRegistersAndDoubles);
// Move left to r1 and right to r0 for the stub call.
if (left.is(r1)) {
__ Move(r0, right);
@@ -1037,7 +1065,6 @@
Safepoint::kNoDeoptimizationIndex);
// Overwrite the stored value of r0 with the result of the stub.
__ StoreToSafepointRegistersAndDoublesSlot(r0, r0);
- __ PopSafepointRegistersAndDoubles();
}
@@ -1460,11 +1487,8 @@
void LCodeGen::DoDeferredStackCheck(LGoto* instr) {
- __ PushSafepointRegisters();
- __ CallRuntimeSaveDoubles(Runtime::kStackGuard);
- RecordSafepointWithRegisters(
- instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
- __ PopSafepointRegisters();
+ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
+ CallRuntimeFromDeferred(Runtime::kStackGuard, 0, instr);
}
@@ -2065,7 +2089,7 @@
flags | InstanceofStub::kReturnTrueFalseObject);
InstanceofStub stub(flags);
- __ PushSafepointRegisters();
+ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
// Get the temp register reserved by the instruction. This needs to be r4 as
// its slot of the pushing of safepoint registers is used to communicate the
@@ -2080,12 +2104,13 @@
__ BlockConstPoolFor(kAdditionalDelta);
__ mov(temp, Operand(delta * kPointerSize));
__ StoreToSafepointRegisterSlot(temp, temp);
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ CallCodeGeneric(stub.GetCode(),
+ RelocInfo::CODE_TARGET,
+ instr,
+ RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS);
// Put the result value into the result register slot and
// restore all registers.
__ StoreToSafepointRegisterSlot(result, result);
-
- __ PopSafepointRegisters();
}
@@ -2155,7 +2180,7 @@
__ push(r0);
__ CallRuntime(Runtime::kTraceExit, 1);
}
- int32_t sp_delta = (ParameterCount() + 1) * kPointerSize;
+ int32_t sp_delta = (GetParameterCount() + 1) * kPointerSize;
__ mov(sp, fp);
__ ldm(ia_w, sp, fp.bit() | lr.bit());
__ add(sp, sp, Operand(sp_delta));
@@ -2163,7 +2188,7 @@
}
-void LCodeGen::DoLoadGlobal(LLoadGlobal* instr) {
+void LCodeGen::DoLoadGlobalCell(LLoadGlobalCell* instr) {
Register result = ToRegister(instr->result());
__ mov(ip, Operand(Handle<Object>(instr->hydrogen()->cell())));
__ ldr(result, FieldMemOperand(ip, JSGlobalPropertyCell::kValueOffset));
@@ -2175,7 +2200,19 @@
}
-void LCodeGen::DoStoreGlobal(LStoreGlobal* instr) {
+void LCodeGen::DoLoadGlobalGeneric(LLoadGlobalGeneric* instr) {
+ ASSERT(ToRegister(instr->global_object()).is(r0));
+ ASSERT(ToRegister(instr->result()).is(r0));
+
+ __ mov(r2, Operand(instr->name()));
+ RelocInfo::Mode mode = instr->for_typeof() ? RelocInfo::CODE_TARGET
+ : RelocInfo::CODE_TARGET_CONTEXT;
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ CallCode(ic, mode, instr);
+}
+
+
+void LCodeGen::DoStoreGlobalCell(LStoreGlobalCell* instr) {
Register value = ToRegister(instr->InputAt(0));
Register scratch = scratch0();
@@ -2200,6 +2237,18 @@
}
+void LCodeGen::DoStoreGlobalGeneric(LStoreGlobalGeneric* instr) {
+ ASSERT(ToRegister(instr->global_object()).is(r1));
+ ASSERT(ToRegister(instr->value()).is(r0));
+
+ __ mov(r2, Operand(instr->name()));
+ Handle<Code> ic = instr->strict_mode()
+ ? isolate()->builtins()->StoreIC_Initialize_Strict()
+ : isolate()->builtins()->StoreIC_Initialize();
+ CallCode(ic, RelocInfo::CODE_TARGET_CONTEXT, instr);
+}
+
+
void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
Register context = ToRegister(instr->context());
Register result = ToRegister(instr->result());
@@ -2361,12 +2410,14 @@
__ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
__ cmp(scratch, ip);
__ b(eq, &done);
- __ LoadRoot(ip, Heap::kExternalPixelArrayMapRootIndex);
- __ cmp(scratch, ip);
- __ b(eq, &done);
__ LoadRoot(ip, Heap::kFixedCOWArrayMapRootIndex);
__ cmp(scratch, ip);
- __ Check(eq, "Check for fast elements failed.");
+ __ b(eq, &done);
+ __ ldr(scratch, FieldMemOperand(result, HeapObject::kMapOffset));
+ __ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
+ __ sub(scratch, scratch, Operand(FIRST_EXTERNAL_ARRAY_TYPE));
+ __ cmp(scratch, Operand(kExternalArrayTypeCount));
+ __ Check(cc, "Check for fast elements failed.");
__ bind(&done);
}
}
@@ -2419,14 +2470,47 @@
void LCodeGen::DoLoadKeyedSpecializedArrayElement(
LLoadKeyedSpecializedArrayElement* instr) {
- ASSERT(instr->array_type() == kExternalPixelArray);
-
Register external_pointer = ToRegister(instr->external_pointer());
Register key = ToRegister(instr->key());
- Register result = ToRegister(instr->result());
-
- // Load the result.
- __ ldrb(result, MemOperand(external_pointer, key));
+ ExternalArrayType array_type = instr->array_type();
+ if (array_type == kExternalFloatArray) {
+ CpuFeatures::Scope scope(VFP3);
+ DwVfpRegister result(ToDoubleRegister(instr->result()));
+ __ add(scratch0(), external_pointer, Operand(key, LSL, 2));
+ __ vldr(result.low(), scratch0(), 0);
+ __ vcvt_f64_f32(result, result.low());
+ } else {
+ Register result(ToRegister(instr->result()));
+ switch (array_type) {
+ case kExternalByteArray:
+ __ ldrsb(result, MemOperand(external_pointer, key));
+ break;
+ case kExternalUnsignedByteArray:
+ case kExternalPixelArray:
+ __ ldrb(result, MemOperand(external_pointer, key));
+ break;
+ case kExternalShortArray:
+ __ ldrsh(result, MemOperand(external_pointer, key, LSL, 1));
+ break;
+ case kExternalUnsignedShortArray:
+ __ ldrh(result, MemOperand(external_pointer, key, LSL, 1));
+ break;
+ case kExternalIntArray:
+ __ ldr(result, MemOperand(external_pointer, key, LSL, 2));
+ break;
+ case kExternalUnsignedIntArray:
+ __ ldr(result, MemOperand(external_pointer, key, LSL, 2));
+ __ cmp(result, Operand(0x80000000));
+ // TODO(danno): we could be more clever here, perhaps having a special
+ // version of the stub that detects if the overflow case actually
+ // happens, and generate code that returns a double rather than int.
+ DeoptimizeIf(cs, instr->environment());
+ break;
+ case kExternalFloatArray:
+ UNREACHABLE();
+ break;
+ }
+ }
}
@@ -2617,7 +2701,7 @@
__ Call(ip);
// Setup deoptimization.
- RegisterLazyDeoptimization(instr);
+ RegisterLazyDeoptimization(instr, RECORD_SIMPLE_SAFEPOINT);
// Restore context.
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
@@ -2655,44 +2739,43 @@
// Input is negative. Reverse its sign.
// Preserve the value of all registers.
- __ PushSafepointRegisters();
+ {
+ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
- // Registers were saved at the safepoint, so we can use
- // many scratch registers.
- Register tmp1 = input.is(r1) ? r0 : r1;
- Register tmp2 = input.is(r2) ? r0 : r2;
- Register tmp3 = input.is(r3) ? r0 : r3;
- Register tmp4 = input.is(r4) ? r0 : r4;
+ // Registers were saved at the safepoint, so we can use
+ // many scratch registers.
+ Register tmp1 = input.is(r1) ? r0 : r1;
+ Register tmp2 = input.is(r2) ? r0 : r2;
+ Register tmp3 = input.is(r3) ? r0 : r3;
+ Register tmp4 = input.is(r4) ? r0 : r4;
- // exponent: floating point exponent value.
+ // exponent: floating point exponent value.
- Label allocated, slow;
- __ LoadRoot(tmp4, Heap::kHeapNumberMapRootIndex);
- __ AllocateHeapNumber(tmp1, tmp2, tmp3, tmp4, &slow);
- __ b(&allocated);
+ Label allocated, slow;
+ __ LoadRoot(tmp4, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(tmp1, tmp2, tmp3, tmp4, &slow);
+ __ b(&allocated);
- // Slow case: Call the runtime system to do the number allocation.
- __ bind(&slow);
+ // Slow case: Call the runtime system to do the number allocation.
+ __ bind(&slow);
- __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber);
- RecordSafepointWithRegisters(
- instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
- // Set the pointer to the new heap number in tmp.
- if (!tmp1.is(r0)) __ mov(tmp1, Operand(r0));
- // Restore input_reg after call to runtime.
- __ LoadFromSafepointRegisterSlot(input, input);
- __ ldr(exponent, FieldMemOperand(input, HeapNumber::kExponentOffset));
+ CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr);
+ // Set the pointer to the new heap number in tmp.
+ if (!tmp1.is(r0)) __ mov(tmp1, Operand(r0));
+ // Restore input_reg after call to runtime.
+ __ LoadFromSafepointRegisterSlot(input, input);
+ __ ldr(exponent, FieldMemOperand(input, HeapNumber::kExponentOffset));
- __ bind(&allocated);
- // exponent: floating point exponent value.
- // tmp1: allocated heap number.
- __ bic(exponent, exponent, Operand(HeapNumber::kSignMask));
- __ str(exponent, FieldMemOperand(tmp1, HeapNumber::kExponentOffset));
- __ ldr(tmp2, FieldMemOperand(input, HeapNumber::kMantissaOffset));
- __ str(tmp2, FieldMemOperand(tmp1, HeapNumber::kMantissaOffset));
+ __ bind(&allocated);
+ // exponent: floating point exponent value.
+ // tmp1: allocated heap number.
+ __ bic(exponent, exponent, Operand(HeapNumber::kSignMask));
+ __ str(exponent, FieldMemOperand(tmp1, HeapNumber::kExponentOffset));
+ __ ldr(tmp2, FieldMemOperand(input, HeapNumber::kMantissaOffset));
+ __ str(tmp2, FieldMemOperand(tmp1, HeapNumber::kMantissaOffset));
- __ StoreToSafepointRegisterSlot(tmp1, input);
- __ PopSafepointRegisters();
+ __ StoreToSafepointRegisterSlot(tmp1, input);
+ }
__ bind(&done);
}
@@ -2778,9 +2861,49 @@
void LCodeGen::DoMathRound(LUnaryMathOperation* instr) {
DoubleRegister input = ToDoubleRegister(instr->InputAt(0));
Register result = ToRegister(instr->result());
- Register scratch1 = scratch0();
- Register scratch2 = result;
- __ EmitVFPTruncate(kRoundToNearest,
+ Register scratch1 = result;
+ Register scratch2 = scratch0();
+ Label done, check_sign_on_zero;
+
+ // Extract exponent bits.
+ __ vmov(scratch1, input.high());
+ __ ubfx(scratch2,
+ scratch1,
+ HeapNumber::kExponentShift,
+ HeapNumber::kExponentBits);
+
+ // If the number is in ]-0.5, +0.5[, the result is +/- 0.
+ __ cmp(scratch2, Operand(HeapNumber::kExponentBias - 2));
+ __ mov(result, Operand(0), LeaveCC, le);
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ b(le, &check_sign_on_zero);
+ } else {
+ __ b(le, &done);
+ }
+
+ // The following conversion will not work with numbers
+ // outside of ]-2^32, 2^32[.
+ __ cmp(scratch2, Operand(HeapNumber::kExponentBias + 32));
+ DeoptimizeIf(ge, instr->environment());
+
+ // Save the original sign for later comparison.
+ __ and_(scratch2, scratch1, Operand(HeapNumber::kSignMask));
+
+ __ vmov(double_scratch0(), 0.5);
+ __ vadd(input, input, double_scratch0());
+
+ // Check sign of the result: if the sign changed, the input
+ // value was in ]0.5, 0[ and the result should be -0.
+ __ vmov(scratch1, input.high());
+ __ eor(scratch1, scratch1, Operand(scratch2), SetCC);
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(mi, instr->environment());
+ } else {
+ __ mov(result, Operand(0), LeaveCC, mi);
+ __ b(mi, &done);
+ }
+
+ __ EmitVFPTruncate(kRoundToMinusInf,
double_scratch0().low(),
input,
scratch1,
@@ -2790,14 +2913,14 @@
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
// Test for -0.
- Label done;
__ cmp(result, Operand(0));
__ b(ne, &done);
+ __ bind(&check_sign_on_zero);
__ vmov(scratch1, input.high());
__ tst(scratch1, Operand(HeapNumber::kSignMask));
DeoptimizeIf(ne, instr->environment());
- __ bind(&done);
}
+ __ bind(&done);
}
@@ -2942,6 +3065,21 @@
}
+void LCodeGen::DoInvokeFunction(LInvokeFunction* instr) {
+ ASSERT(ToRegister(instr->function()).is(r1));
+ ASSERT(instr->HasPointerMap());
+ ASSERT(instr->HasDeoptimizationEnvironment());
+ LPointerMap* pointers = instr->pointer_map();
+ LEnvironment* env = instr->deoptimization_environment();
+ RecordPosition(pointers->position());
+ RegisterEnvironmentForDeoptimization(env);
+ SafepointGenerator generator(this, pointers, env->deoptimization_index());
+ ParameterCount count(instr->arity());
+ __ InvokeFunction(r1, count, CALL_FUNCTION, &generator);
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+}
+
+
void LCodeGen::DoCallKeyed(LCallKeyed* instr) {
ASSERT(ToRegister(instr->result()).is(r0));
@@ -3049,7 +3187,7 @@
// Name is always in r2.
__ mov(r2, Operand(instr->name()));
- Handle<Code> ic = info_->is_strict()
+ Handle<Code> ic = instr->strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
@@ -3090,15 +3228,41 @@
void LCodeGen::DoStoreKeyedSpecializedArrayElement(
LStoreKeyedSpecializedArrayElement* instr) {
- ASSERT(instr->array_type() == kExternalPixelArray);
Register external_pointer = ToRegister(instr->external_pointer());
Register key = ToRegister(instr->key());
- Register value = ToRegister(instr->value());
-
- // Clamp the value to [0..255].
- __ Usat(value, 8, Operand(value));
- __ strb(value, MemOperand(external_pointer, key, LSL, 0));
+ ExternalArrayType array_type = instr->array_type();
+ if (array_type == kExternalFloatArray) {
+ CpuFeatures::Scope scope(VFP3);
+ DwVfpRegister value(ToDoubleRegister(instr->value()));
+ __ add(scratch0(), external_pointer, Operand(key, LSL, 2));
+ __ vcvt_f32_f64(double_scratch0().low(), value);
+ __ vstr(double_scratch0().low(), scratch0(), 0);
+ } else {
+ Register value(ToRegister(instr->value()));
+ switch (array_type) {
+ case kExternalPixelArray:
+ // Clamp the value to [0..255].
+ __ Usat(value, 8, Operand(value));
+ __ strb(value, MemOperand(external_pointer, key));
+ break;
+ case kExternalByteArray:
+ case kExternalUnsignedByteArray:
+ __ strb(value, MemOperand(external_pointer, key));
+ break;
+ case kExternalShortArray:
+ case kExternalUnsignedShortArray:
+ __ strh(value, MemOperand(external_pointer, key, LSL, 1));
+ break;
+ case kExternalIntArray:
+ case kExternalUnsignedIntArray:
+ __ str(value, MemOperand(external_pointer, key, LSL, 2));
+ break;
+ case kExternalFloatArray:
+ UNREACHABLE();
+ break;
+ }
+ }
}
@@ -3107,13 +3271,21 @@
ASSERT(ToRegister(instr->key()).is(r1));
ASSERT(ToRegister(instr->value()).is(r0));
- Handle<Code> ic = info_->is_strict()
+ Handle<Code> ic = instr->strict_mode()
? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
: isolate()->builtins()->KeyedStoreIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
}
+void LCodeGen::DoStringAdd(LStringAdd* instr) {
+ __ push(ToRegister(instr->left()));
+ __ push(ToRegister(instr->right()));
+ StringAddStub stub(NO_STRING_CHECK_IN_STUB);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+}
+
+
void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
class DeferredStringCharCodeAt: public LDeferredCode {
public:
@@ -3230,7 +3402,7 @@
// contained in the register pointer map.
__ mov(result, Operand(0));
- __ PushSafepointRegisters();
+ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
__ push(string);
// Push the index as a smi. This is safe because of the checks in
// DoStringCharCodeAt above.
@@ -3243,15 +3415,12 @@
__ SmiTag(index);
__ push(index);
}
- __ CallRuntimeSaveDoubles(Runtime::kStringCharCodeAt);
- RecordSafepointWithRegisters(
- instr->pointer_map(), 2, Safepoint::kNoDeoptimizationIndex);
+ CallRuntimeFromDeferred(Runtime::kStringCharCodeAt, 2, instr);
if (FLAG_debug_code) {
__ AbortIfNotSmi(r0);
}
__ SmiUntag(r0);
__ StoreToSafepointRegisterSlot(r0, result);
- __ PopSafepointRegisters();
}
@@ -3294,14 +3463,11 @@
// contained in the register pointer map.
__ mov(result, Operand(0));
- __ PushSafepointRegisters();
+ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
__ SmiTag(char_code);
__ push(char_code);
- __ CallRuntimeSaveDoubles(Runtime::kCharFromCode);
- RecordSafepointWithRegisters(
- instr->pointer_map(), 1, Safepoint::kNoDeoptimizationIndex);
+ CallRuntimeFromDeferred(Runtime::kCharFromCode, 1, instr);
__ StoreToSafepointRegisterSlot(r0, result);
- __ PopSafepointRegisters();
}
@@ -3357,7 +3523,7 @@
SwVfpRegister flt_scratch = s0;
// Preserve the value of all registers.
- __ PushSafepointRegisters();
+ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
// There was overflow, so bits 30 and 31 of the original integer
// disagree. Try to allocate a heap number in new space and store
@@ -3382,9 +3548,7 @@
// integer value.
__ mov(ip, Operand(0));
__ StoreToSafepointRegisterSlot(ip, reg);
- __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber);
- RecordSafepointWithRegisters(
- instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
+ CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr);
if (!reg.is(r0)) __ mov(reg, r0);
// Done. Put the value in dbl_scratch into the value of the allocated heap
@@ -3393,7 +3557,6 @@
__ sub(ip, reg, Operand(kHeapObjectTag));
__ vstr(dbl_scratch, ip, HeapNumber::kValueOffset);
__ StoreToSafepointRegisterSlot(reg, reg);
- __ PopSafepointRegisters();
}
@@ -3433,12 +3596,9 @@
Register reg = ToRegister(instr->result());
__ mov(reg, Operand(0));
- __ PushSafepointRegisters();
- __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber);
- RecordSafepointWithRegisters(
- instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
+ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
+ CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr);
__ StoreToSafepointRegisterSlot(r0, reg);
- __ PopSafepointRegisters();
}
diff --git a/src/arm/lithium-codegen-arm.h b/src/arm/lithium-codegen-arm.h
index caa85d2..1110ea6 100644
--- a/src/arm/lithium-codegen-arm.h
+++ b/src/arm/lithium-codegen-arm.h
@@ -57,7 +57,8 @@
status_(UNUSED),
deferred_(8),
osr_pc_offset_(-1),
- resolver_(this) {
+ resolver_(this),
+ expected_safepoint_kind_(Safepoint::kSimple) {
PopulateDeoptimizationLiteralsWithInlinedFunctions();
}
@@ -137,7 +138,7 @@
bool is_aborted() const { return status_ == ABORTED; }
int strict_mode_flag() const {
- return info()->is_strict() ? kStrictMode : kNonStrictMode;
+ return info()->is_strict_mode() ? kStrictMode : kNonStrictMode;
}
LChunk* chunk() const { return chunk_; }
@@ -157,8 +158,8 @@
Register temporary,
Register temporary2);
- int StackSlotCount() const { return chunk()->spill_slot_count(); }
- int ParameterCount() const { return scope()->num_parameters(); }
+ int GetStackSlotCount() const { return chunk()->spill_slot_count(); }
+ int GetParameterCount() const { return scope()->num_parameters(); }
void Abort(const char* format, ...);
void Comment(const char* format, ...);
@@ -172,12 +173,24 @@
bool GenerateDeferredCode();
bool GenerateSafepointTable();
+ enum SafepointMode {
+ RECORD_SIMPLE_SAFEPOINT,
+ RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS
+ };
+
void CallCode(Handle<Code> code,
RelocInfo::Mode mode,
LInstruction* instr);
+
+ void CallCodeGeneric(Handle<Code> code,
+ RelocInfo::Mode mode,
+ LInstruction* instr,
+ SafepointMode safepoint_mode);
+
void CallRuntime(const Runtime::Function* function,
int num_arguments,
LInstruction* instr);
+
void CallRuntime(Runtime::FunctionId id,
int num_arguments,
LInstruction* instr) {
@@ -185,6 +198,10 @@
CallRuntime(function, num_arguments, instr);
}
+ void CallRuntimeFromDeferred(Runtime::FunctionId id,
+ int argc,
+ LInstruction* instr);
+
// Generate a direct call to a known function. Expects the function
// to be in edi.
void CallKnownFunction(Handle<JSFunction> function,
@@ -193,7 +210,9 @@
void LoadHeapObject(Register result, Handle<HeapObject> object);
- void RegisterLazyDeoptimization(LInstruction* instr);
+ void RegisterLazyDeoptimization(LInstruction* instr,
+ SafepointMode safepoint_mode);
+
void RegisterEnvironmentForDeoptimization(LEnvironment* environment);
void DeoptimizeIf(Condition cc, LEnvironment* environment);
@@ -292,6 +311,48 @@
// Compiler from a set of parallel moves to a sequential list of moves.
LGapResolver resolver_;
+ Safepoint::Kind expected_safepoint_kind_;
+
+ class PushSafepointRegistersScope BASE_EMBEDDED {
+ public:
+ PushSafepointRegistersScope(LCodeGen* codegen,
+ Safepoint::Kind kind)
+ : codegen_(codegen) {
+ ASSERT(codegen_->expected_safepoint_kind_ == Safepoint::kSimple);
+ codegen_->expected_safepoint_kind_ = kind;
+
+ switch (codegen_->expected_safepoint_kind_) {
+ case Safepoint::kWithRegisters:
+ codegen_->masm_->PushSafepointRegisters();
+ break;
+ case Safepoint::kWithRegistersAndDoubles:
+ codegen_->masm_->PushSafepointRegistersAndDoubles();
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ ~PushSafepointRegistersScope() {
+ Safepoint::Kind kind = codegen_->expected_safepoint_kind_;
+ ASSERT((kind & Safepoint::kWithRegisters) != 0);
+ switch (kind) {
+ case Safepoint::kWithRegisters:
+ codegen_->masm_->PopSafepointRegisters();
+ break;
+ case Safepoint::kWithRegistersAndDoubles:
+ codegen_->masm_->PopSafepointRegistersAndDoubles();
+ break;
+ default:
+ UNREACHABLE();
+ }
+ codegen_->expected_safepoint_kind_ = Safepoint::kSimple;
+ }
+
+ private:
+ LCodeGen* codegen_;
+ };
+
friend class LDeferredCode;
friend class LEnvironment;
friend class SafepointGenerator;
diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc
index 3a1a8b6..6a095d3 100644
--- a/src/arm/macro-assembler-arm.cc
+++ b/src/arm/macro-assembler-arm.cc
@@ -32,18 +32,21 @@
#if defined(V8_TARGET_ARCH_ARM)
#include "bootstrapper.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "debug.h"
#include "runtime.h"
namespace v8 {
namespace internal {
-MacroAssembler::MacroAssembler(void* buffer, int size)
- : Assembler(buffer, size),
+MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size)
+ : Assembler(arg_isolate, buffer, size),
generating_stub_(false),
- allow_stub_calls_(true),
- code_object_(HEAP->undefined_value()) {
+ allow_stub_calls_(true) {
+ if (isolate() != NULL) {
+ code_object_ = Handle<Object>(isolate()->heap()->undefined_value(),
+ isolate());
+ }
}
@@ -292,7 +295,7 @@
} else if (!src2.is_single_instruction() &&
!src2.must_use_constant_pool() &&
- Isolate::Current()->cpu_features()->IsSupported(ARMv7) &&
+ CpuFeatures::IsSupported(ARMv7) &&
IsPowerOf2(src2.immediate() + 1)) {
ubfx(dst, src1, 0, WhichPowerOf2(src2.immediate() + 1), cond);
@@ -305,7 +308,7 @@
void MacroAssembler::Ubfx(Register dst, Register src1, int lsb, int width,
Condition cond) {
ASSERT(lsb < 32);
- if (!Isolate::Current()->cpu_features()->IsSupported(ARMv7)) {
+ if (!CpuFeatures::IsSupported(ARMv7)) {
int mask = (1 << (width + lsb)) - 1 - ((1 << lsb) - 1);
and_(dst, src1, Operand(mask), LeaveCC, cond);
if (lsb != 0) {
@@ -320,7 +323,7 @@
void MacroAssembler::Sbfx(Register dst, Register src1, int lsb, int width,
Condition cond) {
ASSERT(lsb < 32);
- if (!Isolate::Current()->cpu_features()->IsSupported(ARMv7)) {
+ if (!CpuFeatures::IsSupported(ARMv7)) {
int mask = (1 << (width + lsb)) - 1 - ((1 << lsb) - 1);
and_(dst, src1, Operand(mask), LeaveCC, cond);
int shift_up = 32 - lsb - width;
@@ -348,7 +351,7 @@
ASSERT(lsb + width < 32);
ASSERT(!scratch.is(dst));
if (width == 0) return;
- if (!Isolate::Current()->cpu_features()->IsSupported(ARMv7)) {
+ if (!CpuFeatures::IsSupported(ARMv7)) {
int mask = (1 << (width + lsb)) - 1 - ((1 << lsb) - 1);
bic(dst, dst, Operand(mask));
and_(scratch, src, Operand((1 << width) - 1));
@@ -362,7 +365,7 @@
void MacroAssembler::Bfc(Register dst, int lsb, int width, Condition cond) {
ASSERT(lsb < 32);
- if (!Isolate::Current()->cpu_features()->IsSupported(ARMv7)) {
+ if (!CpuFeatures::IsSupported(ARMv7)) {
int mask = (1 << (width + lsb)) - 1 - ((1 << lsb) - 1);
bic(dst, dst, Operand(mask));
} else {
@@ -373,7 +376,7 @@
void MacroAssembler::Usat(Register dst, int satpos, const Operand& src,
Condition cond) {
- if (!Isolate::Current()->cpu_features()->IsSupported(ARMv7)) {
+ if (!CpuFeatures::IsSupported(ARMv7)) {
ASSERT(!dst.is(pc) && !src.rm().is(pc));
ASSERT((satpos >= 0) && (satpos <= 31));
@@ -619,7 +622,7 @@
ASSERT_EQ(dst1.code() + 1, dst2.code());
// Generate two ldr instructions if ldrd is not available.
- if (Isolate::Current()->cpu_features()->IsSupported(ARMv7)) {
+ if (CpuFeatures::IsSupported(ARMv7)) {
CpuFeatures::Scope scope(ARMv7);
ldrd(dst1, dst2, src, cond);
} else {
@@ -644,7 +647,7 @@
ASSERT_EQ(src1.code() + 1, src2.code());
// Generate two str instructions if strd is not available.
- if (Isolate::Current()->cpu_features()->IsSupported(ARMv7)) {
+ if (CpuFeatures::IsSupported(ARMv7)) {
CpuFeatures::Scope scope(ARMv7);
strd(src1, src2, dst, cond);
} else {
@@ -746,12 +749,10 @@
// Optionally save all double registers.
if (save_doubles) {
- sub(sp, sp, Operand(DwVfpRegister::kNumRegisters * kDoubleSize));
- const int offset = -2 * kPointerSize;
- for (int i = 0; i < DwVfpRegister::kNumRegisters; i++) {
- DwVfpRegister reg = DwVfpRegister::from_code(i);
- vstr(reg, fp, offset - ((i + 1) * kDoubleSize));
- }
+ DwVfpRegister first = d0;
+ DwVfpRegister last =
+ DwVfpRegister::from_code(DwVfpRegister::kNumRegisters - 1);
+ vstm(db_w, sp, first, last);
// Note that d0 will be accessible at
// fp - 2 * kPointerSize - DwVfpRegister::kNumRegisters * kDoubleSize,
// since the sp slot and code slot were pushed after the fp.
@@ -808,11 +809,13 @@
Register argument_count) {
// Optionally restore all double registers.
if (save_doubles) {
- for (int i = 0; i < DwVfpRegister::kNumRegisters; i++) {
- DwVfpRegister reg = DwVfpRegister::from_code(i);
- const int offset = -2 * kPointerSize;
- vldr(reg, fp, offset - ((i + 1) * kDoubleSize));
- }
+ // Calculate the stack location of the saved doubles and restore them.
+ const int offset = 2 * kPointerSize;
+ sub(r3, fp, Operand(offset + DwVfpRegister::kNumRegisters * kDoubleSize));
+ DwVfpRegister first = d0;
+ DwVfpRegister last =
+ DwVfpRegister::from_code(DwVfpRegister::kNumRegisters - 1);
+ vldm(ia, r3, first, last);
}
// Clear top frame.
@@ -836,11 +839,7 @@
}
void MacroAssembler::GetCFunctionDoubleResult(const DoubleRegister dst) {
-#if !defined(USE_ARM_EABI)
- UNREACHABLE();
-#else
vmov(dst, r0, r1);
-#endif
}
@@ -1799,9 +1798,10 @@
bind(&delete_allocated_handles);
str(r5, MemOperand(r7, kLimitOffset));
mov(r4, r0);
- PrepareCallCFunction(0, r5);
+ PrepareCallCFunction(1, r5);
+ mov(r0, Operand(ExternalReference::isolate_address()));
CallCFunction(
- ExternalReference::delete_handle_scope_extensions(isolate()), 0);
+ ExternalReference::delete_handle_scope_extensions(isolate()), 1);
mov(r0, r4);
jmp(&leave_exit_frame);
@@ -1902,7 +1902,7 @@
Register scratch2,
DwVfpRegister double_scratch,
Label *not_int32) {
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
sub(scratch, source, Operand(kHeapObjectTag));
vldr(double_scratch, scratch, HeapNumber::kValueOffset);
@@ -1998,7 +1998,7 @@
Register scratch1,
Register scratch2,
CheckForInexactConversion check_inexact) {
- ASSERT(Isolate::Current()->cpu_features()->IsSupported(VFP3));
+ ASSERT(CpuFeatures::IsSupported(VFP3));
CpuFeatures::Scope scope(VFP3);
Register prev_fpscr = scratch1;
Register scratch = scratch2;
@@ -2156,7 +2156,7 @@
void MacroAssembler::GetLeastBitsFromSmi(Register dst,
Register src,
int num_least_bits) {
- if (Isolate::Current()->cpu_features()->IsSupported(ARMv7)) {
+ if (CpuFeatures::IsSupported(ARMv7)) {
ubfx(dst, src, kSmiTagSize, num_least_bits);
} else {
mov(dst, Operand(src, ASR, kSmiTagSize));
@@ -2797,9 +2797,6 @@
void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) {
int frame_alignment = ActivationFrameAlignment();
- // Reserve space for Isolate address which is always passed as last parameter
- num_arguments += 1;
-
// Up to four simple arguments are passed in registers r0..r3.
int stack_passed_arguments = (num_arguments <= kRegisterPassedArguments) ?
0 : num_arguments - kRegisterPassedArguments;
@@ -2836,19 +2833,6 @@
ExternalReference function_reference,
Register scratch,
int num_arguments) {
- // Push Isolate address as the last argument.
- if (num_arguments < kRegisterPassedArguments) {
- Register arg_to_reg[] = {r0, r1, r2, r3};
- Register r = arg_to_reg[num_arguments];
- mov(r, Operand(ExternalReference::isolate_address()));
- } else {
- int stack_passed_arguments = num_arguments - kRegisterPassedArguments;
- // Push Isolate address on the stack after the arguments.
- mov(scratch, Operand(ExternalReference::isolate_address()));
- str(scratch, MemOperand(sp, stack_passed_arguments * kPointerSize));
- }
- num_arguments += 1;
-
// Make sure that the stack is aligned before calling a C function unless
// running in the simulator. The simulator has its own alignment check which
// provides more information.
@@ -2911,7 +2895,7 @@
: address_(address),
instructions_(instructions),
size_(instructions * Assembler::kInstrSize),
- masm_(address, size_ + Assembler::kGap) {
+ masm_(Isolate::Current(), address, size_ + Assembler::kGap) {
// Create a new macro assembler pointing to the address of the code to patch.
// The size is adjusted with kGap on order for the assembler to generate size
// bytes of instructions without failing with buffer size constraints.
diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h
index 2b81c08..ab5efb0 100644
--- a/src/arm/macro-assembler-arm.h
+++ b/src/arm/macro-assembler-arm.h
@@ -90,7 +90,11 @@
// MacroAssembler implements a collection of frequently used macros.
class MacroAssembler: public Assembler {
public:
- MacroAssembler(void* buffer, int size);
+ // The isolate parameter can be NULL if the macro assembler should
+ // not use isolate-dependent functionality. In this case, it's the
+ // responsibility of the caller to never invoke such function on the
+ // macro assembler.
+ MacroAssembler(Isolate* isolate, void* buffer, int size);
// Jump, Call, and Ret pseudo instructions implementing inter-working.
void Jump(Register target, Condition cond = al);
@@ -781,7 +785,10 @@
// Store the function for the given builtin in the target register.
void GetBuiltinFunction(Register target, Builtins::JavaScript id);
- Handle<Object> CodeObject() { return code_object_; }
+ Handle<Object> CodeObject() {
+ ASSERT(!code_object_.is_null());
+ return code_object_;
+ }
// ---------------------------------------------------------------------------
diff --git a/src/arm/regexp-macro-assembler-arm.cc b/src/arm/regexp-macro-assembler-arm.cc
index 8d540d4..4bd8c80 100644
--- a/src/arm/regexp-macro-assembler-arm.cc
+++ b/src/arm/regexp-macro-assembler-arm.cc
@@ -116,7 +116,7 @@
RegExpMacroAssemblerARM::RegExpMacroAssemblerARM(
Mode mode,
int registers_to_save)
- : masm_(new MacroAssembler(NULL, kRegExpCodeSize)),
+ : masm_(new MacroAssembler(Isolate::Current(), NULL, kRegExpCodeSize)),
mode_(mode),
num_registers_(registers_to_save),
num_saved_registers_(registers_to_save),
@@ -347,7 +347,7 @@
__ sub(current_input_offset(), r2, end_of_input_address());
} else {
ASSERT(mode_ == UC16);
- int argument_count = 3;
+ int argument_count = 4;
__ PrepareCallCFunction(argument_count, r2);
// r0 - offset of start of capture
@@ -358,6 +358,7 @@
// r0: Address byte_offset1 - Address captured substring's start.
// r1: Address byte_offset2 - Address of current character position.
// r2: size_t byte_length - length of capture in bytes(!)
+ // r3: Isolate* isolate
// Address of start of capture.
__ add(r0, r0, Operand(end_of_input_address()));
@@ -367,6 +368,8 @@
__ mov(r4, Operand(r1));
// Address of current input position.
__ add(r1, current_input_offset(), Operand(end_of_input_address()));
+ // Isolate.
+ __ mov(r3, Operand(ExternalReference::isolate_address()));
ExternalReference function =
ExternalReference::re_case_insensitive_compare_uc16(masm_->isolate());
@@ -778,10 +781,11 @@
Label grow_failed;
// Call GrowStack(backtrack_stackpointer(), &stack_base)
- static const int num_arguments = 2;
+ static const int num_arguments = 3;
__ PrepareCallCFunction(num_arguments, r0);
__ mov(r0, backtrack_stackpointer());
__ add(r1, frame_pointer(), Operand(kStackHighEnd));
+ __ mov(r2, Operand(ExternalReference::isolate_address()));
ExternalReference grow_stack =
ExternalReference::re_grow_stack(masm_->isolate());
__ CallCFunction(grow_stack, num_arguments);
diff --git a/src/arm/register-allocator-arm-inl.h b/src/arm/register-allocator-arm-inl.h
deleted file mode 100644
index 945cdeb..0000000
--- a/src/arm/register-allocator-arm-inl.h
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_ARM_REGISTER_ALLOCATOR_ARM_INL_H_
-#define V8_ARM_REGISTER_ALLOCATOR_ARM_INL_H_
-
-#include "v8.h"
-
-namespace v8 {
-namespace internal {
-
-// -------------------------------------------------------------------------
-// RegisterAllocator implementation.
-
-bool RegisterAllocator::IsReserved(Register reg) {
- return reg.is(cp) || reg.is(fp) || reg.is(sp) || reg.is(pc);
-}
-
-
-
-// The register allocator uses small integers to represent the
-// non-reserved assembler registers. The mapping is:
-//
-// r0 <-> 0
-// r1 <-> 1
-// r2 <-> 2
-// r3 <-> 3
-// r4 <-> 4
-// r5 <-> 5
-// r6 <-> 6
-// r7 <-> 7
-// r9 <-> 8
-// r10 <-> 9
-// ip <-> 10
-// lr <-> 11
-
-int RegisterAllocator::ToNumber(Register reg) {
- ASSERT(reg.is_valid() && !IsReserved(reg));
- const int kNumbers[] = {
- 0, // r0
- 1, // r1
- 2, // r2
- 3, // r3
- 4, // r4
- 5, // r5
- 6, // r6
- 7, // r7
- -1, // cp
- 8, // r9
- 9, // r10
- -1, // fp
- 10, // ip
- -1, // sp
- 11, // lr
- -1 // pc
- };
- return kNumbers[reg.code()];
-}
-
-
-Register RegisterAllocator::ToRegister(int num) {
- ASSERT(num >= 0 && num < kNumRegisters);
- const Register kRegisters[] =
- { r0, r1, r2, r3, r4, r5, r6, r7, r9, r10, ip, lr };
- return kRegisters[num];
-}
-
-
-void RegisterAllocator::Initialize() {
- Reset();
-}
-
-
-} } // namespace v8::internal
-
-#endif // V8_ARM_REGISTER_ALLOCATOR_ARM_INL_H_
diff --git a/src/arm/register-allocator-arm.cc b/src/arm/register-allocator-arm.cc
deleted file mode 100644
index 3b35574..0000000
--- a/src/arm/register-allocator-arm.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "v8.h"
-
-#if defined(V8_TARGET_ARCH_ARM)
-
-#include "codegen-inl.h"
-#include "register-allocator-inl.h"
-
-namespace v8 {
-namespace internal {
-
-// -------------------------------------------------------------------------
-// Result implementation.
-
-void Result::ToRegister() {
- UNIMPLEMENTED();
-}
-
-
-void Result::ToRegister(Register target) {
- UNIMPLEMENTED();
-}
-
-
-// -------------------------------------------------------------------------
-// RegisterAllocator implementation.
-
-Result RegisterAllocator::AllocateByteRegisterWithoutSpilling() {
- // No byte registers on ARM.
- UNREACHABLE();
- return Result();
-}
-
-
-} } // namespace v8::internal
-
-#endif // V8_TARGET_ARCH_ARM
diff --git a/src/arm/simulator-arm.cc b/src/arm/simulator-arm.cc
index 46797d9..da554c2 100644
--- a/src/arm/simulator-arm.cc
+++ b/src/arm/simulator-arm.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -67,6 +67,7 @@
Simulator* sim_;
int32_t GetRegisterValue(int regnum);
+ double GetRegisterPairDoubleValue(int regnum);
double GetVFPDoubleRegisterValue(int regnum);
bool GetValue(const char* desc, int32_t* value);
bool GetVFPSingleValue(const char* desc, float* value);
@@ -168,6 +169,11 @@
}
+double ArmDebugger::GetRegisterPairDoubleValue(int regnum) {
+ return sim_->get_double_from_register_pair(regnum);
+}
+
+
double ArmDebugger::GetVFPDoubleRegisterValue(int regnum) {
return sim_->get_double_from_d_register(regnum);
}
@@ -305,14 +311,22 @@
// Leave the debugger shell.
done = true;
} else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) {
- if (argc == 2) {
+ if (argc == 2 || (argc == 3 && strcmp(arg2, "fp") == 0)) {
int32_t value;
float svalue;
double dvalue;
if (strcmp(arg1, "all") == 0) {
for (int i = 0; i < kNumRegisters; i++) {
value = GetRegisterValue(i);
- PrintF("%3s: 0x%08x %10d\n", Registers::Name(i), value, value);
+ PrintF("%3s: 0x%08x %10d", Registers::Name(i), value, value);
+ if ((argc == 3 && strcmp(arg2, "fp") == 0) &&
+ i < 8 &&
+ (i % 2) == 0) {
+ dvalue = GetRegisterPairDoubleValue(i);
+ PrintF(" (%f)\n", dvalue);
+ } else {
+ PrintF("\n");
+ }
}
for (int i = 0; i < kNumVFPDoubleRegisters; i++) {
dvalue = GetVFPDoubleRegisterValue(i);
@@ -550,6 +564,7 @@
PrintF("print <register>\n");
PrintF(" print register content (alias 'p')\n");
PrintF(" use register name 'all' to print all registers\n");
+ PrintF(" add argument 'fp' to print register pair double values\n");
PrintF("printobject <register>\n");
PrintF(" print an object from a register (alias 'po')\n");
PrintF("flags\n");
@@ -873,6 +888,19 @@
}
+double Simulator::get_double_from_register_pair(int reg) {
+ ASSERT((reg >= 0) && (reg < num_registers) && ((reg % 2) == 0));
+
+ double dm_val = 0.0;
+ // Read the bits from the unsigned integer register_[] array
+ // into the double precision floating point value and return it.
+ char buffer[2 * sizeof(vfp_register[0])];
+ memcpy(buffer, ®isters_[reg], 2 * sizeof(registers_[0]));
+ memcpy(&dm_val, buffer, 2 * sizeof(registers_[0]));
+ return(dm_val);
+}
+
+
void Simulator::set_dw_register(int dreg, const int* dbl) {
ASSERT((dreg >= 0) && (dreg < num_d_registers));
registers_[dreg] = dbl[0];
@@ -938,12 +966,7 @@
// 2*sreg and 2*sreg+1.
char buffer[2 * sizeof(vfp_register[0])];
memcpy(buffer, &dbl, 2 * sizeof(vfp_register[0]));
-#ifndef BIG_ENDIAN_FLOATING_POINT
memcpy(&vfp_register[dreg * 2], buffer, 2 * sizeof(vfp_register[0]));
-#else
- memcpy(&vfp_register[dreg * 2], &buffer[4], sizeof(vfp_register[0]));
- memcpy(&vfp_register[dreg * 2 + 1], &buffer[0], sizeof(vfp_register[0]));
-#endif
}
@@ -980,12 +1003,7 @@
// Read the bits from the unsigned integer vfp_register[] array
// into the double precision floating point value and return it.
char buffer[2 * sizeof(vfp_register[0])];
-#ifdef BIG_ENDIAN_FLOATING_POINT
- memcpy(&buffer[0], &vfp_register[2 * dreg + 1], sizeof(vfp_register[0]));
- memcpy(&buffer[4], &vfp_register[2 * dreg], sizeof(vfp_register[0]));
-#else
memcpy(buffer, &vfp_register[2 * dreg], 2 * sizeof(vfp_register[0]));
-#endif
memcpy(&dm_val, buffer, 2 * sizeof(vfp_register[0]));
return(dm_val);
}
@@ -1504,36 +1522,34 @@
}
-// Addressing Mode 4 - Load and Store Multiple
-void Simulator::HandleRList(Instruction* instr, bool load) {
+void Simulator::ProcessPUW(Instruction* instr,
+ int num_regs,
+ int reg_size,
+ intptr_t* start_address,
+ intptr_t* end_address) {
int rn = instr->RnValue();
int32_t rn_val = get_register(rn);
- int rlist = instr->RlistValue();
- int num_regs = count_bits(rlist);
-
- intptr_t start_address = 0;
- intptr_t end_address = 0;
switch (instr->PUField()) {
case da_x: {
UNIMPLEMENTED();
break;
}
case ia_x: {
- start_address = rn_val;
- end_address = rn_val + (num_regs * 4) - 4;
- rn_val = rn_val + (num_regs * 4);
+ *start_address = rn_val;
+ *end_address = rn_val + (num_regs * reg_size) - reg_size;
+ rn_val = rn_val + (num_regs * reg_size);
break;
}
case db_x: {
- start_address = rn_val - (num_regs * 4);
- end_address = rn_val - 4;
- rn_val = start_address;
+ *start_address = rn_val - (num_regs * reg_size);
+ *end_address = rn_val - reg_size;
+ rn_val = *start_address;
break;
}
case ib_x: {
- start_address = rn_val + 4;
- end_address = rn_val + (num_regs * 4);
- rn_val = end_address;
+ *start_address = rn_val + reg_size;
+ *end_address = rn_val + (num_regs * reg_size);
+ rn_val = *end_address;
break;
}
default: {
@@ -1544,6 +1560,17 @@
if (instr->HasW()) {
set_register(rn, rn_val);
}
+}
+
+// Addressing Mode 4 - Load and Store Multiple
+void Simulator::HandleRList(Instruction* instr, bool load) {
+ int rlist = instr->RlistValue();
+ int num_regs = count_bits(rlist);
+
+ intptr_t start_address = 0;
+ intptr_t end_address = 0;
+ ProcessPUW(instr, num_regs, kPointerSize, &start_address, &end_address);
+
intptr_t* address = reinterpret_cast<intptr_t*>(start_address);
int reg = 0;
while (rlist != 0) {
@@ -1562,6 +1589,57 @@
}
+// Addressing Mode 6 - Load and Store Multiple Coprocessor registers.
+void Simulator::HandleVList(Instruction* instr) {
+ VFPRegPrecision precision =
+ (instr->SzValue() == 0) ? kSinglePrecision : kDoublePrecision;
+ int operand_size = (precision == kSinglePrecision) ? 4 : 8;
+
+ bool load = (instr->VLValue() == 0x1);
+
+ int vd;
+ int num_regs;
+ vd = instr->VFPDRegValue(precision);
+ if (precision == kSinglePrecision) {
+ num_regs = instr->Immed8Value();
+ } else {
+ num_regs = instr->Immed8Value() / 2;
+ }
+
+ intptr_t start_address = 0;
+ intptr_t end_address = 0;
+ ProcessPUW(instr, num_regs, operand_size, &start_address, &end_address);
+
+ intptr_t* address = reinterpret_cast<intptr_t*>(start_address);
+ for (int reg = vd; reg < vd + num_regs; reg++) {
+ if (precision == kSinglePrecision) {
+ if (load) {
+ set_s_register_from_sinteger(
+ reg, ReadW(reinterpret_cast<int32_t>(address), instr));
+ } else {
+ WriteW(reinterpret_cast<int32_t>(address),
+ get_sinteger_from_s_register(reg), instr);
+ }
+ address += 1;
+ } else {
+ if (load) {
+ set_s_register_from_sinteger(
+ 2 * reg, ReadW(reinterpret_cast<int32_t>(address), instr));
+ set_s_register_from_sinteger(
+ 2 * reg + 1, ReadW(reinterpret_cast<int32_t>(address + 1), instr));
+ } else {
+ WriteW(reinterpret_cast<int32_t>(address),
+ get_sinteger_from_s_register(2 * reg), instr);
+ WriteW(reinterpret_cast<int32_t>(address + 1),
+ get_sinteger_from_s_register(2 * reg + 1), instr);
+ }
+ address += 2;
+ }
+ }
+ ASSERT(reinterpret_cast<intptr_t>(address) - operand_size == end_address);
+}
+
+
// Calls into the V8 runtime are based on this very simple interface.
// Note: To be able to return two values from some calls the code in runtime.cc
// uses the ObjectPair which is essentially two 32-bit values stuffed into a
@@ -2945,9 +3023,17 @@
}
break;
}
+ case 0x4:
+ case 0x5:
+ case 0x6:
+ case 0x7:
+ case 0x9:
+ case 0xB:
+ // Load/store multiple single from memory: vldm/vstm.
+ HandleVList(instr);
+ break;
default:
UNIMPLEMENTED(); // Not used by V8.
- break;
}
} else if (instr->CoprocessorValue() == 0xB) {
switch (instr->OpcodeValue()) {
@@ -2994,9 +3080,14 @@
}
break;
}
+ case 0x4:
+ case 0x5:
+ case 0x9:
+ // Load/store multiple double from memory: vldm/vstm.
+ HandleVList(instr);
+ break;
default:
UNIMPLEMENTED(); // Not used by V8.
- break;
}
} else {
UNIMPLEMENTED(); // Not used by V8.
diff --git a/src/arm/simulator-arm.h b/src/arm/simulator-arm.h
index b7b1b68..a16cae5 100644
--- a/src/arm/simulator-arm.h
+++ b/src/arm/simulator-arm.h
@@ -1,4 +1,4 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -155,6 +155,7 @@
// instruction.
void set_register(int reg, int32_t value);
int32_t get_register(int reg) const;
+ double get_double_from_register_pair(int reg);
void set_dw_register(int dreg, const int* dbl);
// Support for VFP.
@@ -236,7 +237,13 @@
// Helper functions to decode common "addressing" modes
int32_t GetShiftRm(Instruction* instr, bool* carry_out);
int32_t GetImm(Instruction* instr, bool* carry_out);
+ void ProcessPUW(Instruction* instr,
+ int num_regs,
+ int operand_size,
+ intptr_t* start_address,
+ intptr_t* end_address);
void HandleRList(Instruction* instr, bool load);
+ void HandleVList(Instruction* inst);
void SoftwareInterrupt(Instruction* instr);
// Stop helper functions.
diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc
index 9936ac0..47d675b 100644
--- a/src/arm/stub-cache-arm.cc
+++ b/src/arm/stub-cache-arm.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2009 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -30,7 +30,7 @@
#if defined(V8_TARGET_ARCH_ARM)
#include "ic-inl.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "stub-cache.h"
namespace v8 {
@@ -953,7 +953,7 @@
Register fval,
Register scratch1,
Register scratch2) {
- if (masm->isolate()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
__ vmov(s0, ival);
__ add(scratch1, dst, Operand(wordoffset, LSL, 2));
@@ -2048,7 +2048,7 @@
// -- sp[argc * 4] : receiver
// -----------------------------------
- if (!masm()->isolate()->cpu_features()->IsSupported(VFP3)) {
+ if (!CpuFeatures::IsSupported(VFP3)) {
return heap()->undefined_value();
}
@@ -3509,7 +3509,7 @@
__ ldr(value, MemOperand(r3, key, LSL, 1));
break;
case kExternalFloatArray:
- if (masm()->isolate()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
__ add(r2, r3, Operand(key, LSL, 1));
__ vldr(s0, r2, 0);
@@ -3548,7 +3548,7 @@
// Now we can use r0 for the result as key is not needed any more.
__ mov(r0, r5);
- if (masm()->isolate()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
__ vmov(s0, value);
__ vcvt_f64_s32(d0, s0);
@@ -3563,7 +3563,7 @@
// The test is different for unsigned int values. Since we need
// the value to be in the range of a positive smi, we can't
// handle either of the top two bits being set in the value.
- if (masm()->isolate()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
Label box_int, done;
__ tst(value, Operand(0xC0000000));
@@ -3627,7 +3627,7 @@
} else if (array_type == kExternalFloatArray) {
// For the floating-point array type, we need to always allocate a
// HeapNumber.
- if (masm()->isolate()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
// Allocate a HeapNumber for the result. Don't use r0 and r1 as
// AllocateHeapNumber clobbers all registers - also when jumping due to
@@ -3820,7 +3820,7 @@
// The WebGL specification leaves the behavior of storing NaN and
// +/-Infinity into integer arrays basically undefined. For more
// reproducible behavior, convert these to zero.
- if (masm()->isolate()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
if (array_type == kExternalFloatArray) {
diff --git a/src/arm/virtual-frame-arm-inl.h b/src/arm/virtual-frame-arm-inl.h
deleted file mode 100644
index 6a7902a..0000000
--- a/src/arm/virtual-frame-arm-inl.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_VIRTUAL_FRAME_ARM_INL_H_
-#define V8_VIRTUAL_FRAME_ARM_INL_H_
-
-#include "assembler-arm.h"
-#include "virtual-frame-arm.h"
-
-namespace v8 {
-namespace internal {
-
-// These VirtualFrame methods should actually be in a virtual-frame-arm-inl.h
-// file if such a thing existed.
-MemOperand VirtualFrame::ParameterAt(int index) {
- // Index -1 corresponds to the receiver.
- ASSERT(-1 <= index); // -1 is the receiver.
- ASSERT(index <= parameter_count());
- return MemOperand(fp, (1 + parameter_count() - index) * kPointerSize);
-}
-
- // The receiver frame slot.
-MemOperand VirtualFrame::Receiver() {
- return ParameterAt(-1);
-}
-
-
-void VirtualFrame::Forget(int count) {
- SpillAll();
- LowerHeight(count);
-}
-
-} } // namespace v8::internal
-
-#endif // V8_VIRTUAL_FRAME_ARM_INL_H_
diff --git a/src/arm/virtual-frame-arm.cc b/src/arm/virtual-frame-arm.cc
deleted file mode 100644
index a852d6e..0000000
--- a/src/arm/virtual-frame-arm.cc
+++ /dev/null
@@ -1,843 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "v8.h"
-
-#if defined(V8_TARGET_ARCH_ARM)
-
-#include "codegen-inl.h"
-#include "register-allocator-inl.h"
-#include "scopes.h"
-#include "virtual-frame-inl.h"
-
-namespace v8 {
-namespace internal {
-
-#define __ ACCESS_MASM(masm())
-
-void VirtualFrame::PopToR1R0() {
- // Shuffle things around so the top of stack is in r0 and r1.
- MergeTOSTo(R0_R1_TOS);
- // Pop the two registers off the stack so they are detached from the frame.
- LowerHeight(2);
- top_of_stack_state_ = NO_TOS_REGISTERS;
-}
-
-
-void VirtualFrame::PopToR1() {
- // Shuffle things around so the top of stack is only in r1.
- MergeTOSTo(R1_TOS);
- // Pop the register off the stack so it is detached from the frame.
- LowerHeight(1);
- top_of_stack_state_ = NO_TOS_REGISTERS;
-}
-
-
-void VirtualFrame::PopToR0() {
- // Shuffle things around so the top of stack only in r0.
- MergeTOSTo(R0_TOS);
- // Pop the register off the stack so it is detached from the frame.
- LowerHeight(1);
- top_of_stack_state_ = NO_TOS_REGISTERS;
-}
-
-
-void VirtualFrame::MergeTo(const VirtualFrame* expected, Condition cond) {
- if (Equals(expected)) return;
- ASSERT((expected->tos_known_smi_map_ & tos_known_smi_map_) ==
- expected->tos_known_smi_map_);
- ASSERT(expected->IsCompatibleWith(this));
- MergeTOSTo(expected->top_of_stack_state_, cond);
- ASSERT(register_allocation_map_ == expected->register_allocation_map_);
-}
-
-
-void VirtualFrame::MergeTo(VirtualFrame* expected, Condition cond) {
- if (Equals(expected)) return;
- tos_known_smi_map_ &= expected->tos_known_smi_map_;
- MergeTOSTo(expected->top_of_stack_state_, cond);
- ASSERT(register_allocation_map_ == expected->register_allocation_map_);
-}
-
-
-void VirtualFrame::MergeTOSTo(
- VirtualFrame::TopOfStack expected_top_of_stack_state, Condition cond) {
-#define CASE_NUMBER(a, b) ((a) * TOS_STATES + (b))
- switch (CASE_NUMBER(top_of_stack_state_, expected_top_of_stack_state)) {
- case CASE_NUMBER(NO_TOS_REGISTERS, NO_TOS_REGISTERS):
- break;
- case CASE_NUMBER(NO_TOS_REGISTERS, R0_TOS):
- __ pop(r0, cond);
- break;
- case CASE_NUMBER(NO_TOS_REGISTERS, R1_TOS):
- __ pop(r1, cond);
- break;
- case CASE_NUMBER(NO_TOS_REGISTERS, R0_R1_TOS):
- __ pop(r0, cond);
- __ pop(r1, cond);
- break;
- case CASE_NUMBER(NO_TOS_REGISTERS, R1_R0_TOS):
- __ pop(r1, cond);
- __ pop(r0, cond);
- break;
- case CASE_NUMBER(R0_TOS, NO_TOS_REGISTERS):
- __ push(r0, cond);
- break;
- case CASE_NUMBER(R0_TOS, R0_TOS):
- break;
- case CASE_NUMBER(R0_TOS, R1_TOS):
- __ mov(r1, r0, LeaveCC, cond);
- break;
- case CASE_NUMBER(R0_TOS, R0_R1_TOS):
- __ pop(r1, cond);
- break;
- case CASE_NUMBER(R0_TOS, R1_R0_TOS):
- __ mov(r1, r0, LeaveCC, cond);
- __ pop(r0, cond);
- break;
- case CASE_NUMBER(R1_TOS, NO_TOS_REGISTERS):
- __ push(r1, cond);
- break;
- case CASE_NUMBER(R1_TOS, R0_TOS):
- __ mov(r0, r1, LeaveCC, cond);
- break;
- case CASE_NUMBER(R1_TOS, R1_TOS):
- break;
- case CASE_NUMBER(R1_TOS, R0_R1_TOS):
- __ mov(r0, r1, LeaveCC, cond);
- __ pop(r1, cond);
- break;
- case CASE_NUMBER(R1_TOS, R1_R0_TOS):
- __ pop(r0, cond);
- break;
- case CASE_NUMBER(R0_R1_TOS, NO_TOS_REGISTERS):
- __ Push(r1, r0, cond);
- break;
- case CASE_NUMBER(R0_R1_TOS, R0_TOS):
- __ push(r1, cond);
- break;
- case CASE_NUMBER(R0_R1_TOS, R1_TOS):
- __ push(r1, cond);
- __ mov(r1, r0, LeaveCC, cond);
- break;
- case CASE_NUMBER(R0_R1_TOS, R0_R1_TOS):
- break;
- case CASE_NUMBER(R0_R1_TOS, R1_R0_TOS):
- __ Swap(r0, r1, ip, cond);
- break;
- case CASE_NUMBER(R1_R0_TOS, NO_TOS_REGISTERS):
- __ Push(r0, r1, cond);
- break;
- case CASE_NUMBER(R1_R0_TOS, R0_TOS):
- __ push(r0, cond);
- __ mov(r0, r1, LeaveCC, cond);
- break;
- case CASE_NUMBER(R1_R0_TOS, R1_TOS):
- __ push(r0, cond);
- break;
- case CASE_NUMBER(R1_R0_TOS, R0_R1_TOS):
- __ Swap(r0, r1, ip, cond);
- break;
- case CASE_NUMBER(R1_R0_TOS, R1_R0_TOS):
- break;
- default:
- UNREACHABLE();
-#undef CASE_NUMBER
- }
- // A conditional merge will be followed by a conditional branch and the
- // fall-through code will have an unchanged virtual frame state. If the
- // merge is unconditional ('al'ways) then it might be followed by a fall
- // through. We need to update the virtual frame state to match the code we
- // are falling into. The final case is an unconditional merge followed by an
- // unconditional branch, in which case it doesn't matter what we do to the
- // virtual frame state, because the virtual frame will be invalidated.
- if (cond == al) {
- top_of_stack_state_ = expected_top_of_stack_state;
- }
-}
-
-
-void VirtualFrame::Enter() {
- Comment cmnt(masm(), "[ Enter JS frame");
-
-#ifdef DEBUG
- // Verify that r1 contains a JS function. The following code relies
- // on r2 being available for use.
- if (FLAG_debug_code) {
- Label map_check, done;
- __ tst(r1, Operand(kSmiTagMask));
- __ b(ne, &map_check);
- __ stop("VirtualFrame::Enter - r1 is not a function (smi check).");
- __ bind(&map_check);
- __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
- __ b(eq, &done);
- __ stop("VirtualFrame::Enter - r1 is not a function (map check).");
- __ bind(&done);
- }
-#endif // DEBUG
-
- // We are about to push four values to the frame.
- Adjust(4);
- __ stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit());
- // Adjust FP to point to saved FP.
- __ add(fp, sp, Operand(2 * kPointerSize));
-}
-
-
-void VirtualFrame::Exit() {
- Comment cmnt(masm(), "[ Exit JS frame");
- // Record the location of the JS exit code for patching when setting
- // break point.
- __ RecordJSReturn();
-
- // Drop the execution stack down to the frame pointer and restore the caller
- // frame pointer and return address.
- __ mov(sp, fp);
- __ ldm(ia_w, sp, fp.bit() | lr.bit());
-}
-
-
-void VirtualFrame::AllocateStackSlots() {
- int count = local_count();
- if (count > 0) {
- Comment cmnt(masm(), "[ Allocate space for locals");
- Adjust(count);
- // Initialize stack slots with 'undefined' value.
- __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
- __ LoadRoot(r2, Heap::kStackLimitRootIndex);
- if (count < kLocalVarBound) {
- // For less locals the unrolled loop is more compact.
- for (int i = 0; i < count; i++) {
- __ push(ip);
- }
- } else {
- // For more locals a loop in generated code is more compact.
- Label alloc_locals_loop;
- __ mov(r1, Operand(count));
- __ bind(&alloc_locals_loop);
- __ push(ip);
- __ sub(r1, r1, Operand(1), SetCC);
- __ b(ne, &alloc_locals_loop);
- }
- } else {
- __ LoadRoot(r2, Heap::kStackLimitRootIndex);
- }
- // Check the stack for overflow or a break request.
- masm()->cmp(sp, Operand(r2));
- StackCheckStub stub;
- // Call the stub if lower.
- masm()->mov(ip,
- Operand(reinterpret_cast<intptr_t>(stub.GetCode().location()),
- RelocInfo::CODE_TARGET),
- LeaveCC,
- lo);
- masm()->Call(ip, lo);
-}
-
-
-
-void VirtualFrame::PushReceiverSlotAddress() {
- UNIMPLEMENTED();
-}
-
-
-void VirtualFrame::PushTryHandler(HandlerType type) {
- // Grow the expression stack by handler size less one (the return
- // address in lr is already counted by a call instruction).
- Adjust(kHandlerSize - 1);
- __ PushTryHandler(IN_JAVASCRIPT, type);
-}
-
-
-void VirtualFrame::CallJSFunction(int arg_count) {
- // InvokeFunction requires function in r1.
- PopToR1();
- SpillAll();
-
- // +1 for receiver.
- Forget(arg_count + 1);
- ASSERT(cgen()->HasValidEntryRegisters());
- ParameterCount count(arg_count);
- __ InvokeFunction(r1, count, CALL_FUNCTION);
- // Restore the context.
- __ ldr(cp, Context());
-}
-
-
-void VirtualFrame::CallRuntime(const Runtime::Function* f, int arg_count) {
- SpillAll();
- Forget(arg_count);
- ASSERT(cgen()->HasValidEntryRegisters());
- __ CallRuntime(f, arg_count);
-}
-
-
-void VirtualFrame::CallRuntime(Runtime::FunctionId id, int arg_count) {
- SpillAll();
- Forget(arg_count);
- ASSERT(cgen()->HasValidEntryRegisters());
- __ CallRuntime(id, arg_count);
-}
-
-
-#ifdef ENABLE_DEBUGGER_SUPPORT
-void VirtualFrame::DebugBreak() {
- ASSERT(cgen()->HasValidEntryRegisters());
- __ DebugBreak();
-}
-#endif
-
-
-void VirtualFrame::InvokeBuiltin(Builtins::JavaScript id,
- InvokeJSFlags flags,
- int arg_count) {
- Forget(arg_count);
- __ InvokeBuiltin(id, flags);
-}
-
-
-void VirtualFrame::CallLoadIC(Handle<String> name, RelocInfo::Mode mode) {
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- Builtins::kLoadIC_Initialize));
- PopToR0();
- SpillAll();
- __ mov(r2, Operand(name));
- CallCodeObject(ic, mode, 0);
-}
-
-
-void VirtualFrame::CallStoreIC(Handle<String> name,
- bool is_contextual,
- StrictModeFlag strict_mode) {
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- (strict_mode == kStrictMode) ? Builtins::kStoreIC_Initialize_Strict
- : Builtins::kStoreIC_Initialize));
- PopToR0();
- RelocInfo::Mode mode;
- if (is_contextual) {
- SpillAll();
- __ ldr(r1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
- mode = RelocInfo::CODE_TARGET_CONTEXT;
- } else {
- EmitPop(r1);
- SpillAll();
- mode = RelocInfo::CODE_TARGET;
- }
- __ mov(r2, Operand(name));
- CallCodeObject(ic, mode, 0);
-}
-
-
-void VirtualFrame::CallKeyedLoadIC() {
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- Builtins::kKeyedLoadIC_Initialize));
- PopToR1R0();
- SpillAll();
- CallCodeObject(ic, RelocInfo::CODE_TARGET, 0);
-}
-
-
-void VirtualFrame::CallKeyedStoreIC(StrictModeFlag strict_mode) {
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- (strict_mode == kStrictMode) ? Builtins::kKeyedStoreIC_Initialize_Strict
- : Builtins::kKeyedStoreIC_Initialize));
- PopToR1R0();
- SpillAll();
- EmitPop(r2);
- CallCodeObject(ic, RelocInfo::CODE_TARGET, 0);
-}
-
-
-void VirtualFrame::CallCodeObject(Handle<Code> code,
- RelocInfo::Mode rmode,
- int dropped_args) {
- switch (code->kind()) {
- case Code::CALL_IC:
- case Code::KEYED_CALL_IC:
- case Code::FUNCTION:
- break;
- case Code::KEYED_LOAD_IC:
- case Code::LOAD_IC:
- case Code::KEYED_STORE_IC:
- case Code::STORE_IC:
- ASSERT(dropped_args == 0);
- break;
- case Code::BUILTIN:
- ASSERT(*code == Isolate::Current()->builtins()->builtin(
- Builtins::kJSConstructCall));
- break;
- default:
- UNREACHABLE();
- break;
- }
- Forget(dropped_args);
- ASSERT(cgen()->HasValidEntryRegisters());
- __ Call(code, rmode);
-}
-
-
-// NO_TOS_REGISTERS, R0_TOS, R1_TOS, R1_R0_TOS, R0_R1_TOS.
-const bool VirtualFrame::kR0InUse[TOS_STATES] =
- { false, true, false, true, true };
-const bool VirtualFrame::kR1InUse[TOS_STATES] =
- { false, false, true, true, true };
-const int VirtualFrame::kVirtualElements[TOS_STATES] =
- { 0, 1, 1, 2, 2 };
-const Register VirtualFrame::kTopRegister[TOS_STATES] =
- { r0, r0, r1, r1, r0 };
-const Register VirtualFrame::kBottomRegister[TOS_STATES] =
- { r0, r0, r1, r0, r1 };
-const Register VirtualFrame::kAllocatedRegisters[
- VirtualFrame::kNumberOfAllocatedRegisters] = { r2, r3, r4, r5, r6 };
-// Popping is done by the transition implied by kStateAfterPop. Of course if
-// there were no stack slots allocated to registers then the physical SP must
-// be adjusted.
-const VirtualFrame::TopOfStack VirtualFrame::kStateAfterPop[TOS_STATES] =
- { NO_TOS_REGISTERS, NO_TOS_REGISTERS, NO_TOS_REGISTERS, R0_TOS, R1_TOS };
-// Pushing is done by the transition implied by kStateAfterPush. Of course if
-// the maximum number of registers was already allocated to the top of stack
-// slots then one register must be physically pushed onto the stack.
-const VirtualFrame::TopOfStack VirtualFrame::kStateAfterPush[TOS_STATES] =
- { R0_TOS, R1_R0_TOS, R0_R1_TOS, R0_R1_TOS, R1_R0_TOS };
-
-
-void VirtualFrame::Drop(int count) {
- ASSERT(count >= 0);
- ASSERT(height() >= count);
- // Discard elements from the virtual frame and free any registers.
- int num_virtual_elements = kVirtualElements[top_of_stack_state_];
- while (num_virtual_elements > 0) {
- Pop();
- num_virtual_elements--;
- count--;
- if (count == 0) return;
- }
- if (count == 0) return;
- __ add(sp, sp, Operand(count * kPointerSize));
- LowerHeight(count);
-}
-
-
-void VirtualFrame::Pop() {
- if (top_of_stack_state_ == NO_TOS_REGISTERS) {
- __ add(sp, sp, Operand(kPointerSize));
- } else {
- top_of_stack_state_ = kStateAfterPop[top_of_stack_state_];
- }
- LowerHeight(1);
-}
-
-
-void VirtualFrame::EmitPop(Register reg) {
- ASSERT(!is_used(RegisterAllocator::ToNumber(reg)));
- if (top_of_stack_state_ == NO_TOS_REGISTERS) {
- __ pop(reg);
- } else {
- __ mov(reg, kTopRegister[top_of_stack_state_]);
- top_of_stack_state_ = kStateAfterPop[top_of_stack_state_];
- }
- LowerHeight(1);
-}
-
-
-void VirtualFrame::SpillAllButCopyTOSToR0() {
- switch (top_of_stack_state_) {
- case NO_TOS_REGISTERS:
- __ ldr(r0, MemOperand(sp, 0));
- break;
- case R0_TOS:
- __ push(r0);
- break;
- case R1_TOS:
- __ push(r1);
- __ mov(r0, r1);
- break;
- case R0_R1_TOS:
- __ Push(r1, r0);
- break;
- case R1_R0_TOS:
- __ Push(r0, r1);
- __ mov(r0, r1);
- break;
- default:
- UNREACHABLE();
- }
- top_of_stack_state_ = NO_TOS_REGISTERS;
-}
-
-
-void VirtualFrame::SpillAllButCopyTOSToR1() {
- switch (top_of_stack_state_) {
- case NO_TOS_REGISTERS:
- __ ldr(r1, MemOperand(sp, 0));
- break;
- case R0_TOS:
- __ push(r0);
- __ mov(r1, r0);
- break;
- case R1_TOS:
- __ push(r1);
- break;
- case R0_R1_TOS:
- __ Push(r1, r0);
- __ mov(r1, r0);
- break;
- case R1_R0_TOS:
- __ Push(r0, r1);
- break;
- default:
- UNREACHABLE();
- }
- top_of_stack_state_ = NO_TOS_REGISTERS;
-}
-
-
-void VirtualFrame::SpillAllButCopyTOSToR1R0() {
- switch (top_of_stack_state_) {
- case NO_TOS_REGISTERS:
- __ ldr(r1, MemOperand(sp, 0));
- __ ldr(r0, MemOperand(sp, kPointerSize));
- break;
- case R0_TOS:
- __ push(r0);
- __ mov(r1, r0);
- __ ldr(r0, MemOperand(sp, kPointerSize));
- break;
- case R1_TOS:
- __ push(r1);
- __ ldr(r0, MemOperand(sp, kPointerSize));
- break;
- case R0_R1_TOS:
- __ Push(r1, r0);
- __ Swap(r0, r1, ip);
- break;
- case R1_R0_TOS:
- __ Push(r0, r1);
- break;
- default:
- UNREACHABLE();
- }
- top_of_stack_state_ = NO_TOS_REGISTERS;
-}
-
-
-Register VirtualFrame::Peek() {
- AssertIsNotSpilled();
- if (top_of_stack_state_ == NO_TOS_REGISTERS) {
- top_of_stack_state_ = kStateAfterPush[top_of_stack_state_];
- Register answer = kTopRegister[top_of_stack_state_];
- __ pop(answer);
- return answer;
- } else {
- return kTopRegister[top_of_stack_state_];
- }
-}
-
-
-Register VirtualFrame::Peek2() {
- AssertIsNotSpilled();
- switch (top_of_stack_state_) {
- case NO_TOS_REGISTERS:
- case R0_TOS:
- case R0_R1_TOS:
- MergeTOSTo(R0_R1_TOS);
- return r1;
- case R1_TOS:
- case R1_R0_TOS:
- MergeTOSTo(R1_R0_TOS);
- return r0;
- default:
- UNREACHABLE();
- return no_reg;
- }
-}
-
-
-void VirtualFrame::Dup() {
- if (SpilledScope::is_spilled()) {
- __ ldr(ip, MemOperand(sp, 0));
- __ push(ip);
- } else {
- switch (top_of_stack_state_) {
- case NO_TOS_REGISTERS:
- __ ldr(r0, MemOperand(sp, 0));
- top_of_stack_state_ = R0_TOS;
- break;
- case R0_TOS:
- __ mov(r1, r0);
- // r0 and r1 contains the same value. Prefer state with r0 holding TOS.
- top_of_stack_state_ = R0_R1_TOS;
- break;
- case R1_TOS:
- __ mov(r0, r1);
- // r0 and r1 contains the same value. Prefer state with r0 holding TOS.
- top_of_stack_state_ = R0_R1_TOS;
- break;
- case R0_R1_TOS:
- __ push(r1);
- __ mov(r1, r0);
- // r0 and r1 contains the same value. Prefer state with r0 holding TOS.
- top_of_stack_state_ = R0_R1_TOS;
- break;
- case R1_R0_TOS:
- __ push(r0);
- __ mov(r0, r1);
- // r0 and r1 contains the same value. Prefer state with r0 holding TOS.
- top_of_stack_state_ = R0_R1_TOS;
- break;
- default:
- UNREACHABLE();
- }
- }
- RaiseHeight(1, tos_known_smi_map_ & 1);
-}
-
-
-void VirtualFrame::Dup2() {
- if (SpilledScope::is_spilled()) {
- __ ldr(ip, MemOperand(sp, kPointerSize));
- __ push(ip);
- __ ldr(ip, MemOperand(sp, kPointerSize));
- __ push(ip);
- } else {
- switch (top_of_stack_state_) {
- case NO_TOS_REGISTERS:
- __ ldr(r0, MemOperand(sp, 0));
- __ ldr(r1, MemOperand(sp, kPointerSize));
- top_of_stack_state_ = R0_R1_TOS;
- break;
- case R0_TOS:
- __ push(r0);
- __ ldr(r1, MemOperand(sp, kPointerSize));
- top_of_stack_state_ = R0_R1_TOS;
- break;
- case R1_TOS:
- __ push(r1);
- __ ldr(r0, MemOperand(sp, kPointerSize));
- top_of_stack_state_ = R1_R0_TOS;
- break;
- case R0_R1_TOS:
- __ Push(r1, r0);
- top_of_stack_state_ = R0_R1_TOS;
- break;
- case R1_R0_TOS:
- __ Push(r0, r1);
- top_of_stack_state_ = R1_R0_TOS;
- break;
- default:
- UNREACHABLE();
- }
- }
- RaiseHeight(2, tos_known_smi_map_ & 3);
-}
-
-
-Register VirtualFrame::PopToRegister(Register but_not_to_this_one) {
- ASSERT(but_not_to_this_one.is(r0) ||
- but_not_to_this_one.is(r1) ||
- but_not_to_this_one.is(no_reg));
- LowerHeight(1);
- if (top_of_stack_state_ == NO_TOS_REGISTERS) {
- if (but_not_to_this_one.is(r0)) {
- __ pop(r1);
- return r1;
- } else {
- __ pop(r0);
- return r0;
- }
- } else {
- Register answer = kTopRegister[top_of_stack_state_];
- ASSERT(!answer.is(but_not_to_this_one));
- top_of_stack_state_ = kStateAfterPop[top_of_stack_state_];
- return answer;
- }
-}
-
-
-void VirtualFrame::EnsureOneFreeTOSRegister() {
- if (kVirtualElements[top_of_stack_state_] == kMaxTOSRegisters) {
- __ push(kBottomRegister[top_of_stack_state_]);
- top_of_stack_state_ = kStateAfterPush[top_of_stack_state_];
- top_of_stack_state_ = kStateAfterPop[top_of_stack_state_];
- }
- ASSERT(kVirtualElements[top_of_stack_state_] != kMaxTOSRegisters);
-}
-
-
-void VirtualFrame::EmitPush(Register reg, TypeInfo info) {
- RaiseHeight(1, info.IsSmi() ? 1 : 0);
- if (reg.is(cp)) {
- // If we are pushing cp then we are about to make a call and things have to
- // be pushed to the physical stack. There's nothing to be gained my moving
- // to a TOS register and then pushing that, we might as well push to the
- // physical stack immediately.
- MergeTOSTo(NO_TOS_REGISTERS);
- __ push(reg);
- return;
- }
- if (SpilledScope::is_spilled()) {
- ASSERT(top_of_stack_state_ == NO_TOS_REGISTERS);
- __ push(reg);
- return;
- }
- if (top_of_stack_state_ == NO_TOS_REGISTERS) {
- if (reg.is(r0)) {
- top_of_stack_state_ = R0_TOS;
- return;
- }
- if (reg.is(r1)) {
- top_of_stack_state_ = R1_TOS;
- return;
- }
- }
- EnsureOneFreeTOSRegister();
- top_of_stack_state_ = kStateAfterPush[top_of_stack_state_];
- Register dest = kTopRegister[top_of_stack_state_];
- __ Move(dest, reg);
-}
-
-
-void VirtualFrame::SetElementAt(Register reg, int this_far_down) {
- if (this_far_down < kTOSKnownSmiMapSize) {
- tos_known_smi_map_ &= ~(1 << this_far_down);
- }
- if (this_far_down == 0) {
- Pop();
- Register dest = GetTOSRegister();
- if (dest.is(reg)) {
- // We already popped one item off the top of the stack. If the only
- // free register is the one we were asked to push then we have been
- // asked to push a register that was already in use, which cannot
- // happen. It therefore folows that there are two free TOS registers:
- ASSERT(top_of_stack_state_ == NO_TOS_REGISTERS);
- dest = dest.is(r0) ? r1 : r0;
- }
- __ mov(dest, reg);
- EmitPush(dest);
- } else if (this_far_down == 1) {
- int virtual_elements = kVirtualElements[top_of_stack_state_];
- if (virtual_elements < 2) {
- __ str(reg, ElementAt(this_far_down));
- } else {
- ASSERT(virtual_elements == 2);
- ASSERT(!reg.is(r0));
- ASSERT(!reg.is(r1));
- Register dest = kBottomRegister[top_of_stack_state_];
- __ mov(dest, reg);
- }
- } else {
- ASSERT(this_far_down >= 2);
- ASSERT(kVirtualElements[top_of_stack_state_] <= 2);
- __ str(reg, ElementAt(this_far_down));
- }
-}
-
-
-Register VirtualFrame::GetTOSRegister() {
- if (SpilledScope::is_spilled()) return r0;
-
- EnsureOneFreeTOSRegister();
- return kTopRegister[kStateAfterPush[top_of_stack_state_]];
-}
-
-
-void VirtualFrame::EmitPush(Operand operand, TypeInfo info) {
- RaiseHeight(1, info.IsSmi() ? 1 : 0);
- if (SpilledScope::is_spilled()) {
- __ mov(r0, operand);
- __ push(r0);
- return;
- }
- EnsureOneFreeTOSRegister();
- top_of_stack_state_ = kStateAfterPush[top_of_stack_state_];
- __ mov(kTopRegister[top_of_stack_state_], operand);
-}
-
-
-void VirtualFrame::EmitPush(MemOperand operand, TypeInfo info) {
- RaiseHeight(1, info.IsSmi() ? 1 : 0);
- if (SpilledScope::is_spilled()) {
- __ ldr(r0, operand);
- __ push(r0);
- return;
- }
- EnsureOneFreeTOSRegister();
- top_of_stack_state_ = kStateAfterPush[top_of_stack_state_];
- __ ldr(kTopRegister[top_of_stack_state_], operand);
-}
-
-
-void VirtualFrame::EmitPushRoot(Heap::RootListIndex index) {
- RaiseHeight(1, 0);
- if (SpilledScope::is_spilled()) {
- __ LoadRoot(r0, index);
- __ push(r0);
- return;
- }
- EnsureOneFreeTOSRegister();
- top_of_stack_state_ = kStateAfterPush[top_of_stack_state_];
- __ LoadRoot(kTopRegister[top_of_stack_state_], index);
-}
-
-
-void VirtualFrame::EmitPushMultiple(int count, int src_regs) {
- ASSERT(SpilledScope::is_spilled());
- Adjust(count);
- __ stm(db_w, sp, src_regs);
-}
-
-
-void VirtualFrame::SpillAll() {
- switch (top_of_stack_state_) {
- case R1_R0_TOS:
- masm()->push(r0);
- // Fall through.
- case R1_TOS:
- masm()->push(r1);
- top_of_stack_state_ = NO_TOS_REGISTERS;
- break;
- case R0_R1_TOS:
- masm()->push(r1);
- // Fall through.
- case R0_TOS:
- masm()->push(r0);
- top_of_stack_state_ = NO_TOS_REGISTERS;
- // Fall through.
- case NO_TOS_REGISTERS:
- break;
- default:
- UNREACHABLE();
- break;
- }
- ASSERT(register_allocation_map_ == 0); // Not yet implemented.
-}
-
-#undef __
-
-} } // namespace v8::internal
-
-#endif // V8_TARGET_ARCH_ARM
diff --git a/src/arm/virtual-frame-arm.h b/src/arm/virtual-frame-arm.h
deleted file mode 100644
index 6d67e70..0000000
--- a/src/arm/virtual-frame-arm.h
+++ /dev/null
@@ -1,523 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_ARM_VIRTUAL_FRAME_ARM_H_
-#define V8_ARM_VIRTUAL_FRAME_ARM_H_
-
-#include "register-allocator.h"
-
-namespace v8 {
-namespace internal {
-
-// This dummy class is only used to create invalid virtual frames.
-extern class InvalidVirtualFrameInitializer {}* kInvalidVirtualFrameInitializer;
-
-
-// -------------------------------------------------------------------------
-// Virtual frames
-//
-// The virtual frame is an abstraction of the physical stack frame. It
-// encapsulates the parameters, frame-allocated locals, and the expression
-// stack. It supports push/pop operations on the expression stack, as well
-// as random access to the expression stack elements, locals, and
-// parameters.
-
-class VirtualFrame : public ZoneObject {
- public:
- class RegisterAllocationScope;
- // A utility class to introduce a scope where the virtual frame is
- // expected to remain spilled. The constructor spills the code
- // generator's current frame, and keeps it spilled.
- class SpilledScope BASE_EMBEDDED {
- public:
- explicit SpilledScope(VirtualFrame* frame)
- : old_is_spilled_(
- Isolate::Current()->is_virtual_frame_in_spilled_scope()) {
- if (frame != NULL) {
- if (!old_is_spilled_) {
- frame->SpillAll();
- } else {
- frame->AssertIsSpilled();
- }
- }
- Isolate::Current()->set_is_virtual_frame_in_spilled_scope(true);
- }
- ~SpilledScope() {
- Isolate::Current()->set_is_virtual_frame_in_spilled_scope(
- old_is_spilled_);
- }
- static bool is_spilled() {
- return Isolate::Current()->is_virtual_frame_in_spilled_scope();
- }
-
- private:
- int old_is_spilled_;
-
- SpilledScope() { }
-
- friend class RegisterAllocationScope;
- };
-
- class RegisterAllocationScope BASE_EMBEDDED {
- public:
- // A utility class to introduce a scope where the virtual frame
- // is not spilled, ie. where register allocation occurs. Eventually
- // when RegisterAllocationScope is ubiquitous it can be removed
- // along with the (by then unused) SpilledScope class.
- inline explicit RegisterAllocationScope(CodeGenerator* cgen);
- inline ~RegisterAllocationScope();
-
- private:
- CodeGenerator* cgen_;
- bool old_is_spilled_;
-
- RegisterAllocationScope() { }
- };
-
- // An illegal index into the virtual frame.
- static const int kIllegalIndex = -1;
-
- // Construct an initial virtual frame on entry to a JS function.
- inline VirtualFrame();
-
- // Construct an invalid virtual frame, used by JumpTargets.
- inline VirtualFrame(InvalidVirtualFrameInitializer* dummy);
-
- // Construct a virtual frame as a clone of an existing one.
- explicit inline VirtualFrame(VirtualFrame* original);
-
- inline CodeGenerator* cgen() const;
- inline MacroAssembler* masm();
-
- // The number of elements on the virtual frame.
- int element_count() const { return element_count_; }
-
- // The height of the virtual expression stack.
- inline int height() const;
-
- bool is_used(int num) {
- switch (num) {
- case 0: { // r0.
- return kR0InUse[top_of_stack_state_];
- }
- case 1: { // r1.
- return kR1InUse[top_of_stack_state_];
- }
- case 2:
- case 3:
- case 4:
- case 5:
- case 6: { // r2 to r6.
- ASSERT(num - kFirstAllocatedRegister < kNumberOfAllocatedRegisters);
- ASSERT(num >= kFirstAllocatedRegister);
- if ((register_allocation_map_ &
- (1 << (num - kFirstAllocatedRegister))) == 0) {
- return false;
- } else {
- return true;
- }
- }
- default: {
- ASSERT(num < kFirstAllocatedRegister ||
- num >= kFirstAllocatedRegister + kNumberOfAllocatedRegisters);
- return false;
- }
- }
- }
-
- // Add extra in-memory elements to the top of the frame to match an actual
- // frame (eg, the frame after an exception handler is pushed). No code is
- // emitted.
- void Adjust(int count);
-
- // Forget elements from the top of the frame to match an actual frame (eg,
- // the frame after a runtime call). No code is emitted except to bring the
- // frame to a spilled state.
- void Forget(int count);
-
- // Spill all values from the frame to memory.
- void SpillAll();
-
- void AssertIsSpilled() const {
- ASSERT(top_of_stack_state_ == NO_TOS_REGISTERS);
- ASSERT(register_allocation_map_ == 0);
- }
-
- void AssertIsNotSpilled() {
- ASSERT(!SpilledScope::is_spilled());
- }
-
- // Spill all occurrences of a specific register from the frame.
- void Spill(Register reg) {
- UNIMPLEMENTED();
- }
-
- // Spill all occurrences of an arbitrary register if possible. Return the
- // register spilled or no_reg if it was not possible to free any register
- // (ie, they all have frame-external references). Unimplemented.
- Register SpillAnyRegister();
-
- // Make this virtual frame have a state identical to an expected virtual
- // frame. As a side effect, code may be emitted to make this frame match
- // the expected one.
- void MergeTo(VirtualFrame* expected, Condition cond = al);
- void MergeTo(const VirtualFrame* expected, Condition cond = al);
-
- // Checks whether this frame can be branched to by the other frame.
- bool IsCompatibleWith(const VirtualFrame* other) const {
- return (tos_known_smi_map_ & (~other->tos_known_smi_map_)) == 0;
- }
-
- inline void ForgetTypeInfo() {
- tos_known_smi_map_ = 0;
- }
-
- // Detach a frame from its code generator, perhaps temporarily. This
- // tells the register allocator that it is free to use frame-internal
- // registers. Used when the code generator's frame is switched from this
- // one to NULL by an unconditional jump.
- void DetachFromCodeGenerator() {
- }
-
- // (Re)attach a frame to its code generator. This informs the register
- // allocator that the frame-internal register references are active again.
- // Used when a code generator's frame is switched from NULL to this one by
- // binding a label.
- void AttachToCodeGenerator() {
- }
-
- // Emit code for the physical JS entry and exit frame sequences. After
- // calling Enter, the virtual frame is ready for use; and after calling
- // Exit it should not be used. Note that Enter does not allocate space in
- // the physical frame for storing frame-allocated locals.
- void Enter();
- void Exit();
-
- // Prepare for returning from the frame by elements in the virtual frame. This
- // avoids generating unnecessary merge code when jumping to the
- // shared return site. No spill code emitted. Value to return should be in r0.
- inline void PrepareForReturn();
-
- // Number of local variables after when we use a loop for allocating.
- static const int kLocalVarBound = 5;
-
- // Allocate and initialize the frame-allocated locals.
- void AllocateStackSlots();
-
- // The current top of the expression stack as an assembly operand.
- MemOperand Top() {
- AssertIsSpilled();
- return MemOperand(sp, 0);
- }
-
- // An element of the expression stack as an assembly operand.
- MemOperand ElementAt(int index) {
- int adjusted_index = index - kVirtualElements[top_of_stack_state_];
- ASSERT(adjusted_index >= 0);
- return MemOperand(sp, adjusted_index * kPointerSize);
- }
-
- bool KnownSmiAt(int index) {
- if (index >= kTOSKnownSmiMapSize) return false;
- return (tos_known_smi_map_ & (1 << index)) != 0;
- }
-
- // A frame-allocated local as an assembly operand.
- inline MemOperand LocalAt(int index);
-
- // Push the address of the receiver slot on the frame.
- void PushReceiverSlotAddress();
-
- // The function frame slot.
- MemOperand Function() { return MemOperand(fp, kFunctionOffset); }
-
- // The context frame slot.
- MemOperand Context() { return MemOperand(fp, kContextOffset); }
-
- // A parameter as an assembly operand.
- inline MemOperand ParameterAt(int index);
-
- // The receiver frame slot.
- inline MemOperand Receiver();
-
- // Push a try-catch or try-finally handler on top of the virtual frame.
- void PushTryHandler(HandlerType type);
-
- // Call stub given the number of arguments it expects on (and
- // removes from) the stack.
- inline void CallStub(CodeStub* stub, int arg_count);
-
- // Call JS function from top of the stack with arguments
- // taken from the stack.
- void CallJSFunction(int arg_count);
-
- // Call runtime given the number of arguments expected on (and
- // removed from) the stack.
- void CallRuntime(const Runtime::Function* f, int arg_count);
- void CallRuntime(Runtime::FunctionId id, int arg_count);
-
-#ifdef ENABLE_DEBUGGER_SUPPORT
- void DebugBreak();
-#endif
-
- // Invoke builtin given the number of arguments it expects on (and
- // removes from) the stack.
- void InvokeBuiltin(Builtins::JavaScript id,
- InvokeJSFlags flag,
- int arg_count);
-
- // Call load IC. Receiver is on the stack and is consumed. Result is returned
- // in r0.
- void CallLoadIC(Handle<String> name, RelocInfo::Mode mode);
-
- // Call store IC. If the load is contextual, value is found on top of the
- // frame. If not, value and receiver are on the frame. Both are consumed.
- // Result is returned in r0.
- void CallStoreIC(Handle<String> name, bool is_contextual,
- StrictModeFlag strict_mode);
-
- // Call keyed load IC. Key and receiver are on the stack. Both are consumed.
- // Result is returned in r0.
- void CallKeyedLoadIC();
-
- // Call keyed store IC. Value, key and receiver are on the stack. All three
- // are consumed. Result is returned in r0.
- void CallKeyedStoreIC(StrictModeFlag strict_mode);
-
- // Call into an IC stub given the number of arguments it removes
- // from the stack. Register arguments to the IC stub are implicit,
- // and depend on the type of IC stub.
- void CallCodeObject(Handle<Code> ic,
- RelocInfo::Mode rmode,
- int dropped_args);
-
- // Drop a number of elements from the top of the expression stack. May
- // emit code to affect the physical frame. Does not clobber any registers
- // excepting possibly the stack pointer.
- void Drop(int count);
-
- // Drop one element.
- void Drop() { Drop(1); }
-
- // Pop an element from the top of the expression stack. Discards
- // the result.
- void Pop();
-
- // Pop an element from the top of the expression stack. The register
- // will be one normally used for the top of stack register allocation
- // so you can't hold on to it if you push on the stack.
- Register PopToRegister(Register but_not_to_this_one = no_reg);
-
- // Look at the top of the stack. The register returned is aliased and
- // must be copied to a scratch register before modification.
- Register Peek();
-
- // Look at the value beneath the top of the stack. The register returned is
- // aliased and must be copied to a scratch register before modification.
- Register Peek2();
-
- // Duplicate the top of stack.
- void Dup();
-
- // Duplicate the two elements on top of stack.
- void Dup2();
-
- // Flushes all registers, but it puts a copy of the top-of-stack in r0.
- void SpillAllButCopyTOSToR0();
-
- // Flushes all registers, but it puts a copy of the top-of-stack in r1.
- void SpillAllButCopyTOSToR1();
-
- // Flushes all registers, but it puts a copy of the top-of-stack in r1
- // and the next value on the stack in r0.
- void SpillAllButCopyTOSToR1R0();
-
- // Pop and save an element from the top of the expression stack and
- // emit a corresponding pop instruction.
- void EmitPop(Register reg);
-
- // Takes the top two elements and puts them in r0 (top element) and r1
- // (second element).
- void PopToR1R0();
-
- // Takes the top element and puts it in r1.
- void PopToR1();
-
- // Takes the top element and puts it in r0.
- void PopToR0();
-
- // Push an element on top of the expression stack and emit a
- // corresponding push instruction.
- void EmitPush(Register reg, TypeInfo type_info = TypeInfo::Unknown());
- void EmitPush(Operand operand, TypeInfo type_info = TypeInfo::Unknown());
- void EmitPush(MemOperand operand, TypeInfo type_info = TypeInfo::Unknown());
- void EmitPushRoot(Heap::RootListIndex index);
-
- // Overwrite the nth thing on the stack. If the nth position is in a
- // register then this turns into a mov, otherwise an str. Afterwards
- // you can still use the register even if it is a register that can be
- // used for TOS (r0 or r1).
- void SetElementAt(Register reg, int this_far_down);
-
- // Get a register which is free and which must be immediately used to
- // push on the top of the stack.
- Register GetTOSRegister();
-
- // Push multiple registers on the stack and the virtual frame
- // Register are selected by setting bit in src_regs and
- // are pushed in decreasing order: r15 .. r0.
- void EmitPushMultiple(int count, int src_regs);
-
- static Register scratch0() { return r7; }
- static Register scratch1() { return r9; }
-
- private:
- static const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset;
- static const int kFunctionOffset = JavaScriptFrameConstants::kFunctionOffset;
- static const int kContextOffset = StandardFrameConstants::kContextOffset;
-
- static const int kHandlerSize = StackHandlerConstants::kSize / kPointerSize;
- static const int kPreallocatedElements = 5 + 8; // 8 expression stack slots.
-
- // 5 states for the top of stack, which can be in memory or in r0 and r1.
- enum TopOfStack {
- NO_TOS_REGISTERS,
- R0_TOS,
- R1_TOS,
- R1_R0_TOS,
- R0_R1_TOS,
- TOS_STATES
- };
-
- static const int kMaxTOSRegisters = 2;
-
- static const bool kR0InUse[TOS_STATES];
- static const bool kR1InUse[TOS_STATES];
- static const int kVirtualElements[TOS_STATES];
- static const TopOfStack kStateAfterPop[TOS_STATES];
- static const TopOfStack kStateAfterPush[TOS_STATES];
- static const Register kTopRegister[TOS_STATES];
- static const Register kBottomRegister[TOS_STATES];
-
- // We allocate up to 5 locals in registers.
- static const int kNumberOfAllocatedRegisters = 5;
- // r2 to r6 are allocated to locals.
- static const int kFirstAllocatedRegister = 2;
-
- static const Register kAllocatedRegisters[kNumberOfAllocatedRegisters];
-
- static Register AllocatedRegister(int r) {
- ASSERT(r >= 0 && r < kNumberOfAllocatedRegisters);
- return kAllocatedRegisters[r];
- }
-
- // The number of elements on the stack frame.
- int element_count_;
- TopOfStack top_of_stack_state_:3;
- int register_allocation_map_:kNumberOfAllocatedRegisters;
- static const int kTOSKnownSmiMapSize = 4;
- unsigned tos_known_smi_map_:kTOSKnownSmiMapSize;
-
- // The index of the element that is at the processor's stack pointer
- // (the sp register). For now since everything is in memory it is given
- // by the number of elements on the not-very-virtual stack frame.
- int stack_pointer() { return element_count_ - 1; }
-
- // The number of frame-allocated locals and parameters respectively.
- inline int parameter_count() const;
- inline int local_count() const;
-
- // The index of the element that is at the processor's frame pointer
- // (the fp register). The parameters, receiver, function, and context
- // are below the frame pointer.
- inline int frame_pointer() const;
-
- // The index of the first parameter. The receiver lies below the first
- // parameter.
- int param0_index() { return 1; }
-
- // The index of the context slot in the frame. It is immediately
- // below the frame pointer.
- inline int context_index();
-
- // The index of the function slot in the frame. It is below the frame
- // pointer and context slot.
- inline int function_index();
-
- // The index of the first local. Between the frame pointer and the
- // locals lies the return address.
- inline int local0_index() const;
-
- // The index of the base of the expression stack.
- inline int expression_base_index() const;
-
- // Convert a frame index into a frame pointer relative offset into the
- // actual stack.
- inline int fp_relative(int index);
-
- // Spill all elements in registers. Spill the top spilled_args elements
- // on the frame. Sync all other frame elements.
- // Then drop dropped_args elements from the virtual frame, to match
- // the effect of an upcoming call that will drop them from the stack.
- void PrepareForCall(int spilled_args, int dropped_args);
-
- // If all top-of-stack registers are in use then the lowest one is pushed
- // onto the physical stack and made free.
- void EnsureOneFreeTOSRegister();
-
- // Emit instructions to get the top of stack state from where we are to where
- // we want to be.
- void MergeTOSTo(TopOfStack expected_state, Condition cond = al);
-
- inline bool Equals(const VirtualFrame* other);
-
- inline void LowerHeight(int count) {
- element_count_ -= count;
- if (count >= kTOSKnownSmiMapSize) {
- tos_known_smi_map_ = 0;
- } else {
- tos_known_smi_map_ >>= count;
- }
- }
-
- inline void RaiseHeight(int count, unsigned known_smi_map = 0) {
- ASSERT(count >= 32 || known_smi_map < (1u << count));
- element_count_ += count;
- if (count >= kTOSKnownSmiMapSize) {
- tos_known_smi_map_ = known_smi_map;
- } else {
- tos_known_smi_map_ = ((tos_known_smi_map_ << count) | known_smi_map);
- }
- }
-
- friend class JumpTarget;
-};
-
-
-} } // namespace v8::internal
-
-#endif // V8_ARM_VIRTUAL_FRAME_ARM_H_
diff --git a/src/assembler.cc b/src/assembler.cc
index 0322747..ca30e19 100644
--- a/src/assembler.cc
+++ b/src/assembler.cc
@@ -492,7 +492,8 @@
target_address());
} else if (IsPosition(rmode_)) {
PrintF(out, " (%" V8_PTR_PREFIX "d)", data());
- } else if (rmode_ == RelocInfo::RUNTIME_ENTRY) {
+ } else if (rmode_ == RelocInfo::RUNTIME_ENTRY &&
+ Isolate::Current()->deoptimizer_data() != NULL) {
// Depotimization bailouts are stored as runtime entries.
int id = Deoptimizer::GetDeoptimizationId(
target_address(), Deoptimizer::EAGER);
@@ -1002,7 +1003,7 @@
#ifdef ENABLE_DEBUGGER_SUPPORT
ExternalReference ExternalReference::debug_break(Isolate* isolate) {
- return ExternalReference(Redirect(isolate, FUNCTION_ADDR(Debug::Break)));
+ return ExternalReference(Redirect(isolate, FUNCTION_ADDR(Debug_Break)));
}
diff --git a/src/assembler.h b/src/assembler.h
index 62fe04d..e8cecc3 100644
--- a/src/assembler.h
+++ b/src/assembler.h
@@ -30,7 +30,7 @@
// The original source code covered by the above license above has been
// modified significantly by Google Inc.
-// Copyright 2006-2009 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
#ifndef V8_ASSEMBLER_H_
#define V8_ASSEMBLER_H_
@@ -111,7 +111,6 @@
friend class Assembler;
friend class RegexpAssembler;
friend class Displacement;
- friend class ShadowTarget;
friend class RegExpMacroAssemblerIrregexp;
};
diff --git a/src/ast-inl.h b/src/ast-inl.h
index 6021fd9..d80684a 100644
--- a/src/ast-inl.h
+++ b/src/ast-inl.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -31,7 +31,6 @@
#include "v8.h"
#include "ast.h"
-#include "jump-target-inl.h"
namespace v8 {
namespace internal {
@@ -62,7 +61,7 @@
IterationStatement::IterationStatement(ZoneStringList* labels)
: BreakableStatement(labels, TARGET_FOR_ANONYMOUS),
body_(NULL),
- continue_target_(JumpTarget::BIDIRECTIONAL),
+ continue_target_(),
osr_entry_id_(GetNextId()) {
}
diff --git a/src/ast.cc b/src/ast.cc
index 8434357..303189d 100644
--- a/src/ast.cc
+++ b/src/ast.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -28,10 +28,10 @@
#include "v8.h"
#include "ast.h"
-#include "jump-target-inl.h"
#include "parser.h"
#include "scopes.h"
#include "string-stream.h"
+#include "type-info.h"
namespace v8 {
namespace internal {
@@ -77,20 +77,23 @@
var_(NULL), // Will be set by the call to BindTo.
is_this_(var->is_this()),
inside_with_(false),
- is_trivial_(false) {
+ is_trivial_(false),
+ position_(RelocInfo::kNoPosition) {
BindTo(var);
}
VariableProxy::VariableProxy(Handle<String> name,
bool is_this,
- bool inside_with)
+ bool inside_with,
+ int position)
: name_(name),
var_(NULL),
is_this_(is_this),
inside_with_(inside_with),
- is_trivial_(false) {
- // names must be canonicalized for fast equality checks
+ is_trivial_(false),
+ position_(position) {
+ // Names must be canonicalized for fast equality checks.
ASSERT(name->IsSymbol());
}
@@ -288,7 +291,7 @@
}
-void TargetCollector::AddTarget(BreakTarget* target) {
+void TargetCollector::AddTarget(Label* target) {
// Add the label to the collector, but discard duplicates.
int length = targets_->length();
for (int i = 0; i < length; i++) {
@@ -298,79 +301,6 @@
}
-bool Expression::GuaranteedSmiResult() {
- BinaryOperation* node = AsBinaryOperation();
- if (node == NULL) return false;
- Token::Value op = node->op();
- switch (op) {
- case Token::COMMA:
- case Token::OR:
- case Token::AND:
- case Token::ADD:
- case Token::SUB:
- case Token::MUL:
- case Token::DIV:
- case Token::MOD:
- case Token::BIT_XOR:
- case Token::SHL:
- return false;
- break;
- case Token::BIT_OR:
- case Token::BIT_AND: {
- Literal* left = node->left()->AsLiteral();
- Literal* right = node->right()->AsLiteral();
- if (left != NULL && left->handle()->IsSmi()) {
- int value = Smi::cast(*left->handle())->value();
- if (op == Token::BIT_OR && ((value & 0xc0000000) == 0xc0000000)) {
- // Result of bitwise or is always a negative Smi.
- return true;
- }
- if (op == Token::BIT_AND && ((value & 0xc0000000) == 0)) {
- // Result of bitwise and is always a positive Smi.
- return true;
- }
- }
- if (right != NULL && right->handle()->IsSmi()) {
- int value = Smi::cast(*right->handle())->value();
- if (op == Token::BIT_OR && ((value & 0xc0000000) == 0xc0000000)) {
- // Result of bitwise or is always a negative Smi.
- return true;
- }
- if (op == Token::BIT_AND && ((value & 0xc0000000) == 0)) {
- // Result of bitwise and is always a positive Smi.
- return true;
- }
- }
- return false;
- break;
- }
- case Token::SAR:
- case Token::SHR: {
- Literal* right = node->right()->AsLiteral();
- if (right != NULL && right->handle()->IsSmi()) {
- int value = Smi::cast(*right->handle())->value();
- if ((value & 0x1F) > 1 ||
- (op == Token::SAR && (value & 0x1F) == 1)) {
- return true;
- }
- }
- return false;
- break;
- }
- default:
- UNREACHABLE();
- break;
- }
- return false;
-}
-
-
-void Expression::CopyAnalysisResultsFrom(Expression* other) {
- bitfields_ = other->bitfields_;
- type_ = other->type_;
-}
-
-
bool UnaryOperation::ResultOverwriteAllowed() {
switch (op_) {
case Token::BIT_NOT:
@@ -413,13 +343,148 @@
left_ = assignment->target();
right_ = assignment->value();
pos_ = assignment->position();
- CopyAnalysisResultsFrom(assignment);
}
// ----------------------------------------------------------------------------
// Inlining support
+bool Declaration::IsInlineable() const {
+ UNREACHABLE();
+ return false;
+}
+
+
+bool TargetCollector::IsInlineable() const {
+ UNREACHABLE();
+ return false;
+}
+
+
+bool Slot::IsInlineable() const {
+ UNREACHABLE();
+ return false;
+}
+
+
+bool ForInStatement::IsInlineable() const {
+ return false;
+}
+
+
+bool WithEnterStatement::IsInlineable() const {
+ return false;
+}
+
+
+bool WithExitStatement::IsInlineable() const {
+ return false;
+}
+
+
+bool SwitchStatement::IsInlineable() const {
+ return false;
+}
+
+
+bool TryStatement::IsInlineable() const {
+ return false;
+}
+
+
+bool TryCatchStatement::IsInlineable() const {
+ return false;
+}
+
+
+bool TryFinallyStatement::IsInlineable() const {
+ return false;
+}
+
+
+bool CatchExtensionObject::IsInlineable() const {
+ return false;
+}
+
+
+bool DebuggerStatement::IsInlineable() const {
+ return false;
+}
+
+
+bool Throw::IsInlineable() const {
+ return true;
+}
+
+
+bool MaterializedLiteral::IsInlineable() const {
+ // TODO(1322): Allow materialized literals.
+ return false;
+}
+
+
+bool FunctionLiteral::IsInlineable() const {
+ // TODO(1322): Allow materialized literals.
+ return false;
+}
+
+
+bool ThisFunction::IsInlineable() const {
+ return false;
+}
+
+
+bool SharedFunctionInfoLiteral::IsInlineable() const {
+ return false;
+}
+
+
+bool ValidLeftHandSideSentinel::IsInlineable() const {
+ UNREACHABLE();
+ return false;
+}
+
+
+bool ForStatement::IsInlineable() const {
+ return (init() == NULL || init()->IsInlineable())
+ && (cond() == NULL || cond()->IsInlineable())
+ && (next() == NULL || next()->IsInlineable())
+ && body()->IsInlineable();
+}
+
+
+bool WhileStatement::IsInlineable() const {
+ return cond()->IsInlineable()
+ && body()->IsInlineable();
+}
+
+
+bool DoWhileStatement::IsInlineable() const {
+ return cond()->IsInlineable()
+ && body()->IsInlineable();
+}
+
+
+bool ContinueStatement::IsInlineable() const {
+ return true;
+}
+
+
+bool BreakStatement::IsInlineable() const {
+ return true;
+}
+
+
+bool EmptyStatement::IsInlineable() const {
+ return true;
+}
+
+
+bool Literal::IsInlineable() const {
+ return true;
+}
+
+
bool Block::IsInlineable() const {
const int count = statements_.length();
for (int i = 0; i < count; ++i) {
@@ -435,8 +500,9 @@
bool IfStatement::IsInlineable() const {
- return condition()->IsInlineable() && then_statement()->IsInlineable() &&
- else_statement()->IsInlineable();
+ return condition()->IsInlineable()
+ && then_statement()->IsInlineable()
+ && else_statement()->IsInlineable();
}
@@ -546,7 +612,7 @@
} else if (is_monomorphic_) {
monomorphic_receiver_type_ = oracle->LoadMonomorphicReceiverType(this);
if (monomorphic_receiver_type_->has_external_array_elements()) {
- SetExternalArrayType(oracle->GetKeyedLoadExternalArrayType(this));
+ set_external_array_type(oracle->GetKeyedLoadExternalArrayType(this));
}
}
}
@@ -566,7 +632,19 @@
// Record receiver type for monomorphic keyed loads.
monomorphic_receiver_type_ = oracle->StoreMonomorphicReceiverType(this);
if (monomorphic_receiver_type_->has_external_array_elements()) {
- SetExternalArrayType(oracle->GetKeyedStoreExternalArrayType(this));
+ set_external_array_type(oracle->GetKeyedStoreExternalArrayType(this));
+ }
+ }
+}
+
+
+void CountOperation::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
+ is_monomorphic_ = oracle->StoreIsMonomorphic(this);
+ if (is_monomorphic_) {
+ // Record receiver type for monomorphic keyed loads.
+ monomorphic_receiver_type_ = oracle->StoreMonomorphicReceiverType(this);
+ if (monomorphic_receiver_type_->has_external_array_elements()) {
+ set_external_array_type(oracle->GetKeyedStoreExternalArrayType(this));
}
}
}
@@ -622,24 +700,21 @@
bool Call::ComputeGlobalTarget(Handle<GlobalObject> global,
- Handle<String> name) {
+ LookupResult* lookup) {
target_ = Handle<JSFunction>::null();
cell_ = Handle<JSGlobalPropertyCell>::null();
- LookupResult lookup;
- global->Lookup(*name, &lookup);
- if (lookup.IsProperty() &&
- lookup.type() == NORMAL &&
- lookup.holder() == *global) {
- cell_ = Handle<JSGlobalPropertyCell>(global->GetPropertyCell(&lookup));
- if (cell_->value()->IsJSFunction()) {
- Handle<JSFunction> candidate(JSFunction::cast(cell_->value()));
- // If the function is in new space we assume it's more likely to
- // change and thus prefer the general IC code.
- if (!HEAP->InNewSpace(*candidate) &&
- CanCallWithoutIC(candidate, arguments()->length())) {
- target_ = candidate;
- return true;
- }
+ ASSERT(lookup->IsProperty() &&
+ lookup->type() == NORMAL &&
+ lookup->holder() == *global);
+ cell_ = Handle<JSGlobalPropertyCell>(global->GetPropertyCell(lookup));
+ if (cell_->value()->IsJSFunction()) {
+ Handle<JSFunction> candidate(JSFunction::cast(cell_->value()));
+ // If the function is in new space we assume it's more likely to
+ // change and thus prefer the general IC code.
+ if (!HEAP->InNewSpace(*candidate) &&
+ CanCallWithoutIC(candidate, arguments()->length())) {
+ target_ = candidate;
+ return true;
}
}
return false;
diff --git a/src/ast.h b/src/ast.h
index e9a06ec..65a25a9 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -31,7 +31,6 @@
#include "execution.h"
#include "factory.h"
#include "jsregexp.h"
-#include "jump-target.h"
#include "runtime.h"
#include "token.h"
#include "variables.h"
@@ -88,7 +87,6 @@
V(CallNew) \
V(CallRuntime) \
V(UnaryOperation) \
- V(IncrementOperation) \
V(CountOperation) \
V(BinaryOperation) \
V(CompareOperation) \
@@ -134,6 +132,7 @@
#undef DECLARE_TYPE_ENUM
static const int kNoNumber = -1;
+ static const int kFunctionEntryId = 2; // Using 0 could disguise errors.
AstNode() : id_(GetNextId()) {
Isolate* isolate = Isolate::Current();
@@ -160,7 +159,7 @@
virtual Slot* AsSlot() { return NULL; }
// True if the node is simple enough for us to inline calls containing it.
- virtual bool IsInlineable() const { return false; }
+ virtual bool IsInlineable() const = 0;
static int Count() { return Isolate::Current()->ast_node_count(); }
static void ResetIds() { Isolate::Current()->set_ast_node_id(0); }
@@ -220,7 +219,12 @@
kTest
};
- Expression() : bitfields_(0) {}
+ Expression() {}
+
+ virtual int position() const {
+ UNREACHABLE();
+ return 0;
+ }
virtual Expression* AsExpression() { return this; }
@@ -266,70 +270,15 @@
return Handle<Map>();
}
- // Static type information for this expression.
- StaticType* type() { return &type_; }
-
- // True if the expression is a loop condition.
- bool is_loop_condition() const {
- return LoopConditionField::decode(bitfields_);
+ ExternalArrayType external_array_type() const {
+ return external_array_type_;
}
- void set_is_loop_condition(bool flag) {
- bitfields_ = (bitfields_ & ~LoopConditionField::mask()) |
- LoopConditionField::encode(flag);
- }
-
- // The value of the expression is guaranteed to be a smi, because the
- // top operation is a bit operation with a mask, or a shift.
- bool GuaranteedSmiResult();
-
- // AST analysis results.
- void CopyAnalysisResultsFrom(Expression* other);
-
- // True if the expression rooted at this node can be compiled by the
- // side-effect free compiler.
- bool side_effect_free() { return SideEffectFreeField::decode(bitfields_); }
- void set_side_effect_free(bool is_side_effect_free) {
- bitfields_ &= ~SideEffectFreeField::mask();
- bitfields_ |= SideEffectFreeField::encode(is_side_effect_free);
- }
-
- // Will the use of this expression treat -0 the same as 0 in all cases?
- // If so, we can return 0 instead of -0 if we want to, to optimize code.
- bool no_negative_zero() { return NoNegativeZeroField::decode(bitfields_); }
- void set_no_negative_zero(bool no_negative_zero) {
- bitfields_ &= ~NoNegativeZeroField::mask();
- bitfields_ |= NoNegativeZeroField::encode(no_negative_zero);
- }
-
- // Will ToInt32 (ECMA 262-3 9.5) or ToUint32 (ECMA 262-3 9.6)
- // be applied to the value of this expression?
- // If so, we may be able to optimize the calculation of the value.
- bool to_int32() { return ToInt32Field::decode(bitfields_); }
- void set_to_int32(bool to_int32) {
- bitfields_ &= ~ToInt32Field::mask();
- bitfields_ |= ToInt32Field::encode(to_int32);
- }
-
- // How many bitwise logical or shift operators are used in this expression?
- int num_bit_ops() { return NumBitOpsField::decode(bitfields_); }
- void set_num_bit_ops(int num_bit_ops) {
- bitfields_ &= ~NumBitOpsField::mask();
- num_bit_ops = Min(num_bit_ops, kMaxNumBitOps);
- bitfields_ |= NumBitOpsField::encode(num_bit_ops);
+ void set_external_array_type(ExternalArrayType array_type) {
+ external_array_type_ = array_type;
}
private:
- static const int kMaxNumBitOps = (1 << 5) - 1;
-
- uint32_t bitfields_;
- StaticType type_;
-
- // Using template BitField<type, start, size>.
- class SideEffectFreeField : public BitField<bool, 0, 1> {};
- class NoNegativeZeroField : public BitField<bool, 1, 1> {};
- class ToInt32Field : public BitField<bool, 2, 1> {};
- class NumBitOpsField : public BitField<int, 3, 5> {};
- class LoopConditionField: public BitField<bool, 8, 1> {};
+ ExternalArrayType external_array_type_;
};
@@ -342,6 +291,7 @@
public:
virtual bool IsValidLeftHandSide() { return true; }
virtual void Accept(AstVisitor* v) { UNREACHABLE(); }
+ virtual bool IsInlineable() const;
};
@@ -360,7 +310,7 @@
virtual BreakableStatement* AsBreakableStatement() { return this; }
// Code generation
- BreakTarget* break_target() { return &break_target_; }
+ Label* break_target() { return &break_target_; }
// Testers.
bool is_target_for_anonymous() const { return type_ == TARGET_FOR_ANONYMOUS; }
@@ -375,7 +325,7 @@
private:
ZoneStringList* labels_;
Type type_;
- BreakTarget break_target_;
+ Label break_target_;
int entry_id_;
int exit_id_;
};
@@ -426,6 +376,7 @@
VariableProxy* proxy() const { return proxy_; }
Variable::Mode mode() const { return mode_; }
FunctionLiteral* fun() const { return fun_; } // may be NULL
+ virtual bool IsInlineable() const;
private:
VariableProxy* proxy_;
@@ -446,7 +397,7 @@
virtual int ContinueId() const = 0;
// Code generation
- BreakTarget* continue_target() { return &continue_target_; }
+ Label* continue_target() { return &continue_target_; }
protected:
explicit inline IterationStatement(ZoneStringList* labels);
@@ -457,7 +408,7 @@
private:
Statement* body_;
- BreakTarget continue_target_;
+ Label continue_target_;
int osr_entry_id_;
};
@@ -484,6 +435,8 @@
virtual int ContinueId() const { return continue_id_; }
int BackEdgeId() const { return back_edge_id_; }
+ virtual bool IsInlineable() const;
+
private:
Expression* cond_;
int condition_position_;
@@ -510,6 +463,7 @@
void set_may_have_function_literal(bool value) {
may_have_function_literal_ = value;
}
+ virtual bool IsInlineable() const;
// Bailout support.
virtual int ContinueId() const { return EntryId(); }
@@ -557,6 +511,7 @@
bool is_fast_smi_loop() { return loop_variable_ != NULL; }
Variable* loop_variable() { return loop_variable_; }
void set_loop_variable(Variable* var) { loop_variable_ = var; }
+ virtual bool IsInlineable() const;
private:
Statement* init_;
@@ -584,6 +539,7 @@
Expression* each() const { return each_; }
Expression* enumerable() const { return enumerable_; }
+ virtual bool IsInlineable() const;
// Bailout support.
int AssignmentId() const { return assignment_id_; }
@@ -624,6 +580,7 @@
DECLARE_NODE_TYPE(ContinueStatement)
IterationStatement* target() const { return target_; }
+ virtual bool IsInlineable() const;
private:
IterationStatement* target_;
@@ -638,6 +595,7 @@
DECLARE_NODE_TYPE(BreakStatement)
BreakableStatement* target() const { return target_; }
+ virtual bool IsInlineable() const;
private:
BreakableStatement* target_;
@@ -669,6 +627,7 @@
Expression* expression() const { return expression_; }
bool is_catch_block() const { return is_catch_block_; }
+ virtual bool IsInlineable() const;
private:
Expression* expression_;
@@ -680,6 +639,8 @@
public:
WithExitStatement() { }
+ virtual bool IsInlineable() const;
+
DECLARE_NODE_TYPE(WithExitStatement)
};
@@ -693,10 +654,10 @@
CHECK(!is_default());
return label_;
}
- JumpTarget* body_target() { return &body_target_; }
+ Label* body_target() { return &body_target_; }
ZoneList<Statement*>* statements() const { return statements_; }
- int position() { return position_; }
+ int position() const { return position_; }
void set_position(int pos) { position_ = pos; }
int EntryId() { return entry_id_; }
@@ -708,7 +669,7 @@
private:
Expression* label_;
- JumpTarget body_target_;
+ Label body_target_;
ZoneList<Statement*>* statements_;
int position_;
enum CompareTypeFeedback { NONE, SMI_ONLY, OBJECT_ONLY };
@@ -730,6 +691,7 @@
Expression* tag() const { return tag_; }
ZoneList<CaseClause*>* cases() const { return cases_; }
+ virtual bool IsInlineable() const;
private:
Expression* tag_;
@@ -781,23 +743,24 @@
// stack in the compiler; this should probably be reworked.
class TargetCollector: public AstNode {
public:
- explicit TargetCollector(ZoneList<BreakTarget*>* targets)
+ explicit TargetCollector(ZoneList<Label*>* targets)
: targets_(targets) {
}
// Adds a jump target to the collector. The collector stores a pointer not
// a copy of the target to make binding work, so make sure not to pass in
// references to something on the stack.
- void AddTarget(BreakTarget* target);
+ void AddTarget(Label* target);
// Virtual behaviour. TargetCollectors are never part of the AST.
virtual void Accept(AstVisitor* v) { UNREACHABLE(); }
virtual TargetCollector* AsTargetCollector() { return this; }
- ZoneList<BreakTarget*>* targets() { return targets_; }
+ ZoneList<Label*>* targets() { return targets_; }
+ virtual bool IsInlineable() const;
private:
- ZoneList<BreakTarget*>* targets_;
+ ZoneList<Label*>* targets_;
};
@@ -806,16 +769,17 @@
explicit TryStatement(Block* try_block)
: try_block_(try_block), escaping_targets_(NULL) { }
- void set_escaping_targets(ZoneList<BreakTarget*>* targets) {
+ void set_escaping_targets(ZoneList<Label*>* targets) {
escaping_targets_ = targets;
}
Block* try_block() const { return try_block_; }
- ZoneList<BreakTarget*>* escaping_targets() const { return escaping_targets_; }
+ ZoneList<Label*>* escaping_targets() const { return escaping_targets_; }
+ virtual bool IsInlineable() const;
private:
Block* try_block_;
- ZoneList<BreakTarget*>* escaping_targets_;
+ ZoneList<Label*>* escaping_targets_;
};
@@ -833,6 +797,7 @@
VariableProxy* catch_var() const { return catch_var_; }
Block* catch_block() const { return catch_block_; }
+ virtual bool IsInlineable() const;
private:
VariableProxy* catch_var_;
@@ -849,6 +814,7 @@
DECLARE_NODE_TYPE(TryFinallyStatement)
Block* finally_block() const { return finally_block_; }
+ virtual bool IsInlineable() const;
private:
Block* finally_block_;
@@ -858,6 +824,7 @@
class DebuggerStatement: public Statement {
public:
DECLARE_NODE_TYPE(DebuggerStatement)
+ virtual bool IsInlineable() const;
};
@@ -865,7 +832,7 @@
public:
DECLARE_NODE_TYPE(EmptyStatement)
- virtual bool IsInlineable() const { return true; }
+ virtual bool IsInlineable() const;
};
@@ -876,7 +843,6 @@
DECLARE_NODE_TYPE(Literal)
virtual bool IsTrivial() { return true; }
- virtual bool IsInlineable() const { return true; }
virtual bool IsSmiLiteral() { return handle_->IsSmi(); }
// Check if this literal is identical to the other literal.
@@ -915,6 +881,7 @@
}
Handle<Object> handle() const { return handle_; }
+ virtual bool IsInlineable() const;
private:
Handle<Object> handle_;
@@ -936,6 +903,7 @@
bool is_simple() const { return is_simple_; }
int depth() const { return depth_; }
+ virtual bool IsInlineable() const;
private:
int literal_index_;
@@ -1085,6 +1053,7 @@
Literal* key() const { return key_; }
VariableProxy* value() const { return value_; }
+ virtual bool IsInlineable() const;
private:
Literal* key_;
@@ -1135,6 +1104,7 @@
Variable* var() const { return var_; }
bool is_this() const { return is_this_; }
bool inside_with() const { return inside_with_; }
+ int position() const { return position_; }
void MarkAsTrivial() { is_trivial_ = true; }
@@ -1147,8 +1117,12 @@
bool is_this_;
bool inside_with_;
bool is_trivial_;
+ int position_;
- VariableProxy(Handle<String> name, bool is_this, bool inside_with);
+ VariableProxy(Handle<String> name,
+ bool is_this,
+ bool inside_with,
+ int position = RelocInfo::kNoPosition);
explicit VariableProxy(bool is_this);
friend class Scope;
@@ -1206,6 +1180,7 @@
Type type() const { return type_; }
int index() const { return index_; }
bool is_arguments() const { return var_->is_arguments(); }
+ virtual bool IsInlineable() const;
private:
Variable* var_;
@@ -1241,7 +1216,7 @@
Expression* obj() const { return obj_; }
Expression* key() const { return key_; }
- int position() const { return pos_; }
+ virtual int position() const { return pos_; }
bool is_synthetic() const { return type_ == SYNTHETIC; }
bool IsStringLength() const { return is_string_length_; }
@@ -1255,11 +1230,6 @@
}
bool is_arguments_access() const { return is_arguments_access_; }
- ExternalArrayType GetExternalArrayType() const { return array_type_; }
- void SetExternalArrayType(ExternalArrayType array_type) {
- array_type_ = array_type;
- }
-
// Type feedback information.
void RecordTypeFeedback(TypeFeedbackOracle* oracle);
virtual bool IsMonomorphic() { return is_monomorphic_; }
@@ -1283,7 +1253,6 @@
bool is_function_prototype_ : 1;
bool is_arguments_access_ : 1;
Handle<Map> monomorphic_receiver_type_;
- ExternalArrayType array_type_;
};
@@ -1305,7 +1274,7 @@
Expression* expression() const { return expression_; }
ZoneList<Expression*>* arguments() const { return arguments_; }
- int position() { return pos_; }
+ virtual int position() const { return pos_; }
void RecordTypeFeedback(TypeFeedbackOracle* oracle);
virtual ZoneMapList* GetReceiverTypes() { return receiver_types_; }
@@ -1316,7 +1285,7 @@
Handle<JSGlobalPropertyCell> cell() { return cell_; }
bool ComputeTarget(Handle<Map> type, Handle<String> name);
- bool ComputeGlobalTarget(Handle<GlobalObject> global, Handle<String> name);
+ bool ComputeGlobalTarget(Handle<GlobalObject> global, LookupResult* lookup);
// Bailout support.
int ReturnId() const { return return_id_; }
@@ -1383,7 +1352,7 @@
Expression* expression() const { return expression_; }
ZoneList<Expression*>* arguments() const { return arguments_; }
- int position() { return pos_; }
+ virtual int position() const { return pos_; }
private:
Expression* expression_;
@@ -1466,7 +1435,7 @@
Token::Value op() const { return op_; }
Expression* left() const { return left_; }
Expression* right() const { return right_; }
- int position() const { return pos_; }
+ virtual int position() const { return pos_; }
// Bailout support.
int RightId() const { return right_id_; }
@@ -1482,59 +1451,52 @@
};
-class IncrementOperation: public Expression {
- public:
- IncrementOperation(Token::Value op, Expression* expr)
- : op_(op), expression_(expr) {
- ASSERT(Token::IsCountOp(op));
- }
-
- DECLARE_NODE_TYPE(IncrementOperation)
-
- Token::Value op() const { return op_; }
- bool is_increment() { return op_ == Token::INC; }
- Expression* expression() const { return expression_; }
-
- private:
- Token::Value op_;
- Expression* expression_;
- int pos_;
-};
-
-
class CountOperation: public Expression {
public:
- CountOperation(bool is_prefix, IncrementOperation* increment, int pos)
- : is_prefix_(is_prefix), increment_(increment), pos_(pos),
- assignment_id_(GetNextId()) {
- }
+ CountOperation(Token::Value op, bool is_prefix, Expression* expr, int pos)
+ : op_(op),
+ is_prefix_(is_prefix),
+ expression_(expr),
+ pos_(pos),
+ assignment_id_(GetNextId()),
+ count_id_(GetNextId()) { }
DECLARE_NODE_TYPE(CountOperation)
bool is_prefix() const { return is_prefix_; }
bool is_postfix() const { return !is_prefix_; }
- Token::Value op() const { return increment_->op(); }
+ Token::Value op() const { return op_; }
Token::Value binary_op() {
return (op() == Token::INC) ? Token::ADD : Token::SUB;
}
- Expression* expression() const { return increment_->expression(); }
- IncrementOperation* increment() const { return increment_; }
- int position() const { return pos_; }
+ Expression* expression() const { return expression_; }
+ virtual int position() const { return pos_; }
virtual void MarkAsStatement() { is_prefix_ = true; }
virtual bool IsInlineable() const;
+ void RecordTypeFeedback(TypeFeedbackOracle* oracle);
+ virtual bool IsMonomorphic() { return is_monomorphic_; }
+ virtual Handle<Map> GetMonomorphicReceiverType() {
+ return monomorphic_receiver_type_;
+ }
+
// Bailout support.
int AssignmentId() const { return assignment_id_; }
+ int CountId() const { return count_id_; }
private:
+ Token::Value op_;
bool is_prefix_;
- IncrementOperation* increment_;
+ bool is_monomorphic_;
+ Expression* expression_;
int pos_;
int assignment_id_;
+ int count_id_;
+ Handle<Map> monomorphic_receiver_type_;
};
@@ -1553,7 +1515,7 @@
Token::Value op() const { return op_; }
Expression* left() const { return left_; }
Expression* right() const { return right_; }
- int position() const { return pos_; }
+ virtual int position() const { return pos_; }
virtual bool IsInlineable() const;
@@ -1648,7 +1610,7 @@
Token::Value op() const { return op_; }
Expression* target() const { return target_; }
Expression* value() const { return value_; }
- int position() { return pos_; }
+ virtual int position() const { return pos_; }
BinaryOperation* binary_operation() const { return binary_operation_; }
// This check relies on the definition order of token in token.h.
@@ -1670,10 +1632,6 @@
virtual Handle<Map> GetMonomorphicReceiverType() {
return monomorphic_receiver_type_;
}
- ExternalArrayType GetExternalArrayType() const { return array_type_; }
- void SetExternalArrayType(ExternalArrayType array_type) {
- array_type_ = array_type;
- }
// Bailout support.
int CompoundLoadId() const { return compound_load_id_; }
@@ -1694,7 +1652,6 @@
bool is_monomorphic_;
ZoneMapList* receiver_types_;
Handle<Map> monomorphic_receiver_type_;
- ExternalArrayType array_type_;
};
@@ -1706,7 +1663,8 @@
DECLARE_NODE_TYPE(Throw)
Expression* exception() const { return exception_; }
- int position() const { return pos_; }
+ virtual int position() const { return pos_; }
+ virtual bool IsInlineable() const;
private:
Expression* exception_;
@@ -1726,8 +1684,7 @@
int num_parameters,
int start_position,
int end_position,
- bool is_expression,
- bool contains_loops)
+ bool is_expression)
: name_(name),
scope_(scope),
body_(body),
@@ -1740,10 +1697,8 @@
start_position_(start_position),
end_position_(end_position),
is_expression_(is_expression),
- contains_loops_(contains_loops),
function_token_position_(RelocInfo::kNoPosition),
inferred_name_(HEAP->empty_string()),
- try_full_codegen_(false),
pretenure_(false) { }
DECLARE_NODE_TYPE(FunctionLiteral)
@@ -1756,7 +1711,6 @@
int start_position() const { return start_position_; }
int end_position() const { return end_position_; }
bool is_expression() const { return is_expression_; }
- bool contains_loops() const { return contains_loops_; }
bool strict_mode() const;
int materialized_literal_count() { return materialized_literal_count_; }
@@ -1781,11 +1735,9 @@
inferred_name_ = inferred_name;
}
- bool try_full_codegen() { return try_full_codegen_; }
- void set_try_full_codegen(bool flag) { try_full_codegen_ = flag; }
-
bool pretenure() { return pretenure_; }
void set_pretenure(bool value) { pretenure_ = value; }
+ virtual bool IsInlineable() const;
private:
Handle<String> name_;
@@ -1799,11 +1751,8 @@
int start_position_;
int end_position_;
bool is_expression_;
- bool contains_loops_;
- bool strict_mode_;
int function_token_position_;
Handle<String> inferred_name_;
- bool try_full_codegen_;
bool pretenure_;
};
@@ -1819,6 +1768,7 @@
Handle<SharedFunctionInfo> shared_function_info() const {
return shared_function_info_;
}
+ virtual bool IsInlineable() const;
private:
Handle<SharedFunctionInfo> shared_function_info_;
@@ -1828,6 +1778,7 @@
class ThisFunction: public Expression {
public:
DECLARE_NODE_TYPE(ThisFunction)
+ virtual bool IsInlineable() const;
};
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index 9c9bac7..0800714 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -141,7 +141,8 @@
class Genesis BASE_EMBEDDED {
public:
- Genesis(Handle<Object> global_object,
+ Genesis(Isolate* isolate,
+ Handle<Object> global_object,
v8::Handle<v8::ObjectTemplate> global_template,
v8::ExtensionConfiguration* extensions);
~Genesis() { }
@@ -150,8 +151,13 @@
Genesis* previous() { return previous_; }
+ Isolate* isolate() const { return isolate_; }
+ Factory* factory() const { return isolate_->factory(); }
+ Heap* heap() const { return isolate_->heap(); }
+
private:
Handle<Context> global_context_;
+ Isolate* isolate_;
// There may be more than one active genesis object: When GC is
// triggered during environment creation there may be weak handle
@@ -163,7 +169,7 @@
// Creates some basic objects. Used for creating a context from scratch.
void CreateRoots();
// Creates the empty function. Used for creating a context from scratch.
- Handle<JSFunction> CreateEmptyFunction();
+ Handle<JSFunction> CreateEmptyFunction(Isolate* isolate);
// Creates the ThrowTypeError function. ECMA 5th Ed. 13.2.3
Handle<JSFunction> CreateThrowTypeErrorFunction(Builtins::Name builtin);
@@ -194,6 +200,7 @@
// Used for creating a context from scratch.
void InstallNativeFunctions();
bool InstallNatives();
+ bool InstallExperimentalNatives();
void InstallBuiltinFunctionIds();
void InstallJSFunctionResultCaches();
void InitializeNormalizedMapCaches();
@@ -239,7 +246,8 @@
Handle<FixedArray> arguments,
Handle<FixedArray> caller);
- static bool CompileBuiltin(int index);
+ static bool CompileBuiltin(Isolate* isolate, int index);
+ static bool CompileExperimentalBuiltin(Isolate* isolate, int index);
static bool CompileNative(Vector<const char> name, Handle<String> source);
static bool CompileScriptCached(Vector<const char> name,
Handle<String> source,
@@ -269,12 +277,13 @@
Handle<Context> Bootstrapper::CreateEnvironment(
+ Isolate* isolate,
Handle<Object> global_object,
v8::Handle<v8::ObjectTemplate> global_template,
v8::ExtensionConfiguration* extensions) {
HandleScope scope;
Handle<Context> env;
- Genesis genesis(global_object, global_template, extensions);
+ Genesis genesis(isolate, global_object, global_template, extensions);
env = genesis.result();
if (!env.is_null()) {
if (InstallExtensions(env, extensions)) {
@@ -287,15 +296,16 @@
static void SetObjectPrototype(Handle<JSObject> object, Handle<Object> proto) {
// object.__proto__ = proto;
+ Factory* factory = object->GetIsolate()->factory();
Handle<Map> old_to_map = Handle<Map>(object->map());
- Handle<Map> new_to_map = FACTORY->CopyMapDropTransitions(old_to_map);
+ Handle<Map> new_to_map = factory->CopyMapDropTransitions(old_to_map);
new_to_map->set_prototype(*proto);
object->set_map(*new_to_map);
}
void Bootstrapper::DetachGlobal(Handle<Context> env) {
- Factory* factory = Isolate::Current()->factory();
+ Factory* factory = env->GetIsolate()->factory();
JSGlobalProxy::cast(env->global_proxy())->set_context(*factory->null_value());
SetObjectPrototype(Handle<JSObject>(env->global_proxy()),
factory->null_value());
@@ -322,7 +332,7 @@
Handle<JSObject> prototype,
Builtins::Name call,
bool is_ecma_native) {
- Isolate* isolate = Isolate::Current();
+ Isolate* isolate = target->GetIsolate();
Factory* factory = isolate->factory();
Handle<String> symbol = factory->LookupAsciiSymbol(name);
Handle<Code> call_code = Handle<Code>(isolate->builtins()->builtin(call));
@@ -344,30 +354,31 @@
Handle<DescriptorArray> Genesis::ComputeFunctionInstanceDescriptor(
PrototypePropertyMode prototypeMode) {
- Factory* factory = Isolate::Current()->factory();
Handle<DescriptorArray> descriptors =
- factory->NewDescriptorArray(prototypeMode == DONT_ADD_PROTOTYPE ? 4 : 5);
+ factory()->NewDescriptorArray(prototypeMode == DONT_ADD_PROTOTYPE
+ ? 4
+ : 5);
PropertyAttributes attributes =
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
{ // Add length.
- Handle<Proxy> proxy = factory->NewProxy(&Accessors::FunctionLength);
- CallbacksDescriptor d(*factory->length_symbol(), *proxy, attributes);
+ Handle<Proxy> proxy = factory()->NewProxy(&Accessors::FunctionLength);
+ CallbacksDescriptor d(*factory()->length_symbol(), *proxy, attributes);
descriptors->Set(0, &d);
}
{ // Add name.
- Handle<Proxy> proxy = factory->NewProxy(&Accessors::FunctionName);
- CallbacksDescriptor d(*factory->name_symbol(), *proxy, attributes);
+ Handle<Proxy> proxy = factory()->NewProxy(&Accessors::FunctionName);
+ CallbacksDescriptor d(*factory()->name_symbol(), *proxy, attributes);
descriptors->Set(1, &d);
}
{ // Add arguments.
- Handle<Proxy> proxy = factory->NewProxy(&Accessors::FunctionArguments);
- CallbacksDescriptor d(*factory->arguments_symbol(), *proxy, attributes);
+ Handle<Proxy> proxy = factory()->NewProxy(&Accessors::FunctionArguments);
+ CallbacksDescriptor d(*factory()->arguments_symbol(), *proxy, attributes);
descriptors->Set(2, &d);
}
{ // Add caller.
- Handle<Proxy> proxy = factory->NewProxy(&Accessors::FunctionCaller);
- CallbacksDescriptor d(*factory->caller_symbol(), *proxy, attributes);
+ Handle<Proxy> proxy = factory()->NewProxy(&Accessors::FunctionCaller);
+ CallbacksDescriptor d(*factory()->caller_symbol(), *proxy, attributes);
descriptors->Set(3, &d);
}
if (prototypeMode != DONT_ADD_PROTOTYPE) {
@@ -375,8 +386,8 @@
if (prototypeMode == ADD_WRITEABLE_PROTOTYPE) {
attributes = static_cast<PropertyAttributes>(attributes & ~READ_ONLY);
}
- Handle<Proxy> proxy = factory->NewProxy(&Accessors::FunctionPrototype);
- CallbacksDescriptor d(*factory->prototype_symbol(), *proxy, attributes);
+ Handle<Proxy> proxy = factory()->NewProxy(&Accessors::FunctionPrototype);
+ CallbacksDescriptor d(*factory()->prototype_symbol(), *proxy, attributes);
descriptors->Set(4, &d);
}
descriptors->Sort();
@@ -385,7 +396,7 @@
Handle<Map> Genesis::CreateFunctionMap(PrototypePropertyMode prototype_mode) {
- Handle<Map> map = FACTORY->NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
+ Handle<Map> map = factory()->NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
Handle<DescriptorArray> descriptors =
ComputeFunctionInstanceDescriptor(prototype_mode);
map->set_instance_descriptors(*descriptors);
@@ -394,32 +405,34 @@
}
-Handle<JSFunction> Genesis::CreateEmptyFunction() {
+Handle<JSFunction> Genesis::CreateEmptyFunction(Isolate* isolate) {
// Allocate the map for function instances. Maps are allocated first and their
// prototypes patched later, once empty function is created.
// Please note that the prototype property for function instances must be
// writable.
- global_context()->set_function_instance_map(
- *CreateFunctionMap(ADD_WRITEABLE_PROTOTYPE));
+ Handle<Map> function_instance_map =
+ CreateFunctionMap(ADD_WRITEABLE_PROTOTYPE);
+ global_context()->set_function_instance_map(*function_instance_map);
// Functions with this map will not have a 'prototype' property, and
// can not be used as constructors.
+ Handle<Map> function_without_prototype_map =
+ CreateFunctionMap(DONT_ADD_PROTOTYPE);
global_context()->set_function_without_prototype_map(
- *CreateFunctionMap(DONT_ADD_PROTOTYPE));
+ *function_without_prototype_map);
// Allocate the function map. This map is temporary, used only for processing
// of builtins.
// Later the map is replaced with writable prototype map, allocated below.
- global_context()->set_function_map(
- *CreateFunctionMap(ADD_READONLY_PROTOTYPE));
+ Handle<Map> function_map = CreateFunctionMap(ADD_READONLY_PROTOTYPE);
+ global_context()->set_function_map(*function_map);
// The final map for functions. Writeable prototype.
// This map is installed in MakeFunctionInstancePrototypeWritable.
function_instance_map_writable_prototype_ =
CreateFunctionMap(ADD_WRITEABLE_PROTOTYPE);
- Isolate* isolate = Isolate::Current();
Factory* factory = isolate->factory();
Heap* heap = isolate->heap();
@@ -474,8 +487,6 @@
function_instance_map_writable_prototype_->set_prototype(*empty_function);
// Allocate the function map first and then patch the prototype later
- Handle<Map> function_without_prototype_map(
- global_context()->function_without_prototype_map());
Handle<Map> empty_fm = factory->CopyMapDropDescriptors(
function_without_prototype_map);
empty_fm->set_instance_descriptors(
@@ -490,28 +501,31 @@
PrototypePropertyMode prototypeMode,
Handle<FixedArray> arguments,
Handle<FixedArray> caller) {
- Factory* factory = Isolate::Current()->factory();
Handle<DescriptorArray> descriptors =
- factory->NewDescriptorArray(prototypeMode == DONT_ADD_PROTOTYPE ? 4 : 5);
+ factory()->NewDescriptorArray(prototypeMode == DONT_ADD_PROTOTYPE
+ ? 4
+ : 5);
PropertyAttributes attributes = static_cast<PropertyAttributes>(
DONT_ENUM | DONT_DELETE | READ_ONLY);
{ // length
- Handle<Proxy> proxy = factory->NewProxy(&Accessors::FunctionLength);
- CallbacksDescriptor d(*factory->length_symbol(), *proxy, attributes);
+ Handle<Proxy> proxy = factory()->NewProxy(&Accessors::FunctionLength);
+ CallbacksDescriptor d(*factory()->length_symbol(), *proxy, attributes);
descriptors->Set(0, &d);
}
{ // name
- Handle<Proxy> proxy = factory->NewProxy(&Accessors::FunctionName);
- CallbacksDescriptor d(*factory->name_symbol(), *proxy, attributes);
+ Handle<Proxy> proxy = factory()->NewProxy(&Accessors::FunctionName);
+ CallbacksDescriptor d(*factory()->name_symbol(), *proxy, attributes);
descriptors->Set(1, &d);
}
{ // arguments
- CallbacksDescriptor d(*factory->arguments_symbol(), *arguments, attributes);
+ CallbacksDescriptor d(*factory()->arguments_symbol(),
+ *arguments,
+ attributes);
descriptors->Set(2, &d);
}
{ // caller
- CallbacksDescriptor d(*factory->caller_symbol(), *caller, attributes);
+ CallbacksDescriptor d(*factory()->caller_symbol(), *caller, attributes);
descriptors->Set(3, &d);
}
@@ -520,8 +534,8 @@
if (prototypeMode == ADD_WRITEABLE_PROTOTYPE) {
attributes = static_cast<PropertyAttributes>(attributes & ~READ_ONLY);
}
- Handle<Proxy> proxy = factory->NewProxy(&Accessors::FunctionPrototype);
- CallbacksDescriptor d(*factory->prototype_symbol(), *proxy, attributes);
+ Handle<Proxy> proxy = factory()->NewProxy(&Accessors::FunctionPrototype);
+ CallbacksDescriptor d(*factory()->prototype_symbol(), *proxy, attributes);
descriptors->Set(4, &d);
}
@@ -533,14 +547,11 @@
// ECMAScript 5th Edition, 13.2.3
Handle<JSFunction> Genesis::CreateThrowTypeErrorFunction(
Builtins::Name builtin) {
- Isolate* isolate = Isolate::Current();
- Factory* factory = isolate->factory();
-
- Handle<String> name = factory->LookupAsciiSymbol("ThrowTypeError");
+ Handle<String> name = factory()->LookupAsciiSymbol("ThrowTypeError");
Handle<JSFunction> throw_type_error =
- factory->NewFunctionWithoutPrototype(name, kStrictMode);
+ factory()->NewFunctionWithoutPrototype(name, kStrictMode);
Handle<Code> code = Handle<Code>(
- isolate->builtins()->builtin(builtin));
+ isolate()->builtins()->builtin(builtin));
throw_type_error->set_map(global_context()->strict_mode_function_map());
throw_type_error->set_code(*code);
@@ -558,7 +569,7 @@
Handle<JSFunction> empty_function,
Handle<FixedArray> arguments_callbacks,
Handle<FixedArray> caller_callbacks) {
- Handle<Map> map = FACTORY->NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
+ Handle<Map> map = factory()->NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
Handle<DescriptorArray> descriptors =
ComputeStrictFunctionInstanceDescriptor(prototype_mode,
arguments_callbacks,
@@ -573,26 +584,32 @@
void Genesis::CreateStrictModeFunctionMaps(Handle<JSFunction> empty) {
// Create the callbacks arrays for ThrowTypeError functions.
// The get/set callacks are filled in after the maps are created below.
- Factory* factory = Isolate::Current()->factory();
+ Factory* factory = empty->GetIsolate()->factory();
Handle<FixedArray> arguments = factory->NewFixedArray(2, TENURED);
Handle<FixedArray> caller = factory->NewFixedArray(2, TENURED);
// Allocate map for the strict mode function instances.
+ Handle<Map> strict_mode_function_instance_map =
+ CreateStrictModeFunctionMap(
+ ADD_WRITEABLE_PROTOTYPE, empty, arguments, caller);
global_context()->set_strict_mode_function_instance_map(
- *CreateStrictModeFunctionMap(
- ADD_WRITEABLE_PROTOTYPE, empty, arguments, caller));
+ *strict_mode_function_instance_map);
// Allocate map for the prototype-less strict mode instances.
+ Handle<Map> strict_mode_function_without_prototype_map =
+ CreateStrictModeFunctionMap(
+ DONT_ADD_PROTOTYPE, empty, arguments, caller);
global_context()->set_strict_mode_function_without_prototype_map(
- *CreateStrictModeFunctionMap(
- DONT_ADD_PROTOTYPE, empty, arguments, caller));
+ *strict_mode_function_without_prototype_map);
// Allocate map for the strict mode functions. This map is temporary, used
// only for processing of builtins.
// Later the map is replaced with writable prototype map, allocated below.
+ Handle<Map> strict_mode_function_map =
+ CreateStrictModeFunctionMap(
+ ADD_READONLY_PROTOTYPE, empty, arguments, caller);
global_context()->set_strict_mode_function_map(
- *CreateStrictModeFunctionMap(
- ADD_READONLY_PROTOTYPE, empty, arguments, caller));
+ *strict_mode_function_map);
// The final map for the strict mode functions. Writeable prototype.
// This map is installed in MakeFunctionInstancePrototypeWritable.
@@ -616,7 +633,7 @@
static void AddToWeakGlobalContextList(Context* context) {
ASSERT(context->IsGlobalContext());
- Heap* heap = Isolate::Current()->heap();
+ Heap* heap = context->GetIsolate()->heap();
#ifdef DEBUG
{ // NOLINT
ASSERT(context->get(Context::NEXT_CONTEXT_LINK)->IsUndefined());
@@ -634,15 +651,14 @@
void Genesis::CreateRoots() {
- Isolate* isolate = Isolate::Current();
// Allocate the global context FixedArray first and then patch the
// closure and extension object later (we need the empty function
// and the global object, but in order to create those, we need the
// global context).
- global_context_ = Handle<Context>::cast(isolate->global_handles()->Create(
- *isolate->factory()->NewGlobalContext()));
+ global_context_ = Handle<Context>::cast(isolate()->global_handles()->Create(
+ *factory()->NewGlobalContext()));
AddToWeakGlobalContextList(*global_context_);
- isolate->set_context(*global_context());
+ isolate()->set_context(*global_context());
// Allocate the message listeners object.
{
@@ -685,17 +701,13 @@
}
}
- Isolate* isolate = Isolate::Current();
- Factory* factory = isolate->factory();
- Heap* heap = isolate->heap();
-
if (js_global_template.is_null()) {
- Handle<String> name = Handle<String>(heap->empty_symbol());
- Handle<Code> code = Handle<Code>(isolate->builtins()->builtin(
+ Handle<String> name = Handle<String>(heap()->empty_symbol());
+ Handle<Code> code = Handle<Code>(isolate()->builtins()->builtin(
Builtins::kIllegal));
js_global_function =
- factory->NewFunction(name, JS_GLOBAL_OBJECT_TYPE,
- JSGlobalObject::kSize, code, true);
+ factory()->NewFunction(name, JS_GLOBAL_OBJECT_TYPE,
+ JSGlobalObject::kSize, code, true);
// Change the constructor property of the prototype of the
// hidden global function to refer to the Object function.
Handle<JSObject> prototype =
@@ -703,20 +715,20 @@
JSObject::cast(js_global_function->instance_prototype()));
SetLocalPropertyNoThrow(
prototype,
- factory->constructor_symbol(),
- isolate->object_function(),
+ factory()->constructor_symbol(),
+ isolate()->object_function(),
NONE);
} else {
Handle<FunctionTemplateInfo> js_global_constructor(
FunctionTemplateInfo::cast(js_global_template->constructor()));
js_global_function =
- factory->CreateApiFunction(js_global_constructor,
- factory->InnerGlobalObject);
+ factory()->CreateApiFunction(js_global_constructor,
+ factory()->InnerGlobalObject);
}
js_global_function->initial_map()->set_is_hidden_prototype();
Handle<GlobalObject> inner_global =
- factory->NewGlobalObject(js_global_function);
+ factory()->NewGlobalObject(js_global_function);
if (inner_global_out != NULL) {
*inner_global_out = inner_global;
}
@@ -724,23 +736,23 @@
// Step 2: create or re-initialize the global proxy object.
Handle<JSFunction> global_proxy_function;
if (global_template.IsEmpty()) {
- Handle<String> name = Handle<String>(heap->empty_symbol());
- Handle<Code> code = Handle<Code>(isolate->builtins()->builtin(
+ Handle<String> name = Handle<String>(heap()->empty_symbol());
+ Handle<Code> code = Handle<Code>(isolate()->builtins()->builtin(
Builtins::kIllegal));
global_proxy_function =
- factory->NewFunction(name, JS_GLOBAL_PROXY_TYPE,
- JSGlobalProxy::kSize, code, true);
+ factory()->NewFunction(name, JS_GLOBAL_PROXY_TYPE,
+ JSGlobalProxy::kSize, code, true);
} else {
Handle<ObjectTemplateInfo> data =
v8::Utils::OpenHandle(*global_template);
Handle<FunctionTemplateInfo> global_constructor(
FunctionTemplateInfo::cast(data->constructor()));
global_proxy_function =
- factory->CreateApiFunction(global_constructor,
- factory->OuterGlobalObject);
+ factory()->CreateApiFunction(global_constructor,
+ factory()->OuterGlobalObject);
}
- Handle<String> global_name = factory->LookupAsciiSymbol("global");
+ Handle<String> global_name = factory()->LookupAsciiSymbol("global");
global_proxy_function->shared()->set_instance_class_name(*global_name);
global_proxy_function->initial_map()->set_is_access_check_needed(true);
@@ -754,7 +766,7 @@
Handle<JSGlobalProxy>::cast(global_object));
} else {
return Handle<JSGlobalProxy>::cast(
- factory->NewJSObject(global_proxy_function, TENURED));
+ factory()->NewJSObject(global_proxy_function, TENURED));
}
}
@@ -779,7 +791,7 @@
static const PropertyAttributes attributes =
static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE);
ForceSetProperty(builtins_global,
- FACTORY->LookupAsciiSymbol("global"),
+ factory()->LookupAsciiSymbol("global"),
inner_global,
attributes);
// Setup the reference from the global object to the builtins object.
@@ -807,7 +819,7 @@
// object reinitialization.
global_context()->set_security_token(*inner_global);
- Isolate* isolate = Isolate::Current();
+ Isolate* isolate = inner_global->GetIsolate();
Factory* factory = isolate->factory();
Heap* heap = isolate->heap();
@@ -1157,17 +1169,26 @@
}
-bool Genesis::CompileBuiltin(int index) {
+bool Genesis::CompileBuiltin(Isolate* isolate, int index) {
Vector<const char> name = Natives::GetScriptName(index);
Handle<String> source_code =
- Isolate::Current()->bootstrapper()->NativesSourceLookup(index);
+ isolate->bootstrapper()->NativesSourceLookup(index);
+ return CompileNative(name, source_code);
+}
+
+
+bool Genesis::CompileExperimentalBuiltin(Isolate* isolate, int index) {
+ Vector<const char> name = ExperimentalNatives::GetScriptName(index);
+ Factory* factory = isolate->factory();
+ Handle<String> source_code =
+ factory->NewStringFromAscii(ExperimentalNatives::GetScriptSource(index));
return CompileNative(name, source_code);
}
bool Genesis::CompileNative(Vector<const char> name, Handle<String> source) {
HandleScope scope;
- Isolate* isolate = Isolate::Current();
+ Isolate* isolate = source->GetIsolate();
#ifdef ENABLE_DEBUGGER_SUPPORT
isolate->debugger()->set_compiling_natives(true);
#endif
@@ -1192,7 +1213,7 @@
v8::Extension* extension,
Handle<Context> top_context,
bool use_runtime_context) {
- Factory* factory = Isolate::Current()->factory();
+ Factory* factory = source->GetIsolate()->factory();
HandleScope scope;
Handle<SharedFunctionInfo> function_info;
@@ -1239,14 +1260,15 @@
}
-#define INSTALL_NATIVE(Type, name, var) \
- Handle<String> var##_name = factory->LookupAsciiSymbol(name); \
- global_context()->set_##var(Type::cast( \
- global_context()->builtins()->GetPropertyNoExceptionThrown(*var##_name)));
+#define INSTALL_NATIVE(Type, name, var) \
+ Handle<String> var##_name = factory()->LookupAsciiSymbol(name); \
+ Object* var##_native = \
+ global_context()->builtins()->GetPropertyNoExceptionThrown( \
+ *var##_name); \
+ global_context()->set_##var(Type::cast(var##_native));
void Genesis::InstallNativeFunctions() {
- Factory* factory = Isolate::Current()->factory();
HandleScope scope;
INSTALL_NATIVE(JSFunction, "CreateDate", create_date_fun);
INSTALL_NATIVE(JSFunction, "ToNumber", to_number_fun);
@@ -1269,25 +1291,23 @@
bool Genesis::InstallNatives() {
HandleScope scope;
- Isolate* isolate = Isolate::Current();
- Factory* factory = isolate->factory();
- Heap* heap = isolate->heap();
// Create a function for the builtins object. Allocate space for the
// JavaScript builtins, a reference to the builtins object
// (itself) and a reference to the global_context directly in the object.
Handle<Code> code = Handle<Code>(
- isolate->builtins()->builtin(Builtins::kIllegal));
+ isolate()->builtins()->builtin(Builtins::kIllegal));
Handle<JSFunction> builtins_fun =
- factory->NewFunction(factory->empty_symbol(), JS_BUILTINS_OBJECT_TYPE,
- JSBuiltinsObject::kSize, code, true);
+ factory()->NewFunction(factory()->empty_symbol(),
+ JS_BUILTINS_OBJECT_TYPE,
+ JSBuiltinsObject::kSize, code, true);
- Handle<String> name = factory->LookupAsciiSymbol("builtins");
+ Handle<String> name = factory()->LookupAsciiSymbol("builtins");
builtins_fun->shared()->set_instance_class_name(*name);
// Allocate the builtins object.
Handle<JSBuiltinsObject> builtins =
- Handle<JSBuiltinsObject>::cast(factory->NewGlobalObject(builtins_fun));
+ Handle<JSBuiltinsObject>::cast(factory()->NewGlobalObject(builtins_fun));
builtins->set_builtins(*builtins);
builtins->set_global_context(*global_context());
builtins->set_global_receiver(*builtins);
@@ -1298,7 +1318,7 @@
// global object.
static const PropertyAttributes attributes =
static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE);
- Handle<String> global_symbol = factory->LookupAsciiSymbol("global");
+ Handle<String> global_symbol = factory()->LookupAsciiSymbol("global");
Handle<Object> global_obj(global_context()->global());
SetLocalPropertyNoThrow(builtins, global_symbol, global_obj, attributes);
@@ -1307,12 +1327,13 @@
// Create a bridge function that has context in the global context.
Handle<JSFunction> bridge =
- factory->NewFunction(factory->empty_symbol(), factory->undefined_value());
- ASSERT(bridge->context() == *isolate->global_context());
+ factory()->NewFunction(factory()->empty_symbol(),
+ factory()->undefined_value());
+ ASSERT(bridge->context() == *isolate()->global_context());
// Allocate the builtins context.
Handle<Context> context =
- factory->NewFunctionContext(Context::MIN_CONTEXT_SLOTS, bridge);
+ factory()->NewFunctionContext(Context::MIN_CONTEXT_SLOTS, bridge);
context->set_global(*builtins); // override builtins global object
global_context()->set_runtime_context(*context);
@@ -1321,113 +1342,113 @@
// Builtin functions for Script.
Handle<JSFunction> script_fun =
InstallFunction(builtins, "Script", JS_VALUE_TYPE, JSValue::kSize,
- isolate->initial_object_prototype(),
+ isolate()->initial_object_prototype(),
Builtins::kIllegal, false);
Handle<JSObject> prototype =
- factory->NewJSObject(isolate->object_function(), TENURED);
+ factory()->NewJSObject(isolate()->object_function(), TENURED);
SetPrototype(script_fun, prototype);
global_context()->set_script_function(*script_fun);
// Add 'source' and 'data' property to scripts.
PropertyAttributes common_attributes =
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
- Handle<Proxy> proxy_source = factory->NewProxy(&Accessors::ScriptSource);
+ Handle<Proxy> proxy_source = factory()->NewProxy(&Accessors::ScriptSource);
Handle<DescriptorArray> script_descriptors =
- factory->CopyAppendProxyDescriptor(
- factory->empty_descriptor_array(),
- factory->LookupAsciiSymbol("source"),
+ factory()->CopyAppendProxyDescriptor(
+ factory()->empty_descriptor_array(),
+ factory()->LookupAsciiSymbol("source"),
proxy_source,
common_attributes);
- Handle<Proxy> proxy_name = factory->NewProxy(&Accessors::ScriptName);
+ Handle<Proxy> proxy_name = factory()->NewProxy(&Accessors::ScriptName);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendProxyDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("name"),
+ factory()->LookupAsciiSymbol("name"),
proxy_name,
common_attributes);
- Handle<Proxy> proxy_id = factory->NewProxy(&Accessors::ScriptId);
+ Handle<Proxy> proxy_id = factory()->NewProxy(&Accessors::ScriptId);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendProxyDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("id"),
+ factory()->LookupAsciiSymbol("id"),
proxy_id,
common_attributes);
Handle<Proxy> proxy_line_offset =
- factory->NewProxy(&Accessors::ScriptLineOffset);
+ factory()->NewProxy(&Accessors::ScriptLineOffset);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendProxyDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("line_offset"),
+ factory()->LookupAsciiSymbol("line_offset"),
proxy_line_offset,
common_attributes);
Handle<Proxy> proxy_column_offset =
- factory->NewProxy(&Accessors::ScriptColumnOffset);
+ factory()->NewProxy(&Accessors::ScriptColumnOffset);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendProxyDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("column_offset"),
+ factory()->LookupAsciiSymbol("column_offset"),
proxy_column_offset,
common_attributes);
- Handle<Proxy> proxy_data = factory->NewProxy(&Accessors::ScriptData);
+ Handle<Proxy> proxy_data = factory()->NewProxy(&Accessors::ScriptData);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendProxyDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("data"),
+ factory()->LookupAsciiSymbol("data"),
proxy_data,
common_attributes);
- Handle<Proxy> proxy_type = factory->NewProxy(&Accessors::ScriptType);
+ Handle<Proxy> proxy_type = factory()->NewProxy(&Accessors::ScriptType);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendProxyDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("type"),
+ factory()->LookupAsciiSymbol("type"),
proxy_type,
common_attributes);
Handle<Proxy> proxy_compilation_type =
- factory->NewProxy(&Accessors::ScriptCompilationType);
+ factory()->NewProxy(&Accessors::ScriptCompilationType);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendProxyDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("compilation_type"),
+ factory()->LookupAsciiSymbol("compilation_type"),
proxy_compilation_type,
common_attributes);
Handle<Proxy> proxy_line_ends =
- factory->NewProxy(&Accessors::ScriptLineEnds);
+ factory()->NewProxy(&Accessors::ScriptLineEnds);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendProxyDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("line_ends"),
+ factory()->LookupAsciiSymbol("line_ends"),
proxy_line_ends,
common_attributes);
Handle<Proxy> proxy_context_data =
- factory->NewProxy(&Accessors::ScriptContextData);
+ factory()->NewProxy(&Accessors::ScriptContextData);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendProxyDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("context_data"),
+ factory()->LookupAsciiSymbol("context_data"),
proxy_context_data,
common_attributes);
Handle<Proxy> proxy_eval_from_script =
- factory->NewProxy(&Accessors::ScriptEvalFromScript);
+ factory()->NewProxy(&Accessors::ScriptEvalFromScript);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendProxyDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("eval_from_script"),
+ factory()->LookupAsciiSymbol("eval_from_script"),
proxy_eval_from_script,
common_attributes);
Handle<Proxy> proxy_eval_from_script_position =
- factory->NewProxy(&Accessors::ScriptEvalFromScriptPosition);
+ factory()->NewProxy(&Accessors::ScriptEvalFromScriptPosition);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendProxyDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("eval_from_script_position"),
+ factory()->LookupAsciiSymbol("eval_from_script_position"),
proxy_eval_from_script_position,
common_attributes);
Handle<Proxy> proxy_eval_from_function_name =
- factory->NewProxy(&Accessors::ScriptEvalFromFunctionName);
+ factory()->NewProxy(&Accessors::ScriptEvalFromFunctionName);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendProxyDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("eval_from_function_name"),
+ factory()->LookupAsciiSymbol("eval_from_function_name"),
proxy_eval_from_function_name,
common_attributes);
@@ -1435,9 +1456,9 @@
script_map->set_instance_descriptors(*script_descriptors);
// Allocate the empty script.
- Handle<Script> script = factory->NewScript(factory->empty_string());
+ Handle<Script> script = factory()->NewScript(factory()->empty_string());
script->set_type(Smi::FromInt(Script::TYPE_NATIVE));
- heap->public_set_empty_script(*script);
+ heap()->public_set_empty_script(*script);
}
{
// Builtin function for OpaqueReference -- a JSValue-based object,
@@ -1446,10 +1467,10 @@
Handle<JSFunction> opaque_reference_fun =
InstallFunction(builtins, "OpaqueReference", JS_VALUE_TYPE,
JSValue::kSize,
- isolate->initial_object_prototype(),
+ isolate()->initial_object_prototype(),
Builtins::kIllegal, false);
Handle<JSObject> prototype =
- factory->NewJSObject(isolate->object_function(), TENURED);
+ factory()->NewJSObject(isolate()->object_function(), TENURED);
SetPrototype(opaque_reference_fun, prototype);
global_context()->set_opaque_reference_function(*opaque_reference_fun);
}
@@ -1468,23 +1489,23 @@
"InternalArray",
JS_ARRAY_TYPE,
JSArray::kSize,
- isolate->initial_object_prototype(),
+ isolate()->initial_object_prototype(),
Builtins::kArrayCode,
true);
Handle<JSObject> prototype =
- factory->NewJSObject(isolate->object_function(), TENURED);
+ factory()->NewJSObject(isolate()->object_function(), TENURED);
SetPrototype(array_function, prototype);
array_function->shared()->set_construct_stub(
- isolate->builtins()->builtin(Builtins::kArrayConstructCode));
+ isolate()->builtins()->builtin(Builtins::kArrayConstructCode));
array_function->shared()->DontAdaptArguments();
// Make "length" magic on instances.
Handle<DescriptorArray> array_descriptors =
- factory->CopyAppendProxyDescriptor(
- factory->empty_descriptor_array(),
- factory->length_symbol(),
- factory->NewProxy(&Accessors::ArrayLength),
+ factory()->CopyAppendProxyDescriptor(
+ factory()->empty_descriptor_array(),
+ factory()->length_symbol(),
+ factory()->NewProxy(&Accessors::ArrayLength),
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE));
array_function->initial_map()->set_instance_descriptors(
@@ -1500,8 +1521,7 @@
for (int i = Natives::GetDebuggerCount();
i < Natives::GetBuiltinsCount();
i++) {
- Vector<const char> name = Natives::GetScriptName(i);
- if (!CompileBuiltin(i)) return false;
+ if (!CompileBuiltin(isolate(), i)) return false;
// TODO(ager): We really only need to install the JS builtin
// functions on the builtins object after compiling and running
// runtime.js.
@@ -1521,9 +1541,9 @@
InstallBuiltinFunctionIds();
// Install Function.prototype.call and apply.
- { Handle<String> key = factory->function_class_symbol();
+ { Handle<String> key = factory()->function_class_symbol();
Handle<JSFunction> function =
- Handle<JSFunction>::cast(GetProperty(isolate->global(), key));
+ Handle<JSFunction>::cast(GetProperty(isolate()->global(), key));
Handle<JSObject> proto =
Handle<JSObject>(JSObject::cast(function->instance_prototype()));
@@ -1565,7 +1585,7 @@
// Add initial map.
Handle<Map> initial_map =
- factory->NewMap(JS_ARRAY_TYPE, JSRegExpResult::kSize);
+ factory()->NewMap(JS_ARRAY_TYPE, JSRegExpResult::kSize);
initial_map->set_constructor(*array_constructor);
// Set prototype on map.
@@ -1579,13 +1599,13 @@
ASSERT_EQ(1, array_descriptors->number_of_descriptors());
Handle<DescriptorArray> reresult_descriptors =
- factory->NewDescriptorArray(3);
+ factory()->NewDescriptorArray(3);
reresult_descriptors->CopyFrom(0, *array_descriptors, 0);
int enum_index = 0;
{
- FieldDescriptor index_field(heap->index_symbol(),
+ FieldDescriptor index_field(heap()->index_symbol(),
JSRegExpResult::kIndexIndex,
NONE,
enum_index++);
@@ -1593,7 +1613,7 @@
}
{
- FieldDescriptor input_field(heap->input_symbol(),
+ FieldDescriptor input_field(heap()->input_symbol(),
JSRegExpResult::kInputIndex,
NONE,
enum_index++);
@@ -1618,10 +1638,22 @@
}
+bool Genesis::InstallExperimentalNatives() {
+ if (FLAG_harmony_proxies) {
+ for (int i = ExperimentalNatives::GetDebuggerCount();
+ i < ExperimentalNatives::GetBuiltinsCount();
+ i++) {
+ if (!CompileExperimentalBuiltin(isolate(), i)) return false;
+ }
+ }
+ return true;
+}
+
+
static Handle<JSObject> ResolveBuiltinIdHolder(
Handle<Context> global_context,
const char* holder_expr) {
- Factory* factory = Isolate::Current()->factory();
+ Factory* factory = global_context->GetIsolate()->factory();
Handle<GlobalObject> global(global_context->global());
const char* period_pos = strchr(holder_expr, '.');
if (period_pos == NULL) {
@@ -1640,7 +1672,8 @@
static void InstallBuiltinFunctionId(Handle<JSObject> holder,
const char* function_name,
BuiltinFunctionId id) {
- Handle<String> name = FACTORY->LookupAsciiSymbol(function_name);
+ Factory* factory = holder->GetIsolate()->factory();
+ Handle<String> name = factory->LookupAsciiSymbol(function_name);
Object* function_object = holder->GetProperty(*name)->ToObjectUnchecked();
Handle<JSFunction> function(JSFunction::cast(function_object));
function->shared()->set_function_data(Smi::FromInt(id));
@@ -1667,13 +1700,14 @@
F(16, global_context()->regexp_function())
-static FixedArray* CreateCache(int size, JSFunction* factory) {
+static FixedArray* CreateCache(int size, JSFunction* factory_function) {
+ Factory* factory = factory_function->GetIsolate()->factory();
// Caches are supposed to live for a long time, allocate in old space.
int array_size = JSFunctionResultCache::kEntriesIndex + 2 * size;
// Cannot use cast as object is not fully initialized yet.
JSFunctionResultCache* cache = reinterpret_cast<JSFunctionResultCache*>(
- *FACTORY->NewFixedArrayWithHoles(array_size, TENURED));
- cache->set(JSFunctionResultCache::kFactoryIndex, factory);
+ *factory->NewFixedArrayWithHoles(array_size, TENURED));
+ cache->set(JSFunctionResultCache::kFactoryIndex, factory_function);
cache->MakeZeroSize();
return cache;
}
@@ -1712,7 +1746,7 @@
bool Bootstrapper::InstallExtensions(Handle<Context> global_context,
v8::ExtensionConfiguration* extensions) {
- Isolate* isolate = Isolate::Current();
+ Isolate* isolate = global_context->GetIsolate();
BootstrapperActive active;
SaveContext saved_context(isolate);
isolate->set_context(*global_context);
@@ -1723,7 +1757,7 @@
void Genesis::InstallSpecialObjects(Handle<Context> global_context) {
- Factory* factory = Isolate::Current()->factory();
+ Factory* factory = global_context->GetIsolate()->factory();
HandleScope scope;
Handle<JSGlobalObject> js_global(
JSGlobalObject::cast(global_context->global()));
@@ -1859,9 +1893,10 @@
bool Genesis::InstallJSBuiltins(Handle<JSBuiltinsObject> builtins) {
HandleScope scope;
+ Factory* factory = builtins->GetIsolate()->factory();
for (int i = 0; i < Builtins::NumberOfJavaScriptBuiltins(); i++) {
Builtins::JavaScript id = static_cast<Builtins::JavaScript>(i);
- Handle<String> name = FACTORY->LookupAsciiSymbol(Builtins::GetName(id));
+ Handle<String> name = factory->LookupAsciiSymbol(Builtins::GetName(id));
Object* function_object = builtins->GetPropertyNoExceptionThrown(*name);
Handle<JSFunction> function
= Handle<JSFunction>(JSFunction::cast(function_object));
@@ -1910,13 +1945,12 @@
ASSERT(object->IsInstanceOf(
FunctionTemplateInfo::cast(object_template->constructor())));
- Isolate* isolate = Isolate::Current();
bool pending_exception = false;
Handle<JSObject> obj =
Execution::InstantiateObject(object_template, &pending_exception);
if (pending_exception) {
- ASSERT(isolate->has_pending_exception());
- isolate->clear_pending_exception();
+ ASSERT(isolate()->has_pending_exception());
+ isolate()->clear_pending_exception();
return false;
}
TransferObject(obj, object);
@@ -2015,6 +2049,7 @@
void Genesis::TransferObject(Handle<JSObject> from, Handle<JSObject> to) {
HandleScope outer;
+ Factory* factory = from->GetIsolate()->factory();
ASSERT(!from->IsJSArray());
ASSERT(!to->IsJSArray());
@@ -2024,7 +2059,7 @@
// Transfer the prototype (new map is needed).
Handle<Map> old_to_map = Handle<Map>(to->map());
- Handle<Map> new_to_map = FACTORY->CopyMapDropTransitions(old_to_map);
+ Handle<Map> new_to_map = factory->CopyMapDropTransitions(old_to_map);
new_to_map->set_prototype(from->map()->prototype());
to->set_map(*new_to_map);
}
@@ -2045,10 +2080,10 @@
}
-Genesis::Genesis(Handle<Object> global_object,
+Genesis::Genesis(Isolate* isolate,
+ Handle<Object> global_object,
v8::Handle<v8::ObjectTemplate> global_template,
- v8::ExtensionConfiguration* extensions) {
- Isolate* isolate = Isolate::Current();
+ v8::ExtensionConfiguration* extensions) : isolate_(isolate) {
result_ = Handle<Context>::null();
// If V8 isn't running and cannot be initialized, just return.
if (!V8::IsRunning() && !V8::Initialize(NULL)) return;
@@ -2078,7 +2113,7 @@
} else {
// We get here if there was no context snapshot.
CreateRoots();
- Handle<JSFunction> empty_function = CreateEmptyFunction();
+ Handle<JSFunction> empty_function = CreateEmptyFunction(isolate);
CreateStrictModeFunctionMaps(empty_function);
Handle<GlobalObject> inner_global;
Handle<JSGlobalProxy> global_proxy =
@@ -2095,6 +2130,9 @@
isolate->counters()->contexts_created_from_scratch()->Increment();
}
+ // Install experimental natives.
+ if (!InstallExperimentalNatives()) return;
+
result_ = global_context_;
}
diff --git a/src/bootstrapper.h b/src/bootstrapper.h
index 3e158d6..018ceef 100644
--- a/src/bootstrapper.h
+++ b/src/bootstrapper.h
@@ -93,6 +93,7 @@
// Creates a JavaScript Global Context with initial object graph.
// The returned value is a global handle casted to V8Environment*.
Handle<Context> CreateEnvironment(
+ Isolate* isolate,
Handle<Object> global_object,
v8::Handle<v8::ObjectTemplate> global_template,
v8::ExtensionConfiguration* extensions);
diff --git a/src/builtins.cc b/src/builtins.cc
index 72f9d57..ae3dab4 100644
--- a/src/builtins.cc
+++ b/src/builtins.cc
@@ -1594,10 +1594,11 @@
void Builtins::Setup(bool create_heap_objects) {
ASSERT(!initialized_);
- Heap* heap = Isolate::Current()->heap();
+ Isolate* isolate = Isolate::Current();
+ Heap* heap = isolate->heap();
// Create a scope for the handles in the builtins.
- HandleScope scope;
+ HandleScope scope(isolate);
const BuiltinDesc* functions = BuiltinFunctionTable::functions();
@@ -1609,7 +1610,7 @@
// separate code object for each one.
for (int i = 0; i < builtin_count; i++) {
if (create_heap_objects) {
- MacroAssembler masm(buffer, sizeof buffer);
+ MacroAssembler masm(isolate, buffer, sizeof buffer);
// Generate the code/adaptor.
typedef void (*Generator)(MacroAssembler*, int, BuiltinExtraArguments);
Generator g = FUNCTION_CAST<Generator>(functions[i].generator);
@@ -1634,7 +1635,7 @@
}
}
// Log the event and add the code to the builtins array.
- PROFILE(ISOLATE,
+ PROFILE(isolate,
CodeCreateEvent(Logger::BUILTIN_TAG,
Code::cast(code),
functions[i].s_name));
diff --git a/src/checks.h b/src/checks.h
index 2bb94bb..a560b2f 100644
--- a/src/checks.h
+++ b/src/checks.h
@@ -271,6 +271,8 @@
#define ASSERT_EQ(v1, v2) CHECK_EQ(v1, v2)
#define ASSERT_NE(v1, v2) CHECK_NE(v1, v2)
#define ASSERT_GE(v1, v2) CHECK_GE(v1, v2)
+#define ASSERT_LT(v1, v2) CHECK_LT(v1, v2)
+#define ASSERT_LE(v1, v2) CHECK_LE(v1, v2)
#define SLOW_ASSERT(condition) if (EnableSlowAsserts()) CHECK(condition)
#else
#define ASSERT_RESULT(expr) (expr)
@@ -278,6 +280,8 @@
#define ASSERT_EQ(v1, v2) ((void) 0)
#define ASSERT_NE(v1, v2) ((void) 0)
#define ASSERT_GE(v1, v2) ((void) 0)
+#define ASSERT_LT(v1, v2) ((void) 0)
+#define ASSERT_LE(v1, v2) ((void) 0)
#define SLOW_ASSERT(condition) ((void) 0)
#endif
// Static asserts has no impact on runtime performance, so they can be
diff --git a/src/code-stubs.cc b/src/code-stubs.cc
index 2ecd336..f680c60 100644
--- a/src/code-stubs.cc
+++ b/src/code-stubs.cc
@@ -95,7 +95,7 @@
HandleScope scope(isolate);
// Generate the new code.
- MacroAssembler masm(NULL, 256);
+ MacroAssembler masm(isolate, NULL, 256);
GenerateCode(&masm);
// Create the code object.
@@ -132,7 +132,7 @@
Code* code;
if (!FindCodeInCache(&code)) {
// Generate the new code.
- MacroAssembler masm(NULL, 256);
+ MacroAssembler masm(Isolate::Current(), NULL, 256);
GenerateCode(&masm);
Heap* heap = masm.isolate()->heap();
diff --git a/src/code-stubs.h b/src/code-stubs.h
index d408034..56ef072 100644
--- a/src/code-stubs.h
+++ b/src/code-stubs.h
@@ -37,7 +37,6 @@
// as only the stubs up to and including Instanceof allows nested stub calls.
#define CODE_STUB_LIST_ALL_PLATFORMS(V) \
V(CallFunction) \
- V(GenericBinaryOp) \
V(TypeRecordingBinaryOp) \
V(StringAdd) \
V(SubString) \
@@ -50,7 +49,6 @@
V(Instanceof) \
V(ConvertToDouble) \
V(WriteInt32ToHeapNumber) \
- V(IntegerMod) \
V(StackCheck) \
V(FastNewClosure) \
V(FastNewContext) \
@@ -164,10 +162,10 @@
// lazily generated function should be fully optimized or not.
virtual InLoopFlag InLoop() { return NOT_IN_LOOP; }
- // GenericBinaryOpStub needs to override this.
+ // TypeRecordingBinaryOpStub needs to override this.
virtual int GetCodeKind();
- // GenericBinaryOpStub needs to override this.
+ // TypeRecordingBinaryOpStub needs to override this.
virtual InlineCacheState GetICState() {
return UNINITIALIZED;
}
diff --git a/src/codegen-inl.h b/src/codegen-inl.h
deleted file mode 100644
index f7da54a..0000000
--- a/src/codegen-inl.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-#ifndef V8_CODEGEN_INL_H_
-#define V8_CODEGEN_INL_H_
-
-#include "codegen.h"
-#include "compiler.h"
-#include "register-allocator-inl.h"
-
-#if V8_TARGET_ARCH_IA32
-#include "ia32/codegen-ia32-inl.h"
-#elif V8_TARGET_ARCH_X64
-#include "x64/codegen-x64-inl.h"
-#elif V8_TARGET_ARCH_ARM
-#include "arm/codegen-arm-inl.h"
-#elif V8_TARGET_ARCH_MIPS
-#include "mips/codegen-mips-inl.h"
-#else
-#error Unsupported target architecture.
-#endif
-
-
-namespace v8 {
-namespace internal {
-
-Handle<Script> CodeGenerator::script() { return info_->script(); }
-
-bool CodeGenerator::is_eval() { return info_->is_eval(); }
-
-Scope* CodeGenerator::scope() { return info_->function()->scope(); }
-
-bool CodeGenerator::is_strict_mode() {
- return info_->function()->strict_mode();
-}
-
-StrictModeFlag CodeGenerator::strict_mode_flag() {
- return is_strict_mode() ? kStrictMode : kNonStrictMode;
-}
-
-} } // namespace v8::internal
-
-#endif // V8_CODEGEN_INL_H_
diff --git a/src/codegen.cc b/src/codegen.cc
index 03f64a1..4bbe6ae 100644
--- a/src/codegen.cc
+++ b/src/codegen.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -28,16 +28,14 @@
#include "v8.h"
#include "bootstrapper.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "compiler.h"
#include "debug.h"
#include "prettyprinter.h"
-#include "register-allocator-inl.h"
#include "rewriter.h"
#include "runtime.h"
#include "scopeinfo.h"
#include "stub-cache.h"
-#include "virtual-frame-inl.h"
namespace v8 {
namespace internal {
@@ -61,64 +59,6 @@
#undef __
-void CodeGenerator::ProcessDeferred() {
- while (!deferred_.is_empty()) {
- DeferredCode* code = deferred_.RemoveLast();
- ASSERT(masm_ == code->masm());
- // Record position of deferred code stub.
- masm_->positions_recorder()->RecordStatementPosition(
- code->statement_position());
- if (code->position() != RelocInfo::kNoPosition) {
- masm_->positions_recorder()->RecordPosition(code->position());
- }
- // Generate the code.
- Comment cmnt(masm_, code->comment());
- masm_->bind(code->entry_label());
- if (code->AutoSaveAndRestore()) {
- code->SaveRegisters();
- }
- code->Generate();
- if (code->AutoSaveAndRestore()) {
- code->RestoreRegisters();
- code->Exit();
- }
- }
-}
-
-
-void DeferredCode::Exit() {
- masm_->jmp(exit_label());
-}
-
-
-void CodeGenerator::SetFrame(VirtualFrame* new_frame,
- RegisterFile* non_frame_registers) {
- RegisterFile saved_counts;
- if (has_valid_frame()) {
- frame_->DetachFromCodeGenerator();
- // The remaining register reference counts are the non-frame ones.
- allocator_->SaveTo(&saved_counts);
- }
-
- if (new_frame != NULL) {
- // Restore the non-frame register references that go with the new frame.
- allocator_->RestoreFrom(non_frame_registers);
- new_frame->AttachToCodeGenerator();
- }
-
- frame_ = new_frame;
- saved_counts.CopyTo(non_frame_registers);
-}
-
-
-void CodeGenerator::DeleteFrame() {
- if (has_valid_frame()) {
- frame_->DetachFromCodeGenerator();
- frame_ = NULL;
- }
-}
-
-
void CodeGenerator::MakeCodePrologue(CompilationInfo* info) {
#ifdef DEBUG
bool print_source = false;
@@ -230,61 +170,10 @@
#endif // ENABLE_DISASSEMBLER
}
-
-// Generate the code. Compile the AST and assemble all the pieces into a
-// Code object.
-bool CodeGenerator::MakeCode(CompilationInfo* info) {
- // When using Crankshaft the classic backend should never be used.
- ASSERT(!V8::UseCrankshaft());
- Handle<Script> script = info->script();
- if (!script->IsUndefined() && !script->source()->IsUndefined()) {
- int len = String::cast(script->source())->length();
- Counters* counters = info->isolate()->counters();
- counters->total_old_codegen_source_size()->Increment(len);
- }
- if (FLAG_trace_codegen) {
- PrintF("Classic Compiler - ");
- }
- MakeCodePrologue(info);
- // Generate code.
- const int kInitialBufferSize = 4 * KB;
- MacroAssembler masm(NULL, kInitialBufferSize);
-#ifdef ENABLE_GDB_JIT_INTERFACE
- masm.positions_recorder()->StartGDBJITLineInfoRecording();
-#endif
- CodeGenerator cgen(&masm);
- CodeGeneratorScope scope(Isolate::Current(), &cgen);
- cgen.Generate(info);
- if (cgen.HasStackOverflow()) {
- ASSERT(!Isolate::Current()->has_pending_exception());
- return false;
- }
-
- InLoopFlag in_loop = info->is_in_loop() ? IN_LOOP : NOT_IN_LOOP;
- Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, in_loop);
- Handle<Code> code = MakeCodeEpilogue(cgen.masm(), flags, info);
- // There is no stack check table in code generated by the classic backend.
- code->SetNoStackCheckTable();
- CodeGenerator::PrintCode(code, info);
- info->SetCode(code); // May be an empty handle.
-#ifdef ENABLE_GDB_JIT_INTERFACE
- if (FLAG_gdbjit && !code.is_null()) {
- GDBJITLineInfo* lineinfo =
- masm.positions_recorder()->DetachGDBJITLineInfo();
-
- GDBJIT(RegisterDetailedLineInfo(*code, lineinfo));
- }
-#endif
- return !code.is_null();
-}
-
-
#ifdef ENABLE_LOGGING_AND_PROFILING
-
static Vector<const char> kRegexp = CStrVector("regexp");
-
bool CodeGenerator::ShouldGenerateLog(Expression* type) {
ASSERT(type != NULL);
if (!LOGGER->is_logging() && !CpuProfiler::is_profiling()) return false;
@@ -299,120 +188,6 @@
#endif
-void CodeGenerator::ProcessDeclarations(ZoneList<Declaration*>* declarations) {
- int length = declarations->length();
- int globals = 0;
- for (int i = 0; i < length; i++) {
- Declaration* node = declarations->at(i);
- Variable* var = node->proxy()->var();
- Slot* slot = var->AsSlot();
-
- // If it was not possible to allocate the variable at compile
- // time, we need to "declare" it at runtime to make sure it
- // actually exists in the local context.
- if ((slot != NULL && slot->type() == Slot::LOOKUP) || !var->is_global()) {
- VisitDeclaration(node);
- } else {
- // Count global variables and functions for later processing
- globals++;
- }
- }
-
- // Return in case of no declared global functions or variables.
- if (globals == 0) return;
-
- // Compute array of global variable and function declarations.
- Handle<FixedArray> array = FACTORY->NewFixedArray(2 * globals, TENURED);
- for (int j = 0, i = 0; i < length; i++) {
- Declaration* node = declarations->at(i);
- Variable* var = node->proxy()->var();
- Slot* slot = var->AsSlot();
-
- if ((slot != NULL && slot->type() == Slot::LOOKUP) || !var->is_global()) {
- // Skip - already processed.
- } else {
- array->set(j++, *(var->name()));
- if (node->fun() == NULL) {
- if (var->mode() == Variable::CONST) {
- // In case this is const property use the hole.
- array->set_the_hole(j++);
- } else {
- array->set_undefined(j++);
- }
- } else {
- Handle<SharedFunctionInfo> function =
- Compiler::BuildFunctionInfo(node->fun(), script());
- // Check for stack-overflow exception.
- if (function.is_null()) {
- SetStackOverflow();
- return;
- }
- array->set(j++, *function);
- }
- }
- }
-
- // Invoke the platform-dependent code generator to do the actual
- // declaration the global variables and functions.
- DeclareGlobals(array);
-}
-
-
-void CodeGenerator::VisitIncrementOperation(IncrementOperation* expr) {
- UNREACHABLE();
-}
-
-
-// Lookup table for code generators for special runtime calls which are
-// generated inline.
-#define INLINE_FUNCTION_GENERATOR_ADDRESS(Name, argc, ressize) \
- &CodeGenerator::Generate##Name,
-
-const CodeGenerator::InlineFunctionGenerator
- CodeGenerator::kInlineFunctionGenerators[] = {
- INLINE_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
- INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
-};
-#undef INLINE_FUNCTION_GENERATOR_ADDRESS
-
-
-bool CodeGenerator::CheckForInlineRuntimeCall(CallRuntime* node) {
- ZoneList<Expression*>* args = node->arguments();
- Handle<String> name = node->name();
- const Runtime::Function* function = node->function();
- if (function != NULL && function->intrinsic_type == Runtime::INLINE) {
- int lookup_index = static_cast<int>(function->function_id) -
- static_cast<int>(Runtime::kFirstInlineFunction);
- ASSERT(lookup_index >= 0);
- ASSERT(static_cast<size_t>(lookup_index) <
- ARRAY_SIZE(kInlineFunctionGenerators));
- InlineFunctionGenerator generator = kInlineFunctionGenerators[lookup_index];
- (this->*generator)(args);
- return true;
- }
- return false;
-}
-
-
-// Simple condition analysis. ALWAYS_TRUE and ALWAYS_FALSE represent a
-// known result for the test expression, with no side effects.
-CodeGenerator::ConditionAnalysis CodeGenerator::AnalyzeCondition(
- Expression* cond) {
- if (cond == NULL) return ALWAYS_TRUE;
-
- Literal* lit = cond->AsLiteral();
- if (lit == NULL) return DONT_KNOW;
-
- if (lit->IsTrue()) {
- return ALWAYS_TRUE;
- } else if (lit->IsFalse()) {
- return ALWAYS_FALSE;
- }
-
- return DONT_KNOW;
-}
-
-
bool CodeGenerator::RecordPositions(MacroAssembler* masm,
int pos,
bool right_here) {
@@ -427,34 +202,6 @@
}
-void CodeGenerator::CodeForFunctionPosition(FunctionLiteral* fun) {
- if (FLAG_debug_info) RecordPositions(masm(), fun->start_position(), false);
-}
-
-
-void CodeGenerator::CodeForReturnPosition(FunctionLiteral* fun) {
- if (FLAG_debug_info) RecordPositions(masm(), fun->end_position() - 1, false);
-}
-
-
-void CodeGenerator::CodeForStatementPosition(Statement* stmt) {
- if (FLAG_debug_info) RecordPositions(masm(), stmt->statement_pos(), false);
-}
-
-
-void CodeGenerator::CodeForDoWhileConditionPosition(DoWhileStatement* stmt) {
- if (FLAG_debug_info)
- RecordPositions(masm(), stmt->condition_position(), false);
-}
-
-
-void CodeGenerator::CodeForSourcePosition(int pos) {
- if (FLAG_debug_info && pos != RelocInfo::kNoPosition) {
- masm()->positions_recorder()->RecordPosition(pos);
- }
-}
-
-
const char* GenericUnaryOpStub::GetName() {
switch (op_) {
case Token::SUB:
diff --git a/src/codegen.h b/src/codegen.h
index aa31999..e551abf 100644
--- a/src/codegen.h
+++ b/src/codegen.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -54,7 +54,6 @@
// shared code:
// CodeGenerator
// ~CodeGenerator
-// ProcessDeferred
// Generate
// ComputeLazyCompile
// BuildFunctionInfo
@@ -68,7 +67,6 @@
// CodeForDoWhileConditionPosition
// CodeForSourcePosition
-enum InitState { CONST_INIT, NOT_CONST_INIT };
enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
#if V8_TARGET_ARCH_IA32
@@ -83,163 +81,4 @@
#error Unsupported target architecture.
#endif
-#include "register-allocator.h"
-
-namespace v8 {
-namespace internal {
-
-// Code generation can be nested. Code generation scopes form a stack
-// of active code generators.
-class CodeGeneratorScope BASE_EMBEDDED {
- public:
- explicit CodeGeneratorScope(Isolate* isolate, CodeGenerator* cgen)
- : isolate_(isolate) {
- previous_ = isolate->current_code_generator();
- isolate->set_current_code_generator(cgen);
- }
-
- ~CodeGeneratorScope() {
- isolate_->set_current_code_generator(previous_);
- }
-
- static CodeGenerator* Current(Isolate* isolate) {
- ASSERT(isolate->current_code_generator() != NULL);
- return isolate->current_code_generator();
- }
-
- private:
- CodeGenerator* previous_;
- Isolate* isolate_;
-};
-
-#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64
-
-// State of used registers in a virtual frame.
-class FrameRegisterState {
- public:
- // Captures the current state of the given frame.
- explicit FrameRegisterState(VirtualFrame* frame);
-
- // Saves the state in the stack.
- void Save(MacroAssembler* masm) const;
-
- // Restores the state from the stack.
- void Restore(MacroAssembler* masm) const;
-
- private:
- // Constants indicating special actions. They should not be multiples
- // of kPointerSize so they will not collide with valid offsets from
- // the frame pointer.
- static const int kIgnore = -1;
- static const int kPush = 1;
-
- // This flag is ored with a valid offset from the frame pointer, so
- // it should fit in the low zero bits of a valid offset.
- static const int kSyncedFlag = 2;
-
- int registers_[RegisterAllocator::kNumRegisters];
-};
-
-#elif V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS
-
-
-class FrameRegisterState {
- public:
- inline FrameRegisterState(VirtualFrame frame) : frame_(frame) { }
-
- inline const VirtualFrame* frame() const { return &frame_; }
-
- private:
- VirtualFrame frame_;
-};
-
-#else
-
-#error Unsupported target architecture.
-
-#endif
-
-
-// RuntimeCallHelper implementation that saves/restores state of a
-// virtual frame.
-class VirtualFrameRuntimeCallHelper : public RuntimeCallHelper {
- public:
- // Does not take ownership of |frame_state|.
- explicit VirtualFrameRuntimeCallHelper(const FrameRegisterState* frame_state)
- : frame_state_(frame_state) {}
-
- virtual void BeforeCall(MacroAssembler* masm) const;
-
- virtual void AfterCall(MacroAssembler* masm) const;
-
- private:
- const FrameRegisterState* frame_state_;
-};
-
-
-// Deferred code objects are small pieces of code that are compiled
-// out of line. They are used to defer the compilation of uncommon
-// paths thereby avoiding expensive jumps around uncommon code parts.
-class DeferredCode: public ZoneObject {
- public:
- DeferredCode();
- virtual ~DeferredCode() { }
-
- virtual void Generate() = 0;
-
- MacroAssembler* masm() { return masm_; }
-
- int statement_position() const { return statement_position_; }
- int position() const { return position_; }
-
- Label* entry_label() { return &entry_label_; }
- Label* exit_label() { return &exit_label_; }
-
-#ifdef DEBUG
- void set_comment(const char* comment) { comment_ = comment; }
- const char* comment() const { return comment_; }
-#else
- void set_comment(const char* comment) { }
- const char* comment() const { return ""; }
-#endif
-
- inline void Jump();
- inline void Branch(Condition cc);
- void BindExit() { masm_->bind(&exit_label_); }
-
- const FrameRegisterState* frame_state() const { return &frame_state_; }
-
- void SaveRegisters();
- void RestoreRegisters();
- void Exit();
-
- // If this returns true then all registers will be saved for the duration
- // of the Generate() call. Otherwise the registers are not saved and the
- // Generate() call must bracket runtime any runtime calls with calls to
- // SaveRegisters() and RestoreRegisters(). In this case the Generate
- // method must also call Exit() in order to return to the non-deferred
- // code.
- virtual bool AutoSaveAndRestore() { return true; }
-
- protected:
- MacroAssembler* masm_;
-
- private:
- int statement_position_;
- int position_;
-
- Label entry_label_;
- Label exit_label_;
-
- FrameRegisterState frame_state_;
-
-#ifdef DEBUG
- const char* comment_;
-#endif
- DISALLOW_COPY_AND_ASSIGN(DeferredCode);
-};
-
-
-} } // namespace v8::internal
-
#endif // V8_CODEGEN_H_
diff --git a/src/compiler.cc b/src/compiler.cc
index 1ec4414..86d5de3 100755
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -30,7 +30,7 @@
#include "compiler.h"
#include "bootstrapper.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "compilation-cache.h"
#include "data-flow.h"
#include "debug.h"
@@ -326,30 +326,9 @@
if (Rewriter::Rewrite(info) && Scope::Analyze(info)) {
if (V8::UseCrankshaft()) return MakeCrankshaftCode(info);
-
- // Generate code and return it. Code generator selection is governed by
- // which backends are enabled and whether the function is considered
- // run-once code or not.
- //
- // --full-compiler enables the dedicated backend for code we expect to
- // be run once
- //
- // The normal choice of backend can be overridden with the flags
- // --always-full-compiler.
- if (Rewriter::Analyze(info)) {
- Handle<SharedFunctionInfo> shared = info->shared_info();
- bool is_run_once = (shared.is_null())
- ? info->scope()->is_global_scope()
- : (shared->is_toplevel() || shared->try_full_codegen());
- bool can_use_full =
- FLAG_full_compiler && !info->function()->contains_loops();
- if (AlwaysFullCompiler() || (is_run_once && can_use_full)) {
- return FullCodeGenerator::MakeCode(info);
- } else {
- return AssignedVariablesAnalyzer::Analyze(info) &&
- CodeGenerator::MakeCode(info);
- }
- }
+ // If crankshaft is not supported fall back to full code generator
+ // for all compilation.
+ return FullCodeGenerator::MakeCode(info);
}
return false;
@@ -388,11 +367,11 @@
// For eval scripts add information on the function from which eval was
// called.
if (info->is_eval()) {
- StackTraceFrameIterator it;
+ StackTraceFrameIterator it(isolate);
if (!it.done()) {
script->set_eval_from_shared(
JSFunction::cast(it.frame()->function())->shared());
- Code* code = it.frame()->LookupCode(isolate);
+ Code* code = it.frame()->LookupCode();
int offset = static_cast<int>(
it.frame()->pc() - code->instruction_start());
script->set_eval_from_instructions_offset(Smi::FromInt(offset));
@@ -588,7 +567,7 @@
CompilationInfo info(script);
info.MarkAsEval();
if (is_global) info.MarkAsGlobal();
- if (strict_mode == kStrictMode) info.MarkAsStrict();
+ if (strict_mode == kStrictMode) info.MarkAsStrictMode();
info.SetCallingContext(context);
result = MakeFunctionInfo(&info);
if (!result.is_null()) {
@@ -625,6 +604,12 @@
// parsing statistics.
HistogramTimerScope timer(isolate->counters()->compile_lazy());
+ // After parsing we know function's strict mode. Remember it.
+ if (info->function()->strict_mode()) {
+ shared->set_strict_mode(true);
+ info->MarkAsStrictMode();
+ }
+
// Compile the code.
if (!MakeCode(info)) {
if (!isolate->has_pending_exception()) {
@@ -721,35 +706,12 @@
if (FLAG_lazy && allow_lazy) {
Handle<Code> code = info.isolate()->builtins()->LazyCompile();
info.SetCode(code);
- } else {
- if (V8::UseCrankshaft()) {
- if (!MakeCrankshaftCode(&info)) {
- return Handle<SharedFunctionInfo>::null();
- }
- } else {
- // The bodies of function literals have not yet been visited by the
- // AST optimizer/analyzer.
- if (!Rewriter::Analyze(&info)) return Handle<SharedFunctionInfo>::null();
-
- bool is_run_once = literal->try_full_codegen();
- bool can_use_full = FLAG_full_compiler && !literal->contains_loops();
-
- if (AlwaysFullCompiler() || (is_run_once && can_use_full)) {
- if (!FullCodeGenerator::MakeCode(&info)) {
- return Handle<SharedFunctionInfo>::null();
- }
- } else {
- // We fall back to the classic V8 code generator.
- if (!AssignedVariablesAnalyzer::Analyze(&info) ||
- !CodeGenerator::MakeCode(&info)) {
- return Handle<SharedFunctionInfo>::null();
- }
- }
- }
+ } else if ((V8::UseCrankshaft() && MakeCrankshaftCode(&info)) ||
+ (!V8::UseCrankshaft() && FullCodeGenerator::MakeCode(&info))) {
ASSERT(!info.code().is_null());
-
- // Function compilation complete.
scope_info = SerializedScopeInfo::Create(info.scope());
+ } else {
+ return Handle<SharedFunctionInfo>::null();
}
// Create a shared function info object.
@@ -791,7 +753,6 @@
function_info->SetThisPropertyAssignmentsInfo(
lit->has_only_simple_this_property_assignments(),
*lit->this_property_assignments());
- function_info->set_try_full_codegen(lit->try_full_codegen());
function_info->set_allows_lazy_compilation(lit->AllowsLazyCompilation());
function_info->set_strict_mode(lit->strict_mode());
}
@@ -829,7 +790,7 @@
}
}
- GDBJIT(AddCode(name,
+ GDBJIT(AddCode(Handle<String>(shared->DebugName()),
Handle<Script>(info->script()),
Handle<Code>(info->code())));
}
diff --git a/src/compiler.h b/src/compiler.h
index a66c540..e75e869 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -30,7 +30,6 @@
#include "ast.h"
#include "frame-element.h"
-#include "register-allocator.h"
#include "zone.h"
namespace v8 {
@@ -53,7 +52,7 @@
bool is_lazy() const { return (flags_ & IsLazy::mask()) != 0; }
bool is_eval() const { return (flags_ & IsEval::mask()) != 0; }
bool is_global() const { return (flags_ & IsGlobal::mask()) != 0; }
- bool is_strict() const { return (flags_ & IsStrict::mask()) != 0; }
+ bool is_strict_mode() const { return (flags_ & IsStrictMode::mask()) != 0; }
bool is_in_loop() const { return (flags_ & IsInLoop::mask()) != 0; }
FunctionLiteral* function() const { return function_; }
Scope* scope() const { return scope_; }
@@ -74,11 +73,11 @@
ASSERT(!is_lazy());
flags_ |= IsGlobal::encode(true);
}
- void MarkAsStrict() {
- flags_ |= IsStrict::encode(true);
+ void MarkAsStrictMode() {
+ flags_ |= IsStrictMode::encode(true);
}
StrictModeFlag StrictMode() {
- return is_strict() ? kStrictMode : kNonStrictMode;
+ return is_strict_mode() ? kStrictMode : kNonStrictMode;
}
void MarkAsInLoop() {
ASSERT(is_lazy());
@@ -165,7 +164,7 @@
void Initialize(Mode mode) {
mode_ = V8::UseCrankshaft() ? mode : NONOPT;
if (!shared_info_.is_null() && shared_info_->strict_mode()) {
- MarkAsStrict();
+ MarkAsStrictMode();
}
}
@@ -185,7 +184,7 @@
// Flags that can be set for lazy compilation.
class IsInLoop: public BitField<bool, 3, 1> {};
// Strict mode - used in eager compilation.
- class IsStrict: public BitField<bool, 4, 1> {};
+ class IsStrictMode: public BitField<bool, 4, 1> {};
// Native syntax (%-stuff) allowed?
class IsNativesSyntaxAllowed: public BitField<bool, 5, 1> {};
@@ -239,6 +238,8 @@
// give up.
static const int kDefaultMaxOptCount = 10;
+ static const int kMaxInliningLevels = 3;
+
// All routines return a SharedFunctionInfo.
// If an error occurs an exception is raised and the return handle
// contains NULL.
diff --git a/src/conversions-inl.h b/src/conversions-inl.h
index bf02947..cb7dbf8 100644
--- a/src/conversions-inl.h
+++ b/src/conversions-inl.h
@@ -60,11 +60,7 @@
if (x < k2Pow52) {
x += k2Pow52;
uint32_t result;
-#ifdef BIG_ENDIAN_FLOATING_POINT
- Address mantissa_ptr = reinterpret_cast<Address>(&x) + kIntSize;
-#else
Address mantissa_ptr = reinterpret_cast<Address>(&x);
-#endif
// Copy least significant 32 bits of mantissa.
memcpy(&result, mantissa_ptr, sizeof(result));
return negative ? ~result + 1 : result;
diff --git a/src/conversions.cc b/src/conversions.cc
index c3d7bdf..1458584 100644
--- a/src/conversions.cc
+++ b/src/conversions.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -109,11 +109,11 @@
// Returns true if a nonspace found and false if the end has reached.
template <class Iterator, class EndMark>
-static inline bool AdvanceToNonspace(ScannerConstants* scanner_constants,
+static inline bool AdvanceToNonspace(UnicodeCache* unicode_cache,
Iterator* current,
EndMark end) {
while (*current != end) {
- if (!scanner_constants->IsWhiteSpace(**current)) return true;
+ if (!unicode_cache->IsWhiteSpace(**current)) return true;
++*current;
}
return false;
@@ -134,7 +134,7 @@
// Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end.
template <int radix_log_2, class Iterator, class EndMark>
-static double InternalStringToIntDouble(ScannerConstants* scanner_constants,
+static double InternalStringToIntDouble(UnicodeCache* unicode_cache,
Iterator current,
EndMark end,
bool negative,
@@ -161,7 +161,7 @@
digit = static_cast<char>(*current) - 'A' + 10;
} else {
if (allow_trailing_junk ||
- !AdvanceToNonspace(scanner_constants, ¤t, end)) {
+ !AdvanceToNonspace(unicode_cache, ¤t, end)) {
break;
} else {
return JUNK_STRING_VALUE;
@@ -193,7 +193,7 @@
}
if (!allow_trailing_junk &&
- AdvanceToNonspace(scanner_constants, ¤t, end)) {
+ AdvanceToNonspace(unicode_cache, ¤t, end)) {
return JUNK_STRING_VALUE;
}
@@ -237,14 +237,14 @@
template <class Iterator, class EndMark>
-static double InternalStringToInt(ScannerConstants* scanner_constants,
+static double InternalStringToInt(UnicodeCache* unicode_cache,
Iterator current,
EndMark end,
int radix) {
const bool allow_trailing_junk = true;
const double empty_string_val = JUNK_STRING_VALUE;
- if (!AdvanceToNonspace(scanner_constants, ¤t, end)) {
+ if (!AdvanceToNonspace(unicode_cache, ¤t, end)) {
return empty_string_val;
}
@@ -254,12 +254,12 @@
if (*current == '+') {
// Ignore leading sign; skip following spaces.
++current;
- if (!AdvanceToNonspace(scanner_constants, ¤t, end)) {
+ if (!AdvanceToNonspace(unicode_cache, ¤t, end)) {
return JUNK_STRING_VALUE;
}
} else if (*current == '-') {
++current;
- if (!AdvanceToNonspace(scanner_constants, ¤t, end)) {
+ if (!AdvanceToNonspace(unicode_cache, ¤t, end)) {
return JUNK_STRING_VALUE;
}
negative = true;
@@ -312,21 +312,21 @@
switch (radix) {
case 2:
return InternalStringToIntDouble<1>(
- scanner_constants, current, end, negative, allow_trailing_junk);
+ unicode_cache, current, end, negative, allow_trailing_junk);
case 4:
return InternalStringToIntDouble<2>(
- scanner_constants, current, end, negative, allow_trailing_junk);
+ unicode_cache, current, end, negative, allow_trailing_junk);
case 8:
return InternalStringToIntDouble<3>(
- scanner_constants, current, end, negative, allow_trailing_junk);
+ unicode_cache, current, end, negative, allow_trailing_junk);
case 16:
return InternalStringToIntDouble<4>(
- scanner_constants, current, end, negative, allow_trailing_junk);
+ unicode_cache, current, end, negative, allow_trailing_junk);
case 32:
return InternalStringToIntDouble<5>(
- scanner_constants, current, end, negative, allow_trailing_junk);
+ unicode_cache, current, end, negative, allow_trailing_junk);
default:
UNREACHABLE();
}
@@ -352,7 +352,7 @@
}
if (!allow_trailing_junk &&
- AdvanceToNonspace(scanner_constants, ¤t, end)) {
+ AdvanceToNonspace(unicode_cache, ¤t, end)) {
return JUNK_STRING_VALUE;
}
@@ -418,7 +418,7 @@
} while (!done);
if (!allow_trailing_junk &&
- AdvanceToNonspace(scanner_constants, ¤t, end)) {
+ AdvanceToNonspace(unicode_cache, ¤t, end)) {
return JUNK_STRING_VALUE;
}
@@ -432,7 +432,7 @@
// 2. *current - gets the current character in the sequence.
// 3. ++current (advances the position).
template <class Iterator, class EndMark>
-static double InternalStringToDouble(ScannerConstants* scanner_constants,
+static double InternalStringToDouble(UnicodeCache* unicode_cache,
Iterator current,
EndMark end,
int flags,
@@ -445,7 +445,7 @@
// 'parsing_done'.
// 4. 'current' is not dereferenced after the 'parsing_done' label.
// 5. Code before 'parsing_done' may rely on 'current != end'.
- if (!AdvanceToNonspace(scanner_constants, ¤t, end)) {
+ if (!AdvanceToNonspace(unicode_cache, ¤t, end)) {
return empty_string_val;
}
@@ -483,7 +483,7 @@
}
if (!allow_trailing_junk &&
- AdvanceToNonspace(scanner_constants, ¤t, end)) {
+ AdvanceToNonspace(unicode_cache, ¤t, end)) {
return JUNK_STRING_VALUE;
}
@@ -505,7 +505,7 @@
return JUNK_STRING_VALUE; // "0x".
}
- return InternalStringToIntDouble<4>(scanner_constants,
+ return InternalStringToIntDouble<4>(unicode_cache,
current,
end,
negative,
@@ -643,7 +643,7 @@
}
if (!allow_trailing_junk &&
- AdvanceToNonspace(scanner_constants, ¤t, end)) {
+ AdvanceToNonspace(unicode_cache, ¤t, end)) {
return JUNK_STRING_VALUE;
}
@@ -651,7 +651,7 @@
exponent += insignificant_digits;
if (octal) {
- return InternalStringToIntDouble<3>(scanner_constants,
+ return InternalStringToIntDouble<3>(unicode_cache,
buffer,
buffer + buffer_pos,
negative,
@@ -671,23 +671,22 @@
}
-double StringToDouble(String* str, int flags, double empty_string_val) {
- ScannerConstants* scanner_constants =
- Isolate::Current()->scanner_constants();
+double StringToDouble(UnicodeCache* unicode_cache,
+ String* str, int flags, double empty_string_val) {
StringShape shape(str);
if (shape.IsSequentialAscii()) {
const char* begin = SeqAsciiString::cast(str)->GetChars();
const char* end = begin + str->length();
- return InternalStringToDouble(scanner_constants, begin, end, flags,
+ return InternalStringToDouble(unicode_cache, begin, end, flags,
empty_string_val);
} else if (shape.IsSequentialTwoByte()) {
const uc16* begin = SeqTwoByteString::cast(str)->GetChars();
const uc16* end = begin + str->length();
- return InternalStringToDouble(scanner_constants, begin, end, flags,
+ return InternalStringToDouble(unicode_cache, begin, end, flags,
empty_string_val);
} else {
StringInputBuffer buffer(str);
- return InternalStringToDouble(scanner_constants,
+ return InternalStringToDouble(unicode_cache,
StringInputBufferIterator(&buffer),
StringInputBufferIterator::EndMarker(),
flags,
@@ -696,21 +695,21 @@
}
-double StringToInt(String* str, int radix) {
- ScannerConstants* scanner_constants =
- Isolate::Current()->scanner_constants();
+double StringToInt(UnicodeCache* unicode_cache,
+ String* str,
+ int radix) {
StringShape shape(str);
if (shape.IsSequentialAscii()) {
const char* begin = SeqAsciiString::cast(str)->GetChars();
const char* end = begin + str->length();
- return InternalStringToInt(scanner_constants, begin, end, radix);
+ return InternalStringToInt(unicode_cache, begin, end, radix);
} else if (shape.IsSequentialTwoByte()) {
const uc16* begin = SeqTwoByteString::cast(str)->GetChars();
const uc16* end = begin + str->length();
- return InternalStringToInt(scanner_constants, begin, end, radix);
+ return InternalStringToInt(unicode_cache, begin, end, radix);
} else {
StringInputBuffer buffer(str);
- return InternalStringToInt(scanner_constants,
+ return InternalStringToInt(unicode_cache,
StringInputBufferIterator(&buffer),
StringInputBufferIterator::EndMarker(),
radix);
@@ -718,22 +717,20 @@
}
-double StringToDouble(const char* str, int flags, double empty_string_val) {
- ScannerConstants* scanner_constants =
- Isolate::Current()->scanner_constants();
+double StringToDouble(UnicodeCache* unicode_cache,
+ const char* str, int flags, double empty_string_val) {
const char* end = str + StrLength(str);
- return InternalStringToDouble(scanner_constants, str, end, flags,
+ return InternalStringToDouble(unicode_cache, str, end, flags,
empty_string_val);
}
-double StringToDouble(Vector<const char> str,
+double StringToDouble(UnicodeCache* unicode_cache,
+ Vector<const char> str,
int flags,
double empty_string_val) {
- ScannerConstants* scanner_constants =
- Isolate::Current()->scanner_constants();
const char* end = str.start() + str.length();
- return InternalStringToDouble(scanner_constants, str.start(), end, flags,
+ return InternalStringToDouble(unicode_cache, str.start(), end, flags,
empty_string_val);
}
diff --git a/src/conversions.h b/src/conversions.h
index 312e6ae..a14dc9a 100644
--- a/src/conversions.h
+++ b/src/conversions.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -28,6 +28,8 @@
#ifndef V8_CONVERSIONS_H_
#define V8_CONVERSIONS_H_
+#include "scanner-base.h"
+
namespace v8 {
namespace internal {
@@ -91,15 +93,22 @@
// Converts a string into a double value according to ECMA-262 9.3.1
-double StringToDouble(String* str, int flags, double empty_string_val = 0);
-double StringToDouble(Vector<const char> str,
+double StringToDouble(UnicodeCache* unicode_cache,
+ String* str,
+ int flags,
+ double empty_string_val = 0);
+double StringToDouble(UnicodeCache* unicode_cache,
+ Vector<const char> str,
int flags,
double empty_string_val = 0);
// This version expects a zero-terminated character array.
-double StringToDouble(const char* str, int flags, double empty_string_val = 0);
+double StringToDouble(UnicodeCache* unicode_cache,
+ const char* str,
+ int flags,
+ double empty_string_val = 0);
// Converts a string into an integer.
-double StringToInt(String* str, int radix);
+double StringToInt(UnicodeCache* unicode_cache, String* str, int radix);
// Converts a double to a string value according to ECMA-262 9.8.1.
// The buffer should be large enough for any floating point number.
diff --git a/src/cpu-profiler-inl.h b/src/cpu-profiler-inl.h
index a7fffe0..b704417 100644
--- a/src/cpu-profiler-inl.h
+++ b/src/cpu-profiler-inl.h
@@ -70,6 +70,7 @@
// Init the required fields only.
result->sample.pc = NULL;
result->sample.frames_count = 0;
+ result->sample.has_external_callback = false;
return result;
}
diff --git a/src/cpu-profiler.cc b/src/cpu-profiler.cc
index ef51950..3894748 100644
--- a/src/cpu-profiler.cc
+++ b/src/cpu-profiler.cc
@@ -184,11 +184,13 @@
void ProfilerEventsProcessor::AddCurrentStack() {
TickSampleEventRecord record;
TickSample* sample = &record.sample;
- sample->state = Isolate::Current()->current_vm_state();
+ Isolate* isolate = Isolate::Current();
+ sample->state = isolate->current_vm_state();
sample->pc = reinterpret_cast<Address>(sample); // Not NULL.
sample->tos = NULL;
+ sample->has_external_callback = false;
sample->frames_count = 0;
- for (StackTraceFrameIterator it;
+ for (StackTraceFrameIterator it(isolate);
!it.done() && sample->frames_count < TickSample::kMaxFramesCount;
it.Advance()) {
sample->stack[sample->frames_count++] = it.frame()->pc();
diff --git a/src/cpu.h b/src/cpu.h
index ddc402f..e307302 100644
--- a/src/cpu.h
+++ b/src/cpu.h
@@ -53,6 +53,8 @@
// Initializes the cpu architecture support. Called once at VM startup.
static void Setup();
+ static bool SupportsCrankshaft();
+
// Flush instruction cache.
static void FlushICache(void* start, size_t size);
diff --git a/src/d8.gyp b/src/d8.gyp
index 901fd65..29212dd 100644
--- a/src/d8.gyp
+++ b/src/d8.gyp
@@ -61,6 +61,7 @@
'variables': {
'js_files': [
'd8.js',
+ 'macros.py',
],
},
'actions': [
@@ -72,7 +73,6 @@
],
'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/d8-js.cc',
- '<(SHARED_INTERMEDIATE_DIR)/d8-js-empty.cc',
],
'action': [
'python',
diff --git a/src/data-flow.cc b/src/data-flow.cc
index 9c02ff4..6a3b05c 100644
--- a/src/data-flow.cc
+++ b/src/data-flow.cc
@@ -63,483 +63,4 @@
current_value_ = val >> 1;
}
-
-bool AssignedVariablesAnalyzer::Analyze(CompilationInfo* info) {
- Scope* scope = info->scope();
- int size = scope->num_parameters() + scope->num_stack_slots();
- if (size == 0) return true;
- AssignedVariablesAnalyzer analyzer(info, size);
- return analyzer.Analyze();
-}
-
-
-AssignedVariablesAnalyzer::AssignedVariablesAnalyzer(CompilationInfo* info,
- int size)
- : info_(info), av_(size) {
-}
-
-
-bool AssignedVariablesAnalyzer::Analyze() {
- ASSERT(av_.length() > 0);
- VisitStatements(info_->function()->body());
- return !HasStackOverflow();
-}
-
-
-Variable* AssignedVariablesAnalyzer::FindSmiLoopVariable(ForStatement* stmt) {
- // The loop must have all necessary parts.
- if (stmt->init() == NULL || stmt->cond() == NULL || stmt->next() == NULL) {
- return NULL;
- }
- // The initialization statement has to be a simple assignment.
- Assignment* init = stmt->init()->StatementAsSimpleAssignment();
- if (init == NULL) return NULL;
-
- // We only deal with local variables.
- Variable* loop_var = init->target()->AsVariableProxy()->AsVariable();
- if (loop_var == NULL || !loop_var->IsStackAllocated()) return NULL;
-
- // Don't try to get clever with const or dynamic variables.
- if (loop_var->mode() != Variable::VAR) return NULL;
-
- // The initial value has to be a smi.
- Literal* init_lit = init->value()->AsLiteral();
- if (init_lit == NULL || !init_lit->handle()->IsSmi()) return NULL;
- int init_value = Smi::cast(*init_lit->handle())->value();
-
- // The condition must be a compare of variable with <, <=, >, or >=.
- CompareOperation* cond = stmt->cond()->AsCompareOperation();
- if (cond == NULL) return NULL;
- if (cond->op() != Token::LT
- && cond->op() != Token::LTE
- && cond->op() != Token::GT
- && cond->op() != Token::GTE) return NULL;
-
- // The lhs must be the same variable as in the init expression.
- if (cond->left()->AsVariableProxy()->AsVariable() != loop_var) return NULL;
-
- // The rhs must be a smi.
- Literal* term_lit = cond->right()->AsLiteral();
- if (term_lit == NULL || !term_lit->handle()->IsSmi()) return NULL;
- int term_value = Smi::cast(*term_lit->handle())->value();
-
- // The count operation updates the same variable as in the init expression.
- CountOperation* update = stmt->next()->StatementAsCountOperation();
- if (update == NULL) return NULL;
- if (update->expression()->AsVariableProxy()->AsVariable() != loop_var) {
- return NULL;
- }
-
- // The direction of the count operation must agree with the start and the end
- // value. We currently do not allow the initial value to be the same as the
- // terminal value. This _would_ be ok as long as the loop body never executes
- // or executes exactly one time.
- if (init_value == term_value) return NULL;
- if (init_value < term_value && update->op() != Token::INC) return NULL;
- if (init_value > term_value && update->op() != Token::DEC) return NULL;
-
- // Check that the update operation cannot overflow the smi range. This can
- // occur in the two cases where the loop bound is equal to the largest or
- // smallest smi.
- if (update->op() == Token::INC && term_value == Smi::kMaxValue) return NULL;
- if (update->op() == Token::DEC && term_value == Smi::kMinValue) return NULL;
-
- // Found a smi loop variable.
- return loop_var;
-}
-
-int AssignedVariablesAnalyzer::BitIndex(Variable* var) {
- ASSERT(var != NULL);
- ASSERT(var->IsStackAllocated());
- Slot* slot = var->AsSlot();
- if (slot->type() == Slot::PARAMETER) {
- return slot->index();
- } else {
- return info_->scope()->num_parameters() + slot->index();
- }
-}
-
-
-void AssignedVariablesAnalyzer::RecordAssignedVar(Variable* var) {
- ASSERT(var != NULL);
- if (var->IsStackAllocated()) {
- av_.Add(BitIndex(var));
- }
-}
-
-
-void AssignedVariablesAnalyzer::MarkIfTrivial(Expression* expr) {
- Variable* var = expr->AsVariableProxy()->AsVariable();
- if (var != NULL &&
- var->IsStackAllocated() &&
- !var->is_arguments() &&
- var->mode() != Variable::CONST &&
- (var->is_this() || !av_.Contains(BitIndex(var)))) {
- expr->AsVariableProxy()->MarkAsTrivial();
- }
-}
-
-
-void AssignedVariablesAnalyzer::ProcessExpression(Expression* expr) {
- BitVector saved_av(av_);
- av_.Clear();
- Visit(expr);
- av_.Union(saved_av);
-}
-
-void AssignedVariablesAnalyzer::VisitBlock(Block* stmt) {
- VisitStatements(stmt->statements());
-}
-
-
-void AssignedVariablesAnalyzer::VisitExpressionStatement(
- ExpressionStatement* stmt) {
- ProcessExpression(stmt->expression());
-}
-
-
-void AssignedVariablesAnalyzer::VisitEmptyStatement(EmptyStatement* stmt) {
- // Do nothing.
-}
-
-
-void AssignedVariablesAnalyzer::VisitIfStatement(IfStatement* stmt) {
- ProcessExpression(stmt->condition());
- Visit(stmt->then_statement());
- Visit(stmt->else_statement());
-}
-
-
-void AssignedVariablesAnalyzer::VisitContinueStatement(
- ContinueStatement* stmt) {
- // Nothing to do.
-}
-
-
-void AssignedVariablesAnalyzer::VisitBreakStatement(BreakStatement* stmt) {
- // Nothing to do.
-}
-
-
-void AssignedVariablesAnalyzer::VisitReturnStatement(ReturnStatement* stmt) {
- ProcessExpression(stmt->expression());
-}
-
-
-void AssignedVariablesAnalyzer::VisitWithEnterStatement(
- WithEnterStatement* stmt) {
- ProcessExpression(stmt->expression());
-}
-
-
-void AssignedVariablesAnalyzer::VisitWithExitStatement(
- WithExitStatement* stmt) {
- // Nothing to do.
-}
-
-
-void AssignedVariablesAnalyzer::VisitSwitchStatement(SwitchStatement* stmt) {
- BitVector result(av_);
- av_.Clear();
- Visit(stmt->tag());
- result.Union(av_);
- for (int i = 0; i < stmt->cases()->length(); i++) {
- CaseClause* clause = stmt->cases()->at(i);
- if (!clause->is_default()) {
- av_.Clear();
- Visit(clause->label());
- result.Union(av_);
- }
- VisitStatements(clause->statements());
- }
- av_.Union(result);
-}
-
-
-void AssignedVariablesAnalyzer::VisitDoWhileStatement(DoWhileStatement* stmt) {
- ProcessExpression(stmt->cond());
- Visit(stmt->body());
-}
-
-
-void AssignedVariablesAnalyzer::VisitWhileStatement(WhileStatement* stmt) {
- ProcessExpression(stmt->cond());
- Visit(stmt->body());
-}
-
-
-void AssignedVariablesAnalyzer::VisitForStatement(ForStatement* stmt) {
- if (stmt->init() != NULL) Visit(stmt->init());
- if (stmt->cond() != NULL) ProcessExpression(stmt->cond());
- if (stmt->next() != NULL) Visit(stmt->next());
-
- // Process loop body. After visiting the loop body av_ contains
- // the assigned variables of the loop body.
- BitVector saved_av(av_);
- av_.Clear();
- Visit(stmt->body());
-
- Variable* var = FindSmiLoopVariable(stmt);
- if (var != NULL && !av_.Contains(BitIndex(var))) {
- stmt->set_loop_variable(var);
- }
- av_.Union(saved_av);
-}
-
-
-void AssignedVariablesAnalyzer::VisitForInStatement(ForInStatement* stmt) {
- ProcessExpression(stmt->each());
- ProcessExpression(stmt->enumerable());
- Visit(stmt->body());
-}
-
-
-void AssignedVariablesAnalyzer::VisitTryCatchStatement(
- TryCatchStatement* stmt) {
- Visit(stmt->try_block());
- Visit(stmt->catch_block());
-}
-
-
-void AssignedVariablesAnalyzer::VisitTryFinallyStatement(
- TryFinallyStatement* stmt) {
- Visit(stmt->try_block());
- Visit(stmt->finally_block());
-}
-
-
-void AssignedVariablesAnalyzer::VisitDebuggerStatement(
- DebuggerStatement* stmt) {
- // Nothing to do.
-}
-
-
-void AssignedVariablesAnalyzer::VisitFunctionLiteral(FunctionLiteral* expr) {
- // Nothing to do.
- ASSERT(av_.IsEmpty());
-}
-
-
-void AssignedVariablesAnalyzer::VisitSharedFunctionInfoLiteral(
- SharedFunctionInfoLiteral* expr) {
- // Nothing to do.
- ASSERT(av_.IsEmpty());
-}
-
-
-void AssignedVariablesAnalyzer::VisitConditional(Conditional* expr) {
- ASSERT(av_.IsEmpty());
-
- Visit(expr->condition());
-
- BitVector result(av_);
- av_.Clear();
- Visit(expr->then_expression());
- result.Union(av_);
-
- av_.Clear();
- Visit(expr->else_expression());
- av_.Union(result);
-}
-
-
-void AssignedVariablesAnalyzer::VisitVariableProxy(VariableProxy* expr) {
- // Nothing to do.
- ASSERT(av_.IsEmpty());
-}
-
-
-void AssignedVariablesAnalyzer::VisitLiteral(Literal* expr) {
- // Nothing to do.
- ASSERT(av_.IsEmpty());
-}
-
-
-void AssignedVariablesAnalyzer::VisitRegExpLiteral(RegExpLiteral* expr) {
- // Nothing to do.
- ASSERT(av_.IsEmpty());
-}
-
-
-void AssignedVariablesAnalyzer::VisitObjectLiteral(ObjectLiteral* expr) {
- ASSERT(av_.IsEmpty());
- BitVector result(av_.length());
- for (int i = 0; i < expr->properties()->length(); i++) {
- Visit(expr->properties()->at(i)->value());
- result.Union(av_);
- av_.Clear();
- }
- av_ = result;
-}
-
-
-void AssignedVariablesAnalyzer::VisitArrayLiteral(ArrayLiteral* expr) {
- ASSERT(av_.IsEmpty());
- BitVector result(av_.length());
- for (int i = 0; i < expr->values()->length(); i++) {
- Visit(expr->values()->at(i));
- result.Union(av_);
- av_.Clear();
- }
- av_ = result;
-}
-
-
-void AssignedVariablesAnalyzer::VisitCatchExtensionObject(
- CatchExtensionObject* expr) {
- ASSERT(av_.IsEmpty());
- Visit(expr->key());
- ProcessExpression(expr->value());
-}
-
-
-void AssignedVariablesAnalyzer::VisitAssignment(Assignment* expr) {
- ASSERT(av_.IsEmpty());
-
- // There are three kinds of assignments: variable assignments, property
- // assignments, and reference errors (invalid left-hand sides).
- Variable* var = expr->target()->AsVariableProxy()->AsVariable();
- Property* prop = expr->target()->AsProperty();
- ASSERT(var == NULL || prop == NULL);
-
- if (var != NULL) {
- MarkIfTrivial(expr->value());
- Visit(expr->value());
- if (expr->is_compound()) {
- // Left-hand side occurs also as an rvalue.
- MarkIfTrivial(expr->target());
- ProcessExpression(expr->target());
- }
- RecordAssignedVar(var);
-
- } else if (prop != NULL) {
- MarkIfTrivial(expr->value());
- Visit(expr->value());
- if (!prop->key()->IsPropertyName()) {
- MarkIfTrivial(prop->key());
- ProcessExpression(prop->key());
- }
- MarkIfTrivial(prop->obj());
- ProcessExpression(prop->obj());
-
- } else {
- Visit(expr->target());
- }
-}
-
-
-void AssignedVariablesAnalyzer::VisitThrow(Throw* expr) {
- ASSERT(av_.IsEmpty());
- Visit(expr->exception());
-}
-
-
-void AssignedVariablesAnalyzer::VisitProperty(Property* expr) {
- ASSERT(av_.IsEmpty());
- if (!expr->key()->IsPropertyName()) {
- MarkIfTrivial(expr->key());
- Visit(expr->key());
- }
- MarkIfTrivial(expr->obj());
- ProcessExpression(expr->obj());
-}
-
-
-void AssignedVariablesAnalyzer::VisitCall(Call* expr) {
- ASSERT(av_.IsEmpty());
- Visit(expr->expression());
- BitVector result(av_);
- for (int i = 0; i < expr->arguments()->length(); i++) {
- av_.Clear();
- Visit(expr->arguments()->at(i));
- result.Union(av_);
- }
- av_ = result;
-}
-
-
-void AssignedVariablesAnalyzer::VisitCallNew(CallNew* expr) {
- ASSERT(av_.IsEmpty());
- Visit(expr->expression());
- BitVector result(av_);
- for (int i = 0; i < expr->arguments()->length(); i++) {
- av_.Clear();
- Visit(expr->arguments()->at(i));
- result.Union(av_);
- }
- av_ = result;
-}
-
-
-void AssignedVariablesAnalyzer::VisitCallRuntime(CallRuntime* expr) {
- ASSERT(av_.IsEmpty());
- BitVector result(av_);
- for (int i = 0; i < expr->arguments()->length(); i++) {
- av_.Clear();
- Visit(expr->arguments()->at(i));
- result.Union(av_);
- }
- av_ = result;
-}
-
-
-void AssignedVariablesAnalyzer::VisitUnaryOperation(UnaryOperation* expr) {
- ASSERT(av_.IsEmpty());
- MarkIfTrivial(expr->expression());
- Visit(expr->expression());
-}
-
-
-void AssignedVariablesAnalyzer::VisitIncrementOperation(
- IncrementOperation* expr) {
- UNREACHABLE();
-}
-
-
-void AssignedVariablesAnalyzer::VisitCountOperation(CountOperation* expr) {
- ASSERT(av_.IsEmpty());
- if (expr->is_prefix()) MarkIfTrivial(expr->expression());
- Visit(expr->expression());
-
- Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
- if (var != NULL) RecordAssignedVar(var);
-}
-
-
-void AssignedVariablesAnalyzer::VisitBinaryOperation(BinaryOperation* expr) {
- ASSERT(av_.IsEmpty());
- MarkIfTrivial(expr->right());
- Visit(expr->right());
- MarkIfTrivial(expr->left());
- ProcessExpression(expr->left());
-}
-
-
-void AssignedVariablesAnalyzer::VisitCompareOperation(CompareOperation* expr) {
- ASSERT(av_.IsEmpty());
- MarkIfTrivial(expr->right());
- Visit(expr->right());
- MarkIfTrivial(expr->left());
- ProcessExpression(expr->left());
-}
-
-
-void AssignedVariablesAnalyzer::VisitCompareToNull(CompareToNull* expr) {
- ASSERT(av_.IsEmpty());
- MarkIfTrivial(expr->expression());
- Visit(expr->expression());
-}
-
-
-void AssignedVariablesAnalyzer::VisitThisFunction(ThisFunction* expr) {
- // Nothing to do.
- ASSERT(av_.IsEmpty());
-}
-
-
-void AssignedVariablesAnalyzer::VisitDeclaration(Declaration* decl) {
- UNREACHABLE();
-}
-
-
} } // namespace v8::internal
diff --git a/src/data-flow.h b/src/data-flow.h
index 573d7d8..76cff88 100644
--- a/src/data-flow.h
+++ b/src/data-flow.h
@@ -335,44 +335,6 @@
List<T*> queue_;
};
-
-// Computes the set of assigned variables and annotates variables proxies
-// that are trivial sub-expressions and for-loops where the loop variable
-// is guaranteed to be a smi.
-class AssignedVariablesAnalyzer : public AstVisitor {
- public:
- static bool Analyze(CompilationInfo* info);
-
- private:
- AssignedVariablesAnalyzer(CompilationInfo* info, int bits);
- bool Analyze();
-
- Variable* FindSmiLoopVariable(ForStatement* stmt);
-
- int BitIndex(Variable* var);
-
- void RecordAssignedVar(Variable* var);
-
- void MarkIfTrivial(Expression* expr);
-
- // Visits an expression saving the accumulator before, clearing
- // it before visting and restoring it after visiting.
- void ProcessExpression(Expression* expr);
-
- // AST node visit functions.
-#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
- AST_NODE_LIST(DECLARE_VISIT)
-#undef DECLARE_VISIT
-
- CompilationInfo* info_;
-
- // Accumulator for assigned variables set.
- BitVector av_;
-
- DISALLOW_COPY_AND_ASSIGN(AssignedVariablesAnalyzer);
-};
-
-
} } // namespace v8::internal
diff --git a/src/dateparser-inl.h b/src/dateparser-inl.h
index ac28c62..7f8fac8 100644
--- a/src/dateparser-inl.h
+++ b/src/dateparser-inl.h
@@ -1,4 +1,4 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -34,9 +34,11 @@
namespace internal {
template <typename Char>
-bool DateParser::Parse(Vector<Char> str, FixedArray* out) {
+bool DateParser::Parse(Vector<Char> str,
+ FixedArray* out,
+ UnicodeCache* unicode_cache) {
ASSERT(out->length() >= OUTPUT_SIZE);
- InputReader<Char> in(str);
+ InputReader<Char> in(unicode_cache, str);
TimeZoneComposer tz;
TimeComposer time;
DayComposer day;
diff --git a/src/dateparser.h b/src/dateparser.h
index 51109ee..9d29715 100644
--- a/src/dateparser.h
+++ b/src/dateparser.h
@@ -1,4 +1,4 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -49,7 +49,7 @@
// [7]: UTC offset in seconds, or null value if no timezone specified
// If parsing fails, return false (content of output array is not defined).
template <typename Char>
- static bool Parse(Vector<Char> str, FixedArray* output);
+ static bool Parse(Vector<Char> str, FixedArray* output, UnicodeCache* cache);
enum {
YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND, UTC_OFFSET, OUTPUT_SIZE
@@ -67,11 +67,11 @@
template <typename Char>
class InputReader BASE_EMBEDDED {
public:
- explicit InputReader(Vector<Char> s)
+ InputReader(UnicodeCache* unicode_cache, Vector<Char> s)
: index_(0),
buffer_(s),
has_read_number_(false),
- scanner_constants_(Isolate::Current()->scanner_constants()) {
+ unicode_cache_(unicode_cache) {
Next();
}
@@ -122,7 +122,7 @@
}
bool SkipWhiteSpace() {
- if (scanner_constants_->IsWhiteSpace(ch_)) {
+ if (unicode_cache_->IsWhiteSpace(ch_)) {
Next();
return true;
}
@@ -158,7 +158,7 @@
Vector<Char> buffer_;
bool has_read_number_;
uint32_t ch_;
- ScannerConstants* scanner_constants_;
+ UnicodeCache* unicode_cache_;
};
enum KeywordType { INVALID, MONTH_NAME, TIME_ZONE_NAME, AM_PM };
diff --git a/src/debug.cc b/src/debug.cc
index bc532ef..3691333 100644
--- a/src/debug.cc
+++ b/src/debug.cc
@@ -167,7 +167,6 @@
Address target = original_rinfo()->target_address();
Code* code = Code::GetCodeFromTargetAddress(target);
if ((code->is_inline_cache_stub() &&
- !code->is_binary_op_stub() &&
!code->is_type_recording_binary_op_stub() &&
!code->is_compare_ic_stub()) ||
RelocInfo::IsConstructCall(rmode())) {
@@ -478,21 +477,6 @@
// calling convention used by the call site.
Handle<Code> dbgbrk_code(Debug::FindDebugBreak(code, mode));
rinfo()->set_target_address(dbgbrk_code->entry());
-
- // For stubs that refer back to an inlined version clear the cached map for
- // the inlined case to always go through the IC. As long as the break point
- // is set the patching performed by the runtime system will take place in
- // the code copy and will therefore have no effect on the running code
- // keeping it from using the inlined code.
- if (code->is_keyed_load_stub()) {
- KeyedLoadIC::ClearInlinedVersion(pc());
- } else if (code->is_keyed_store_stub()) {
- KeyedStoreIC::ClearInlinedVersion(pc());
- } else if (code->is_load_stub()) {
- LoadIC::ClearInlinedVersion(pc());
- } else if (code->is_store_stub()) {
- StoreIC::ClearInlinedVersion(pc());
- }
}
}
@@ -500,20 +484,6 @@
void BreakLocationIterator::ClearDebugBreakAtIC() {
// Patch the code to the original invoke.
rinfo()->set_target_address(original_rinfo()->target_address());
-
- RelocInfo::Mode mode = rmode();
- if (RelocInfo::IsCodeTarget(mode)) {
- AssertNoAllocation nogc;
- Address target = original_rinfo()->target_address();
- Code* code = Code::GetCodeFromTargetAddress(target);
-
- // Restore the inlined version of keyed stores to get back to the
- // fast case. We need to patch back the keyed store because no
- // patching happens when running normally. For keyed loads, the
- // map check will get patched back when running normally after ICs
- // have been cleared at GC.
- if (code->is_keyed_store_stub()) KeyedStoreIC::RestoreInlinedVersion(pc());
- }
}
@@ -810,7 +780,7 @@
Handle<Object> message = MessageHandler::MakeMessageObject(
"error_loading_debugger", NULL, Vector<Handle<Object> >::empty(),
Handle<String>(), Handle<JSArray>());
- MessageHandler::ReportMessage(NULL, message);
+ MessageHandler::ReportMessage(Isolate::Current(), NULL, message);
return false;
}
@@ -844,6 +814,7 @@
HandleScope scope(isolate_);
Handle<Context> context =
isolate_->bootstrapper()->CreateEnvironment(
+ isolate_,
Handle<Object>::null(),
v8::Handle<ObjectTemplate>(),
NULL);
@@ -917,24 +888,20 @@
}
-// This remains a static method so that generated code can call it.
-Object* Debug::Break(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
-
- Debug* debug = isolate->debug();
- Heap* heap = isolate->heap();
- HandleScope scope(isolate);
+Object* Debug::Break(Arguments args) {
+ Heap* heap = isolate_->heap();
+ HandleScope scope(isolate_);
ASSERT(args.length() == 0);
- debug->thread_local_.frame_drop_mode_ = FRAMES_UNTOUCHED;
+ thread_local_.frame_drop_mode_ = FRAMES_UNTOUCHED;
// Get the top-most JavaScript frame.
- JavaScriptFrameIterator it;
+ JavaScriptFrameIterator it(isolate_);
JavaScriptFrame* frame = it.frame();
// Just continue if breaks are disabled or debugger cannot be loaded.
- if (debug->disable_break() || !debug->Load()) {
- debug->SetAfterBreakTarget(frame);
+ if (disable_break() || !Load()) {
+ SetAfterBreakTarget(frame);
return heap->undefined_value();
}
@@ -945,7 +912,7 @@
}
// Postpone interrupt during breakpoint processing.
- PostponeInterruptsScope postpone(isolate);
+ PostponeInterruptsScope postpone(isolate_);
// Get the debug info (create it if it does not exist).
Handle<SharedFunctionInfo> shared =
@@ -958,10 +925,10 @@
break_location_iterator.FindBreakLocationFromAddress(frame->pc());
// Check whether step next reached a new statement.
- if (!debug->StepNextContinue(&break_location_iterator, frame)) {
+ if (!StepNextContinue(&break_location_iterator, frame)) {
// Decrease steps left if performing multiple steps.
- if (debug->thread_local_.step_count_ > 0) {
- debug->thread_local_.step_count_--;
+ if (thread_local_.step_count_ > 0) {
+ thread_local_.step_count_--;
}
}
@@ -971,56 +938,55 @@
if (break_location_iterator.HasBreakPoint()) {
Handle<Object> break_point_objects =
Handle<Object>(break_location_iterator.BreakPointObjects());
- break_points_hit = debug->CheckBreakPoints(break_point_objects);
+ break_points_hit = CheckBreakPoints(break_point_objects);
}
// If step out is active skip everything until the frame where we need to step
// out to is reached, unless real breakpoint is hit.
- if (debug->StepOutActive() && frame->fp() != debug->step_out_fp() &&
+ if (StepOutActive() && frame->fp() != step_out_fp() &&
break_points_hit->IsUndefined() ) {
// Step count should always be 0 for StepOut.
- ASSERT(debug->thread_local_.step_count_ == 0);
+ ASSERT(thread_local_.step_count_ == 0);
} else if (!break_points_hit->IsUndefined() ||
- (debug->thread_local_.last_step_action_ != StepNone &&
- debug->thread_local_.step_count_ == 0)) {
+ (thread_local_.last_step_action_ != StepNone &&
+ thread_local_.step_count_ == 0)) {
// Notify debugger if a real break point is triggered or if performing
// single stepping with no more steps to perform. Otherwise do another step.
// Clear all current stepping setup.
- debug->ClearStepping();
+ ClearStepping();
// Notify the debug event listeners.
- isolate->debugger()->OnDebugBreak(break_points_hit, false);
- } else if (debug->thread_local_.last_step_action_ != StepNone) {
+ isolate_->debugger()->OnDebugBreak(break_points_hit, false);
+ } else if (thread_local_.last_step_action_ != StepNone) {
// Hold on to last step action as it is cleared by the call to
// ClearStepping.
- StepAction step_action = debug->thread_local_.last_step_action_;
- int step_count = debug->thread_local_.step_count_;
+ StepAction step_action = thread_local_.last_step_action_;
+ int step_count = thread_local_.step_count_;
// Clear all current stepping setup.
- debug->ClearStepping();
+ ClearStepping();
// Set up for the remaining steps.
- debug->PrepareStep(step_action, step_count);
+ PrepareStep(step_action, step_count);
}
- if (debug->thread_local_.frame_drop_mode_ == FRAMES_UNTOUCHED) {
- debug->SetAfterBreakTarget(frame);
- } else if (debug->thread_local_.frame_drop_mode_ ==
+ if (thread_local_.frame_drop_mode_ == FRAMES_UNTOUCHED) {
+ SetAfterBreakTarget(frame);
+ } else if (thread_local_.frame_drop_mode_ ==
FRAME_DROPPED_IN_IC_CALL) {
// We must have been calling IC stub. Do not go there anymore.
- Code* plain_return =
- Isolate::Current()->builtins()->builtin(
- Builtins::kPlainReturn_LiveEdit);
- debug->thread_local_.after_break_target_ = plain_return->entry();
- } else if (debug->thread_local_.frame_drop_mode_ ==
+ Code* plain_return = isolate_->builtins()->builtin(
+ Builtins::kPlainReturn_LiveEdit);
+ thread_local_.after_break_target_ = plain_return->entry();
+ } else if (thread_local_.frame_drop_mode_ ==
FRAME_DROPPED_IN_DEBUG_SLOT_CALL) {
// Debug break slot stub does not return normally, instead it manually
// cleans the stack and jumps. We should patch the jump address.
- Code* plain_return = Isolate::Current()->builtins()->builtin(
+ Code* plain_return = isolate_->builtins()->builtin(
Builtins::kFrameDropper_LiveEdit);
- debug->thread_local_.after_break_target_ = plain_return->entry();
- } else if (debug->thread_local_.frame_drop_mode_ ==
+ thread_local_.after_break_target_ = plain_return->entry();
+ } else if (thread_local_.frame_drop_mode_ ==
FRAME_DROPPED_IN_DIRECT_CALL) {
// Nothing to do, after_break_target is not used here.
} else {
@@ -1031,6 +997,11 @@
}
+RUNTIME_FUNCTION(Object*, Debug_Break) {
+ return isolate->debug()->Break(args);
+}
+
+
// Check the break point objects for whether one or more are actually
// triggered. This function returns a JSArray with the break point objects
// which is triggered.
@@ -1224,7 +1195,7 @@
// If there is no JavaScript stack don't do anything.
return;
}
- for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) {
+ for (JavaScriptFrameIterator it(isolate_, id); !it.done(); it.Advance()) {
JavaScriptFrame* frame = it.frame();
if (frame->HasHandler()) {
Handle<SharedFunctionInfo> shared =
@@ -1280,7 +1251,7 @@
// If there is no JavaScript stack don't do anything.
return;
}
- JavaScriptFrameIterator frames_it(id);
+ JavaScriptFrameIterator frames_it(isolate_, id);
JavaScriptFrame* frame = frames_it.frame();
// First of all ensure there is one-shot break points in the top handler
@@ -1777,7 +1748,7 @@
Handle<Code> original_code(debug_info->original_code());
#ifdef DEBUG
// Get the code which is actually executing.
- Handle<Code> frame_code(frame->LookupCode(isolate_));
+ Handle<Code> frame_code(frame->LookupCode());
ASSERT(frame_code.is_identical_to(code));
#endif
@@ -1859,7 +1830,7 @@
Handle<Code> code(debug_info->code());
#ifdef DEBUG
// Get the code which is actually executing.
- Handle<Code> frame_code(frame->LookupCode(Isolate::Current()));
+ Handle<Code> frame_code(frame->LookupCode());
ASSERT(frame_code.is_identical_to(code));
#endif
diff --git a/src/debug.h b/src/debug.h
index d512595..9366fc3 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -228,7 +228,7 @@
void PreemptionWhileInDebugger();
void Iterate(ObjectVisitor* v);
- static Object* Break(RUNTIME_CALLING_CONVENTION);
+ Object* Break(Arguments args);
void SetBreakPoint(Handle<SharedFunctionInfo> shared,
Handle<Object> break_point_object,
int* source_position);
@@ -548,6 +548,9 @@
};
+DECLARE_RUNTIME_FUNCTION(Object*, Debug_Break);
+
+
// Message delivered to the message handler callback. This is either a debugger
// event or the response to a command.
class MessageImpl: public v8::Debug::Message {
@@ -860,6 +863,7 @@
EnterDebugger()
: isolate_(Isolate::Current()),
prev_(isolate_->debug()->debugger_entry()),
+ it_(isolate_),
has_js_frames_(!it_.done()),
save_(isolate_) {
Debug* debug = isolate_->debug();
diff --git a/src/deoptimizer.cc b/src/deoptimizer.cc
index 4372af0..2fc0e47 100644
--- a/src/deoptimizer.cc
+++ b/src/deoptimizer.cc
@@ -199,8 +199,7 @@
}
-void Deoptimizer::ComputeOutputFrames(Deoptimizer* deoptimizer,
- Isolate* isolate) {
+void Deoptimizer::ComputeOutputFrames(Deoptimizer* deoptimizer) {
deoptimizer->DoComputeOutputFrames();
}
@@ -219,8 +218,7 @@
fp_to_sp_delta_(fp_to_sp_delta),
output_count_(0),
output_(NULL),
- integer32_values_(NULL),
- double_values_(NULL) {
+ deferred_heap_numbers_(0) {
if (FLAG_trace_deopt && type != OSR) {
PrintF("**** DEOPT: ");
function->PrintName();
@@ -259,8 +257,6 @@
Deoptimizer::~Deoptimizer() {
ASSERT(input_ == NULL && output_ == NULL);
- delete[] integer32_values_;
- delete[] double_values_;
}
@@ -391,13 +387,8 @@
int count = iterator.Next();
ASSERT(output_ == NULL);
output_ = new FrameDescription*[count];
- // Per-frame lists of untagged and unboxed int32 and double values.
- integer32_values_ = new List<ValueDescriptionInteger32>[count];
- double_values_ = new List<ValueDescriptionDouble>[count];
for (int i = 0; i < count; ++i) {
output_[i] = NULL;
- integer32_values_[i].Initialize(0);
- double_values_[i].Initialize(0);
}
output_count_ = count;
@@ -425,37 +416,19 @@
}
-void Deoptimizer::InsertHeapNumberValues(int index, JavaScriptFrame* frame) {
- // We need to adjust the stack index by one for the top-most frame.
- int extra_slot_count = (index == output_count() - 1) ? 1 : 0;
- List<ValueDescriptionInteger32>* ints = &integer32_values_[index];
- for (int i = 0; i < ints->length(); i++) {
- ValueDescriptionInteger32 value = ints->at(i);
- double val = static_cast<double>(value.int32_value());
- InsertHeapNumberValue(frame, value.stack_index(), val, extra_slot_count);
+void Deoptimizer::MaterializeHeapNumbers() {
+ for (int i = 0; i < deferred_heap_numbers_.length(); i++) {
+ HeapNumberMaterializationDescriptor d = deferred_heap_numbers_[i];
+ Handle<Object> num = isolate_->factory()->NewNumber(d.value());
+ if (FLAG_trace_deopt) {
+ PrintF("Materializing a new heap number %p [%e] in slot %p\n",
+ reinterpret_cast<void*>(*num),
+ d.value(),
+ d.slot_address());
+ }
+
+ Memory::Object_at(d.slot_address()) = *num;
}
-
- // Iterate over double values and convert them to a heap number.
- List<ValueDescriptionDouble>* doubles = &double_values_[index];
- for (int i = 0; i < doubles->length(); ++i) {
- ValueDescriptionDouble value = doubles->at(i);
- InsertHeapNumberValue(frame, value.stack_index(), value.double_value(),
- extra_slot_count);
- }
-}
-
-
-void Deoptimizer::InsertHeapNumberValue(JavaScriptFrame* frame,
- int stack_index,
- double val,
- int extra_slot_count) {
- // Add one to the TOS index to take the 'state' pushed before jumping
- // to the stub that calls Runtime::NotifyDeoptimized into account.
- int tos_index = stack_index + extra_slot_count;
- int index = (frame->ComputeExpressionsCount() - 1) - tos_index;
- if (FLAG_trace_deopt) PrintF("Allocating a new heap number: %e\n", val);
- Handle<Object> num = isolate_->factory()->NewNumber(val);
- frame->SetExpression(index, *num);
}
@@ -501,7 +474,6 @@
int input_reg = iterator->Next();
intptr_t value = input_->GetRegister(input_reg);
bool is_smi = Smi::IsValid(value);
- unsigned output_index = output_offset / kPointerSize;
if (FLAG_trace_deopt) {
PrintF(
" 0x%08" V8PRIxPTR ": [top + %d] <- %" V8PRIdPTR " ; %s (%s)\n",
@@ -518,9 +490,8 @@
} else {
// We save the untagged value on the side and store a GC-safe
// temporary placeholder in the frame.
- AddInteger32Value(frame_index,
- output_index,
- static_cast<int32_t>(value));
+ AddDoubleValue(output_[frame_index]->GetTop() + output_offset,
+ static_cast<double>(static_cast<int32_t>(value)));
output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
}
return;
@@ -529,7 +500,6 @@
case Translation::DOUBLE_REGISTER: {
int input_reg = iterator->Next();
double value = input_->GetDoubleRegister(input_reg);
- unsigned output_index = output_offset / kPointerSize;
if (FLAG_trace_deopt) {
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- %e ; %s\n",
output_[frame_index]->GetTop() + output_offset,
@@ -539,7 +509,7 @@
}
// We save the untagged value on the side and store a GC-safe
// temporary placeholder in the frame.
- AddDoubleValue(frame_index, output_index, value);
+ AddDoubleValue(output_[frame_index]->GetTop() + output_offset, value);
output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
return;
}
@@ -567,7 +537,6 @@
input_->GetOffsetFromSlotIndex(this, input_slot_index);
intptr_t value = input_->GetFrameSlot(input_offset);
bool is_smi = Smi::IsValid(value);
- unsigned output_index = output_offset / kPointerSize;
if (FLAG_trace_deopt) {
PrintF(" 0x%08" V8PRIxPTR ": ",
output_[frame_index]->GetTop() + output_offset);
@@ -584,9 +553,8 @@
} else {
// We save the untagged value on the side and store a GC-safe
// temporary placeholder in the frame.
- AddInteger32Value(frame_index,
- output_index,
- static_cast<int32_t>(value));
+ AddDoubleValue(output_[frame_index]->GetTop() + output_offset,
+ static_cast<double>(static_cast<int32_t>(value)));
output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
}
return;
@@ -597,7 +565,6 @@
unsigned input_offset =
input_->GetOffsetFromSlotIndex(this, input_slot_index);
double value = input_->GetDoubleFrameSlot(input_offset);
- unsigned output_index = output_offset / kPointerSize;
if (FLAG_trace_deopt) {
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- %e ; [esp + %d]\n",
output_[frame_index]->GetTop() + output_offset,
@@ -607,7 +574,7 @@
}
// We save the untagged value on the side and store a GC-safe
// temporary placeholder in the frame.
- AddDoubleValue(frame_index, output_index, value);
+ AddDoubleValue(output_[frame_index]->GetTop() + output_offset, value);
output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
return;
}
@@ -911,19 +878,11 @@
}
-void Deoptimizer::AddInteger32Value(int frame_index,
- int slot_index,
- int32_t value) {
- ValueDescriptionInteger32 value_desc(slot_index, value);
- integer32_values_[frame_index].Add(value_desc);
-}
-
-
-void Deoptimizer::AddDoubleValue(int frame_index,
- int slot_index,
+void Deoptimizer::AddDoubleValue(intptr_t slot_address,
double value) {
- ValueDescriptionDouble value_desc(slot_index, value);
- double_values_[frame_index].Add(value_desc);
+ HeapNumberMaterializationDescriptor value_desc(
+ reinterpret_cast<Address>(slot_address), value);
+ deferred_heap_numbers_.Add(value_desc);
}
@@ -934,7 +893,7 @@
// isn't meant to be serialized at all.
ASSERT(!Serializer::enabled());
- MacroAssembler masm(NULL, 16 * KB);
+ MacroAssembler masm(Isolate::Current(), NULL, 16 * KB);
masm.set_emit_debug_code(false);
GenerateDeoptimizationEntries(&masm, kNumberOfEntries, type);
CodeDesc desc;
@@ -1195,4 +1154,103 @@
}
+// We can't intermix stack decoding and allocations because
+// deoptimization infrastracture is not GC safe.
+// Thus we build a temporary structure in malloced space.
+SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator,
+ DeoptimizationInputData* data,
+ JavaScriptFrame* frame) {
+ Translation::Opcode opcode =
+ static_cast<Translation::Opcode>(iterator->Next());
+
+ switch (opcode) {
+ case Translation::BEGIN:
+ case Translation::FRAME:
+ // Peeled off before getting here.
+ break;
+
+ case Translation::ARGUMENTS_OBJECT:
+ // This can be only emitted for local slots not for argument slots.
+ break;
+
+ case Translation::REGISTER:
+ case Translation::INT32_REGISTER:
+ case Translation::DOUBLE_REGISTER:
+ case Translation::DUPLICATE:
+ // We are at safepoint which corresponds to call. All registers are
+ // saved by caller so there would be no live registers at this
+ // point. Thus these translation commands should not be used.
+ break;
+
+ case Translation::STACK_SLOT: {
+ int slot_index = iterator->Next();
+ Address slot_addr = SlotAddress(frame, slot_index);
+ return SlotRef(slot_addr, SlotRef::TAGGED);
+ }
+
+ case Translation::INT32_STACK_SLOT: {
+ int slot_index = iterator->Next();
+ Address slot_addr = SlotAddress(frame, slot_index);
+ return SlotRef(slot_addr, SlotRef::INT32);
+ }
+
+ case Translation::DOUBLE_STACK_SLOT: {
+ int slot_index = iterator->Next();
+ Address slot_addr = SlotAddress(frame, slot_index);
+ return SlotRef(slot_addr, SlotRef::DOUBLE);
+ }
+
+ case Translation::LITERAL: {
+ int literal_index = iterator->Next();
+ return SlotRef(data->LiteralArray()->get(literal_index));
+ }
+ }
+
+ UNREACHABLE();
+ return SlotRef();
+}
+
+
+void SlotRef::ComputeSlotMappingForArguments(JavaScriptFrame* frame,
+ int inlined_frame_index,
+ Vector<SlotRef>* args_slots) {
+ AssertNoAllocation no_gc;
+ int deopt_index = AstNode::kNoNumber;
+ DeoptimizationInputData* data =
+ static_cast<OptimizedFrame*>(frame)->GetDeoptimizationData(&deopt_index);
+ TranslationIterator it(data->TranslationByteArray(),
+ data->TranslationIndex(deopt_index)->value());
+ Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
+ ASSERT(opcode == Translation::BEGIN);
+ int frame_count = it.Next();
+ USE(frame_count);
+ ASSERT(frame_count > inlined_frame_index);
+ int frames_to_skip = inlined_frame_index;
+ while (true) {
+ opcode = static_cast<Translation::Opcode>(it.Next());
+ // Skip over operands to advance to the next opcode.
+ it.Skip(Translation::NumberOfOperandsFor(opcode));
+ if (opcode == Translation::FRAME) {
+ if (frames_to_skip == 0) {
+ // We reached the frame corresponding to the inlined function
+ // in question. Process the translation commands for the
+ // arguments.
+ //
+ // Skip the translation command for the receiver.
+ it.Skip(Translation::NumberOfOperandsFor(
+ static_cast<Translation::Opcode>(it.Next())));
+ // Compute slots for arguments.
+ for (int i = 0; i < args_slots->length(); ++i) {
+ (*args_slots)[i] = ComputeSlotForNextArgument(&it, data, frame);
+ }
+ return;
+ }
+ frames_to_skip--;
+ }
+ }
+
+ UNREACHABLE();
+}
+
+
} } // namespace v8::internal
diff --git a/src/deoptimizer.h b/src/deoptimizer.h
index a53de3d..cb82f44 100644
--- a/src/deoptimizer.h
+++ b/src/deoptimizer.h
@@ -42,38 +42,17 @@
class DeoptimizingCodeListNode;
-class ValueDescription BASE_EMBEDDED {
+class HeapNumberMaterializationDescriptor BASE_EMBEDDED {
public:
- explicit ValueDescription(int index) : stack_index_(index) { }
- int stack_index() const { return stack_index_; }
+ HeapNumberMaterializationDescriptor(Address slot_address, double val)
+ : slot_address_(slot_address), val_(val) { }
+
+ Address slot_address() const { return slot_address_; }
+ double value() const { return val_; }
private:
- // Offset relative to the top of the stack.
- int stack_index_;
-};
-
-
-class ValueDescriptionInteger32: public ValueDescription {
- public:
- ValueDescriptionInteger32(int index, int32_t value)
- : ValueDescription(index), int32_value_(value) { }
- int32_t int32_value() const { return int32_value_; }
-
- private:
- // Raw value.
- int32_t int32_value_;
-};
-
-
-class ValueDescriptionDouble: public ValueDescription {
- public:
- ValueDescriptionDouble(int index, double value)
- : ValueDescription(index), double_value_(value) { }
- double double_value() const { return double_value_; }
-
- private:
- // Raw value.
- double double_value_;
+ Address slot_address_;
+ double val_;
};
@@ -190,9 +169,9 @@
~Deoptimizer();
- void InsertHeapNumberValues(int index, JavaScriptFrame* frame);
+ void MaterializeHeapNumbers();
- static void ComputeOutputFrames(Deoptimizer* deoptimizer, Isolate* isolate);
+ static void ComputeOutputFrames(Deoptimizer* deoptimizer);
static Address GetDeoptimizationEntry(int id, BailoutType type);
static int GetDeoptimizationId(Address addr, BailoutType type);
@@ -277,13 +256,7 @@
Object* ComputeLiteral(int index) const;
- void InsertHeapNumberValue(JavaScriptFrame* frame,
- int stack_index,
- double val,
- int extra_slot_count);
-
- void AddInteger32Value(int frame_index, int slot_index, int32_t value);
- void AddDoubleValue(int frame_index, int slot_index, double value);
+ void AddDoubleValue(intptr_t slot_address, double value);
static LargeObjectChunk* CreateCode(BailoutType type);
static void GenerateDeoptimizationEntries(
@@ -310,8 +283,7 @@
// Array of output frame descriptions.
FrameDescription** output_;
- List<ValueDescriptionInteger32>* integer32_values_;
- List<ValueDescriptionDouble>* double_values_;
+ List<HeapNumberMaterializationDescriptor> deferred_heap_numbers_;
static int table_entry_size_;
@@ -552,6 +524,78 @@
};
+class SlotRef BASE_EMBEDDED {
+ public:
+ enum SlotRepresentation {
+ UNKNOWN,
+ TAGGED,
+ INT32,
+ DOUBLE,
+ LITERAL
+ };
+
+ SlotRef()
+ : addr_(NULL), representation_(UNKNOWN) { }
+
+ SlotRef(Address addr, SlotRepresentation representation)
+ : addr_(addr), representation_(representation) { }
+
+ explicit SlotRef(Object* literal)
+ : literal_(literal), representation_(LITERAL) { }
+
+ Handle<Object> GetValue() {
+ switch (representation_) {
+ case TAGGED:
+ return Handle<Object>(Memory::Object_at(addr_));
+
+ case INT32: {
+ int value = Memory::int32_at(addr_);
+ if (Smi::IsValid(value)) {
+ return Handle<Object>(Smi::FromInt(value));
+ } else {
+ return Isolate::Current()->factory()->NewNumberFromInt(value);
+ }
+ }
+
+ case DOUBLE: {
+ double value = Memory::double_at(addr_);
+ return Isolate::Current()->factory()->NewNumber(value);
+ }
+
+ case LITERAL:
+ return literal_;
+
+ default:
+ UNREACHABLE();
+ return Handle<Object>::null();
+ }
+ }
+
+ static void ComputeSlotMappingForArguments(JavaScriptFrame* frame,
+ int inlined_frame_index,
+ Vector<SlotRef>* args_slots);
+
+ private:
+ Address addr_;
+ Handle<Object> literal_;
+ SlotRepresentation representation_;
+
+ static Address SlotAddress(JavaScriptFrame* frame, int slot_index) {
+ if (slot_index >= 0) {
+ const int offset = JavaScriptFrameConstants::kLocal0Offset;
+ return frame->fp() + offset - (slot_index * kPointerSize);
+ } else {
+ const int offset = JavaScriptFrameConstants::kLastParameterOffset;
+ return frame->fp() + offset - ((slot_index + 1) * kPointerSize);
+ }
+ }
+
+ static SlotRef ComputeSlotForNextArgument(TranslationIterator* iterator,
+ DeoptimizationInputData* data,
+ JavaScriptFrame* frame);
+};
+
+
} } // namespace v8::internal
#endif // V8_DEOPTIMIZER_H_
diff --git a/src/disassembler.cc b/src/disassembler.cc
index d142ef6..65e1668 100644
--- a/src/disassembler.cc
+++ b/src/disassembler.cc
@@ -28,7 +28,7 @@
#include "v8.h"
#include "code-stubs.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "debug.h"
#include "deoptimizer.h"
#include "disasm.h"
@@ -282,7 +282,8 @@
} else {
out.AddFormatted(" %s", Code::Kind2String(kind));
}
- } else if (rmode == RelocInfo::RUNTIME_ENTRY) {
+ } else if (rmode == RelocInfo::RUNTIME_ENTRY &&
+ Isolate::Current()->deoptimizer_data() != NULL) {
// A runtime entry reloinfo might be a deoptimization bailout.
Address addr = relocinfo.target_address();
int id = Deoptimizer::GetDeoptimizationId(addr, Deoptimizer::EAGER);
diff --git a/src/execution.cc b/src/execution.cc
index 98c8b68..eb26438 100644
--- a/src/execution.cc
+++ b/src/execution.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -31,7 +31,7 @@
#include "api.h"
#include "bootstrapper.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "debug.h"
#include "runtime-profiler.h"
#include "simulator.h"
@@ -199,6 +199,8 @@
Handle<Object> Execution::GetFunctionDelegate(Handle<Object> object) {
ASSERT(!object->IsJSFunction());
+ Isolate* isolate = Isolate::Current();
+ Factory* factory = isolate->factory();
// If you return a function from here, it will be called when an
// attempt is made to call the given object as a function.
@@ -206,7 +208,7 @@
// Regular expressions can be called as functions in both Firefox
// and Safari so we allow it too.
if (object->IsJSRegExp()) {
- Handle<String> exec = FACTORY->exec_symbol();
+ Handle<String> exec = factory->exec_symbol();
// TODO(lrn): Bug 617. We should use the default function here, not the
// one on the RegExp object.
Object* exec_function;
@@ -214,7 +216,7 @@
// This can lose an exception, but the alternative is to put a failure
// object in a handle, which is not GC safe.
if (!maybe_exec_function->ToObject(&exec_function)) {
- return FACTORY->undefined_value();
+ return factory->undefined_value();
}
}
return Handle<Object>(exec_function);
@@ -225,15 +227,16 @@
if (object->IsHeapObject() &&
HeapObject::cast(*object)->map()->has_instance_call_handler()) {
return Handle<JSFunction>(
- Isolate::Current()->global_context()->call_as_function_delegate());
+ isolate->global_context()->call_as_function_delegate());
}
- return FACTORY->undefined_value();
+ return factory->undefined_value();
}
Handle<Object> Execution::GetConstructorDelegate(Handle<Object> object) {
ASSERT(!object->IsJSFunction());
+ Isolate* isolate = Isolate::Current();
// If you return a function from here, it will be called when an
// attempt is made to call the given object as a constructor.
@@ -243,10 +246,10 @@
if (object->IsHeapObject() &&
HeapObject::cast(*object)->map()->has_instance_call_handler()) {
return Handle<JSFunction>(
- Isolate::Current()->global_context()->call_as_constructor_delegate());
+ isolate->global_context()->call_as_constructor_delegate());
}
- return FACTORY->undefined_value();
+ return isolate->factory()->undefined_value();
}
@@ -467,10 +470,11 @@
#define RETURN_NATIVE_CALL(name, argc, argv, has_pending_exception) \
do { \
+ Isolate* isolate = Isolate::Current(); \
Object** args[argc] = argv; \
ASSERT(has_pending_exception != NULL); \
- return Call(Isolate::Current()->name##_fun(), \
- Isolate::Current()->js_builtins_object(), argc, args, \
+ return Call(isolate->name##_fun(), \
+ isolate->js_builtins_object(), argc, args, \
has_pending_exception); \
} while (false)
@@ -549,20 +553,23 @@
Handle<Object> Execution::CharAt(Handle<String> string, uint32_t index) {
+ Isolate* isolate = string->GetIsolate();
+ Factory* factory = isolate->factory();
+
int int_index = static_cast<int>(index);
if (int_index < 0 || int_index >= string->length()) {
- return FACTORY->undefined_value();
+ return factory->undefined_value();
}
Handle<Object> char_at =
- GetProperty(Isolate::Current()->js_builtins_object(),
- FACTORY->char_at_symbol());
+ GetProperty(isolate->js_builtins_object(),
+ factory->char_at_symbol());
if (!char_at->IsJSFunction()) {
- return FACTORY->undefined_value();
+ return factory->undefined_value();
}
bool caught_exception;
- Handle<Object> index_object = FACTORY->NewNumberFromInt(int_index);
+ Handle<Object> index_object = factory->NewNumberFromInt(int_index);
Object** index_arg[] = { index_object.location() };
Handle<Object> result = TryCall(Handle<JSFunction>::cast(char_at),
string,
@@ -570,7 +577,7 @@
index_arg,
&caught_exception);
if (caught_exception) {
- return FACTORY->undefined_value();
+ return factory->undefined_value();
}
return result;
}
@@ -578,17 +585,18 @@
Handle<JSFunction> Execution::InstantiateFunction(
Handle<FunctionTemplateInfo> data, bool* exc) {
+ Isolate* isolate = data->GetIsolate();
// Fast case: see if the function has already been instantiated
int serial_number = Smi::cast(data->serial_number())->value();
Object* elm =
- Isolate::Current()->global_context()->function_cache()->
+ isolate->global_context()->function_cache()->
GetElementNoExceptionThrown(serial_number);
if (elm->IsJSFunction()) return Handle<JSFunction>(JSFunction::cast(elm));
// The function has not yet been instantiated in this context; do it.
Object** args[1] = { Handle<Object>::cast(data).location() };
Handle<Object> result =
- Call(Isolate::Current()->instantiate_fun(),
- Isolate::Current()->js_builtins_object(), 1, args, exc);
+ Call(isolate->instantiate_fun(),
+ isolate->js_builtins_object(), 1, args, exc);
if (*exc) return Handle<JSFunction>::null();
return Handle<JSFunction>::cast(result);
}
@@ -596,12 +604,13 @@
Handle<JSObject> Execution::InstantiateObject(Handle<ObjectTemplateInfo> data,
bool* exc) {
+ Isolate* isolate = data->GetIsolate();
if (data->property_list()->IsUndefined() &&
!data->constructor()->IsUndefined()) {
// Initialization to make gcc happy.
Object* result = NULL;
{
- HandleScope scope;
+ HandleScope scope(isolate);
Handle<FunctionTemplateInfo> cons_template =
Handle<FunctionTemplateInfo>(
FunctionTemplateInfo::cast(data->constructor()));
@@ -616,8 +625,8 @@
} else {
Object** args[1] = { Handle<Object>::cast(data).location() };
Handle<Object> result =
- Call(Isolate::Current()->instantiate_fun(),
- Isolate::Current()->js_builtins_object(), 1, args, exc);
+ Call(isolate->instantiate_fun(),
+ isolate->js_builtins_object(), 1, args, exc);
if (*exc) return Handle<JSObject>::null();
return Handle<JSObject>::cast(result);
}
@@ -627,9 +636,10 @@
void Execution::ConfigureInstance(Handle<Object> instance,
Handle<Object> instance_template,
bool* exc) {
+ Isolate* isolate = Isolate::Current();
Object** args[2] = { instance.location(), instance_template.location() };
- Execution::Call(Isolate::Current()->configure_instance_fun(),
- Isolate::Current()->js_builtins_object(), 2, args, exc);
+ Execution::Call(isolate->configure_instance_fun(),
+ isolate->js_builtins_object(), 2, args, exc);
}
@@ -637,6 +647,7 @@
Handle<JSFunction> fun,
Handle<Object> pos,
Handle<Object> is_global) {
+ Isolate* isolate = fun->GetIsolate();
const int argc = 4;
Object** args[argc] = { recv.location(),
Handle<Object>::cast(fun).location(),
@@ -644,10 +655,13 @@
is_global.location() };
bool caught_exception = false;
Handle<Object> result =
- TryCall(Isolate::Current()->get_stack_trace_line_fun(),
- Isolate::Current()->js_builtins_object(), argc, args,
+ TryCall(isolate->get_stack_trace_line_fun(),
+ isolate->js_builtins_object(), argc, args,
&caught_exception);
- if (caught_exception || !result->IsString()) return FACTORY->empty_symbol();
+ if (caught_exception || !result->IsString()) {
+ return isolate->factory()->empty_symbol();
+ }
+
return Handle<String>::cast(result);
}
@@ -697,7 +711,7 @@
}
{
- JavaScriptFrameIterator it;
+ JavaScriptFrameIterator it(isolate);
ASSERT(!it.done());
Object* fun = it.frame()->function();
if (fun && fun->IsJSFunction()) {
@@ -728,10 +742,11 @@
}
void Execution::ProcessDebugMesssages(bool debug_command_only) {
+ Isolate* isolate = Isolate::Current();
// Clear the debug command request flag.
- Isolate::Current()->stack_guard()->Continue(DEBUGCOMMAND);
+ isolate->stack_guard()->Continue(DEBUGCOMMAND);
- HandleScope scope;
+ HandleScope scope(isolate);
// Enter the debugger. Just continue if we fail to enter the debugger.
EnterDebugger debugger;
if (debugger.FailedToEnter()) {
@@ -740,8 +755,8 @@
// Notify the debug event listeners. Indicate auto continue if the break was
// a debug command break.
- Isolate::Current()->debugger()->OnDebugBreak(FACTORY->undefined_value(),
- debug_command_only);
+ isolate->debugger()->OnDebugBreak(isolate->factory()->undefined_value(),
+ debug_command_only);
}
diff --git a/src/extensions/experimental/break-iterator.cc b/src/extensions/experimental/break-iterator.cc
index 6f574d4..e8baea7 100644
--- a/src/extensions/experimental/break-iterator.cc
+++ b/src/extensions/experimental/break-iterator.cc
@@ -46,16 +46,16 @@
return NULL;
}
-UnicodeString* BreakIterator::ResetAdoptedText(
+icu::UnicodeString* BreakIterator::ResetAdoptedText(
v8::Handle<v8::Object> obj, v8::Handle<v8::Value> value) {
// Get the previous value from the internal field.
- UnicodeString* text = static_cast<UnicodeString*>(
+ icu::UnicodeString* text = static_cast<icu::UnicodeString*>(
obj->GetPointerFromInternalField(1));
delete text;
// Assign new value to the internal pointer.
v8::String::Value text_value(value);
- text = new UnicodeString(
+ text = new icu::UnicodeString(
reinterpret_cast<const UChar*>(*text_value), text_value.length());
obj->SetPointerInInternalField(1, text);
@@ -74,7 +74,7 @@
// pointing to a break iterator.
delete UnpackBreakIterator(persistent_object);
- delete static_cast<UnicodeString*>(
+ delete static_cast<icu::UnicodeString*>(
persistent_object->GetPointerFromInternalField(1));
// Then dispose of the persistent handle to JS object.
@@ -144,8 +144,9 @@
}
// TODO(cira): Remove cast once ICU fixes base BreakIterator class.
- int32_t status =
- static_cast<RuleBasedBreakIterator*>(break_iterator)->getRuleStatus();
+ icu::RuleBasedBreakIterator* rule_based_iterator =
+ static_cast<icu::RuleBasedBreakIterator*>(break_iterator);
+ int32_t status = rule_based_iterator->getRuleStatus();
// Keep return values in sync with JavaScript BreakType enum.
if (status >= UBRK_WORD_NONE && status < UBRK_WORD_NONE_LIMIT) {
return v8::Int32::New(UBRK_WORD_NONE);
diff --git a/src/extensions/experimental/break-iterator.h b/src/extensions/experimental/break-iterator.h
index 473bc89..fac1ed8 100644
--- a/src/extensions/experimental/break-iterator.h
+++ b/src/extensions/experimental/break-iterator.h
@@ -51,8 +51,8 @@
// Deletes the old value and sets the adopted text in
// corresponding JavaScript object.
- static UnicodeString* ResetAdoptedText(v8::Handle<v8::Object> obj,
- v8::Handle<v8::Value> text_value);
+ static icu::UnicodeString* ResetAdoptedText(v8::Handle<v8::Object> obj,
+ v8::Handle<v8::Value> text_value);
// Release memory we allocated for the BreakIterator once the JS object that
// holds the pointer gets garbage collected.
diff --git a/src/extensions/experimental/collator.cc b/src/extensions/experimental/collator.cc
new file mode 100644
index 0000000..7d1a21d
--- /dev/null
+++ b/src/extensions/experimental/collator.cc
@@ -0,0 +1,218 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "collator.h"
+
+#include "unicode/coll.h"
+#include "unicode/locid.h"
+#include "unicode/ucol.h"
+
+namespace v8 {
+namespace internal {
+
+v8::Persistent<v8::FunctionTemplate> Collator::collator_template_;
+
+icu::Collator* Collator::UnpackCollator(v8::Handle<v8::Object> obj) {
+ if (collator_template_->HasInstance(obj)) {
+ return static_cast<icu::Collator*>(obj->GetPointerFromInternalField(0));
+ }
+
+ return NULL;
+}
+
+void Collator::DeleteCollator(v8::Persistent<v8::Value> object, void* param) {
+ v8::Persistent<v8::Object> persistent_object =
+ v8::Persistent<v8::Object>::Cast(object);
+
+ // First delete the hidden C++ object.
+ // Unpacking should never return NULL here. That would only happen if
+ // this method is used as the weak callback for persistent handles not
+ // pointing to a collator.
+ delete UnpackCollator(persistent_object);
+
+ // Then dispose of the persistent handle to JS object.
+ persistent_object.Dispose();
+}
+
+// Throws a JavaScript exception.
+static v8::Handle<v8::Value> ThrowUnexpectedObjectError() {
+ // Returns undefined, and schedules an exception to be thrown.
+ return v8::ThrowException(v8::Exception::Error(
+ v8::String::New("Collator method called on an object "
+ "that is not a Collator.")));
+}
+
+// Extract a boolean option named in |option| and set it to |result|.
+// Return true if it's specified. Otherwise, return false.
+static bool ExtractBooleanOption(const v8::Local<v8::Object>& options,
+ const char* option,
+ bool* result) {
+ v8::HandleScope handle_scope;
+ v8::TryCatch try_catch;
+ v8::Handle<v8::Value> value = options->Get(v8::String::New(option));
+ if (try_catch.HasCaught()) {
+ return false;
+ }
+ // No need to check if |value| is empty because it's taken care of
+ // by TryCatch above.
+ if (!value->IsUndefined() && !value->IsNull()) {
+ if (value->IsBoolean()) {
+ *result = value->BooleanValue();
+ return true;
+ }
+ }
+ return false;
+}
+
+// When there's an ICU error, throw a JavaScript error with |message|.
+static v8::Handle<v8::Value> ThrowExceptionForICUError(const char* message) {
+ return v8::ThrowException(v8::Exception::Error(v8::String::New(message)));
+}
+
+v8::Handle<v8::Value> Collator::CollatorCompare(const v8::Arguments& args) {
+ if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsString()) {
+ return v8::ThrowException(v8::Exception::SyntaxError(
+ v8::String::New("Two string arguments are required.")));
+ }
+
+ icu::Collator* collator = UnpackCollator(args.Holder());
+ if (!collator) {
+ return ThrowUnexpectedObjectError();
+ }
+
+ v8::String::Value string_value1(args[0]);
+ v8::String::Value string_value2(args[1]);
+ const UChar* string1 = reinterpret_cast<const UChar*>(*string_value1);
+ const UChar* string2 = reinterpret_cast<const UChar*>(*string_value2);
+ UErrorCode status = U_ZERO_ERROR;
+ UCollationResult result = collator->compare(
+ string1, string_value1.length(), string2, string_value2.length(), status);
+
+ if (U_FAILURE(status)) {
+ return ThrowExceptionForICUError(
+ "Unexpected failure in Collator.compare.");
+ }
+
+ return v8::Int32::New(result);
+}
+
+v8::Handle<v8::Value> Collator::JSCollator(const v8::Arguments& args) {
+ v8::HandleScope handle_scope;
+
+ if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsObject()) {
+ return v8::ThrowException(v8::Exception::SyntaxError(
+ v8::String::New("Locale and collation options are required.")));
+ }
+
+ v8::String::AsciiValue locale(args[0]);
+ icu::Locale icu_locale(*locale);
+
+ icu::Collator* collator = NULL;
+ UErrorCode status = U_ZERO_ERROR;
+ collator = icu::Collator::createInstance(icu_locale, status);
+
+ if (U_FAILURE(status)) {
+ delete collator;
+ return ThrowExceptionForICUError("Failed to create collator.");
+ }
+
+ v8::Local<v8::Object> options(args[1]->ToObject());
+
+ // Below, we change collation options that are explicitly specified
+ // by a caller in JavaScript. Otherwise, we don't touch because
+ // we don't want to change the locale-dependent default value.
+ // The three options below are very likely to have the same default
+ // across locales, but I haven't checked them all. Others we may add
+ // in the future have certainly locale-dependent default (e.g.
+ // caseFirst is upperFirst for Danish while is off for most other locales).
+
+ bool ignore_case, ignore_accents, numeric;
+
+ if (ExtractBooleanOption(options, "ignoreCase", &ignore_case)) {
+ collator->setAttribute(UCOL_CASE_LEVEL, ignore_case ? UCOL_OFF : UCOL_ON,
+ status);
+ if (U_FAILURE(status)) {
+ delete collator;
+ return ThrowExceptionForICUError("Failed to set ignoreCase.");
+ }
+ }
+
+ // Accents are taken into account with strength secondary or higher.
+ if (ExtractBooleanOption(options, "ignoreAccents", &ignore_accents)) {
+ if (!ignore_accents) {
+ collator->setStrength(icu::Collator::SECONDARY);
+ } else {
+ collator->setStrength(icu::Collator::PRIMARY);
+ }
+ }
+
+ if (ExtractBooleanOption(options, "numeric", &numeric)) {
+ collator->setAttribute(UCOL_NUMERIC_COLLATION,
+ numeric ? UCOL_ON : UCOL_OFF, status);
+ if (U_FAILURE(status)) {
+ delete collator;
+ return ThrowExceptionForICUError("Failed to set numeric sort option.");
+ }
+ }
+
+ if (collator_template_.IsEmpty()) {
+ v8::Local<v8::FunctionTemplate> raw_template(v8::FunctionTemplate::New());
+ raw_template->SetClassName(v8::String::New("v8Locale.Collator"));
+
+ // Define internal field count on instance template.
+ v8::Local<v8::ObjectTemplate> object_template =
+ raw_template->InstanceTemplate();
+
+ // Set aside internal fields for icu collator.
+ object_template->SetInternalFieldCount(1);
+
+ // Define all of the prototype methods on prototype template.
+ v8::Local<v8::ObjectTemplate> proto = raw_template->PrototypeTemplate();
+ proto->Set(v8::String::New("compare"),
+ v8::FunctionTemplate::New(CollatorCompare));
+
+ collator_template_ =
+ v8::Persistent<v8::FunctionTemplate>::New(raw_template);
+ }
+
+ // Create an empty object wrapper.
+ v8::Local<v8::Object> local_object =
+ collator_template_->GetFunction()->NewInstance();
+ v8::Persistent<v8::Object> wrapper =
+ v8::Persistent<v8::Object>::New(local_object);
+
+ // Set collator as internal field of the resulting JS object.
+ wrapper->SetPointerInInternalField(0, collator);
+
+ // Make object handle weak so we can delete iterator once GC kicks in.
+ wrapper.MakeWeak(NULL, DeleteCollator);
+
+ return wrapper;
+}
+
+} } // namespace v8::internal
+
diff --git a/src/extensions/experimental/collator.h b/src/extensions/experimental/collator.h
new file mode 100644
index 0000000..10d6ffb
--- /dev/null
+++ b/src/extensions/experimental/collator.h
@@ -0,0 +1,69 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef V8_EXTENSIONS_EXPERIMENTAL_COLLATOR_H
+#define V8_EXTENSIONS_EXPERIMENTAL_COLLATOR_H_
+
+#include <v8.h>
+
+#include "unicode/uversion.h"
+
+namespace U_ICU_NAMESPACE {
+class Collator;
+class UnicodeString;
+}
+
+namespace v8 {
+namespace internal {
+
+class Collator {
+ public:
+ static v8::Handle<v8::Value> JSCollator(const v8::Arguments& args);
+
+ // Helper methods for various bindings.
+
+ // Unpacks collator object from corresponding JavaScript object.
+ static icu::Collator* UnpackCollator(v8::Handle<v8::Object> obj);
+
+ // Release memory we allocated for the Collator once the JS object that
+ // holds the pointer gets garbage collected.
+ static void DeleteCollator(v8::Persistent<v8::Value> object, void* param);
+
+ // Compare two strings and returns -1, 0 and 1 depending on
+ // whether string1 is smaller than, equal to or larger than string2.
+ static v8::Handle<v8::Value> CollatorCompare(const v8::Arguments& args);
+
+ private:
+ Collator() {}
+
+ static v8::Persistent<v8::FunctionTemplate> collator_template_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_EXTENSIONS_EXPERIMENTAL_COLLATOR
+
diff --git a/src/extensions/experimental/experimental.gyp b/src/extensions/experimental/experimental.gyp
index 761f4c7..d1194ce 100644
--- a/src/extensions/experimental/experimental.gyp
+++ b/src/extensions/experimental/experimental.gyp
@@ -39,8 +39,13 @@
'sources': [
'break-iterator.cc',
'break-iterator.h',
+ 'collator.cc',
+ 'collator.h',
'i18n-extension.cc',
'i18n-extension.h',
+ 'i18n-locale.cc',
+ 'i18n-locale.h',
+ '<(SHARED_INTERMEDIATE_DIR)/i18n-js.cc',
],
'include_dirs': [
'<(icu_src_dir)/public/common',
@@ -48,8 +53,38 @@
],
'dependencies': [
'<(icu_src_dir)/icu.gyp:*',
+ 'js2c_i18n#host',
'../../../tools/gyp/v8.gyp:v8',
],
},
+ {
+ 'target_name': 'js2c_i18n',
+ 'type': 'none',
+ 'toolsets': ['host'],
+ 'variables': {
+ 'library_files': [
+ 'i18n.js'
+ ],
+ },
+ 'actions': [
+ {
+ 'action_name': 'js2c_i18n',
+ 'inputs': [
+ '../../../tools/js2c.py',
+ '<@(library_files)',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/i18n-js.cc',
+ ],
+ 'action': [
+ 'python',
+ '../../../tools/js2c.py',
+ '<@(_outputs)',
+ 'I18N',
+ '<@(library_files)'
+ ],
+ },
+ ],
+ },
], # targets
}
diff --git a/src/extensions/experimental/i18n-extension.cc b/src/extensions/experimental/i18n-extension.cc
index e65fdcc..56bea23 100644
--- a/src/extensions/experimental/i18n-extension.cc
+++ b/src/extensions/experimental/i18n-extension.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -27,249 +27,57 @@
#include "i18n-extension.h"
-#include <algorithm>
-#include <string>
-
#include "break-iterator.h"
-#include "unicode/locid.h"
-#include "unicode/uloc.h"
+#include "collator.h"
+#include "i18n-locale.h"
+#include "natives.h"
namespace v8 {
namespace internal {
I18NExtension* I18NExtension::extension_ = NULL;
-// TODO(cira): maybe move JS code to a .js file and generata cc files from it?
-// TODO(cira): Remove v8 prefix from v8Locale once we have stable API.
-const char* const I18NExtension::kSource =
- "v8Locale = function(optLocale) {"
- " native function NativeJSLocale();"
- " var properties = NativeJSLocale(optLocale);"
- " this.locale = properties.locale;"
- " this.language = properties.language;"
- " this.script = properties.script;"
- " this.region = properties.region;"
- "};"
- "v8Locale.availableLocales = function() {"
- " native function NativeJSAvailableLocales();"
- " return NativeJSAvailableLocales();"
- "};"
- "v8Locale.prototype.maximizedLocale = function() {"
- " native function NativeJSMaximizedLocale();"
- " return new v8Locale(NativeJSMaximizedLocale(this.locale));"
- "};"
- "v8Locale.prototype.minimizedLocale = function() {"
- " native function NativeJSMinimizedLocale();"
- " return new v8Locale(NativeJSMinimizedLocale(this.locale));"
- "};"
- "v8Locale.prototype.displayLocale_ = function(displayLocale) {"
- " var result = this.locale;"
- " if (displayLocale !== undefined) {"
- " result = displayLocale.locale;"
- " }"
- " return result;"
- "};"
- "v8Locale.prototype.displayLanguage = function(optDisplayLocale) {"
- " var displayLocale = this.displayLocale_(optDisplayLocale);"
- " native function NativeJSDisplayLanguage();"
- " return NativeJSDisplayLanguage(this.locale, displayLocale);"
- "};"
- "v8Locale.prototype.displayScript = function(optDisplayLocale) {"
- " var displayLocale = this.displayLocale_(optDisplayLocale);"
- " native function NativeJSDisplayScript();"
- " return NativeJSDisplayScript(this.locale, displayLocale);"
- "};"
- "v8Locale.prototype.displayRegion = function(optDisplayLocale) {"
- " var displayLocale = this.displayLocale_(optDisplayLocale);"
- " native function NativeJSDisplayRegion();"
- " return NativeJSDisplayRegion(this.locale, displayLocale);"
- "};"
- "v8Locale.prototype.displayName = function(optDisplayLocale) {"
- " var displayLocale = this.displayLocale_(optDisplayLocale);"
- " native function NativeJSDisplayName();"
- " return NativeJSDisplayName(this.locale, displayLocale);"
- "};"
- "v8Locale.v8BreakIterator = function(locale, type) {"
- " native function NativeJSBreakIterator();"
- " var iterator = NativeJSBreakIterator(locale, type);"
- " iterator.type = type;"
- " return iterator;"
- "};"
- "v8Locale.v8BreakIterator.BreakType = {"
- " 'unknown': -1,"
- " 'none': 0,"
- " 'number': 100,"
- " 'word': 200,"
- " 'kana': 300,"
- " 'ideo': 400"
- "};"
- "v8Locale.prototype.v8CreateBreakIterator = function(type) {"
- " return new v8Locale.v8BreakIterator(this.locale, type);"
- "};";
+// Returns a pointer to static string containing the actual
+// JavaScript code generated from i18n.js file.
+static const char* GetScriptSource() {
+ int index = NativesCollection<I18N>::GetIndex("i18n");
+ Vector<const char> script_data =
+ NativesCollection<I18N>::GetScriptSource(index);
+
+ return script_data.start();
+}
+
+I18NExtension::I18NExtension()
+ : v8::Extension("v8/i18n", GetScriptSource()) {
+}
v8::Handle<v8::FunctionTemplate> I18NExtension::GetNativeFunction(
v8::Handle<v8::String> name) {
if (name->Equals(v8::String::New("NativeJSLocale"))) {
- return v8::FunctionTemplate::New(JSLocale);
+ return v8::FunctionTemplate::New(I18NLocale::JSLocale);
} else if (name->Equals(v8::String::New("NativeJSAvailableLocales"))) {
- return v8::FunctionTemplate::New(JSAvailableLocales);
+ return v8::FunctionTemplate::New(I18NLocale::JSAvailableLocales);
} else if (name->Equals(v8::String::New("NativeJSMaximizedLocale"))) {
- return v8::FunctionTemplate::New(JSMaximizedLocale);
+ return v8::FunctionTemplate::New(I18NLocale::JSMaximizedLocale);
} else if (name->Equals(v8::String::New("NativeJSMinimizedLocale"))) {
- return v8::FunctionTemplate::New(JSMinimizedLocale);
+ return v8::FunctionTemplate::New(I18NLocale::JSMinimizedLocale);
} else if (name->Equals(v8::String::New("NativeJSDisplayLanguage"))) {
- return v8::FunctionTemplate::New(JSDisplayLanguage);
+ return v8::FunctionTemplate::New(I18NLocale::JSDisplayLanguage);
} else if (name->Equals(v8::String::New("NativeJSDisplayScript"))) {
- return v8::FunctionTemplate::New(JSDisplayScript);
+ return v8::FunctionTemplate::New(I18NLocale::JSDisplayScript);
} else if (name->Equals(v8::String::New("NativeJSDisplayRegion"))) {
- return v8::FunctionTemplate::New(JSDisplayRegion);
+ return v8::FunctionTemplate::New(I18NLocale::JSDisplayRegion);
} else if (name->Equals(v8::String::New("NativeJSDisplayName"))) {
- return v8::FunctionTemplate::New(JSDisplayName);
+ return v8::FunctionTemplate::New(I18NLocale::JSDisplayName);
} else if (name->Equals(v8::String::New("NativeJSBreakIterator"))) {
return v8::FunctionTemplate::New(BreakIterator::JSBreakIterator);
+ } else if (name->Equals(v8::String::New("NativeJSCollator"))) {
+ return v8::FunctionTemplate::New(Collator::JSCollator);
}
return v8::Handle<v8::FunctionTemplate>();
}
-v8::Handle<v8::Value> I18NExtension::JSLocale(const v8::Arguments& args) {
- // TODO(cira): Fetch browser locale. Accept en-US as good default for now.
- // We could possibly pass browser locale as a parameter in the constructor.
- std::string locale_name("en-US");
- if (args.Length() == 1 && args[0]->IsString()) {
- locale_name = *v8::String::Utf8Value(args[0]->ToString());
- }
-
- v8::Local<v8::Object> locale = v8::Object::New();
- locale->Set(v8::String::New("locale"), v8::String::New(locale_name.c_str()));
-
- icu::Locale icu_locale(locale_name.c_str());
-
- const char* language = icu_locale.getLanguage();
- locale->Set(v8::String::New("language"), v8::String::New(language));
-
- const char* script = icu_locale.getScript();
- if (strlen(script)) {
- locale->Set(v8::String::New("script"), v8::String::New(script));
- }
-
- const char* region = icu_locale.getCountry();
- if (strlen(region)) {
- locale->Set(v8::String::New("region"), v8::String::New(region));
- }
-
- return locale;
-}
-
-// TODO(cira): Filter out locales that Chrome doesn't support.
-v8::Handle<v8::Value> I18NExtension::JSAvailableLocales(
- const v8::Arguments& args) {
- v8::Local<v8::Array> all_locales = v8::Array::New();
-
- int count = 0;
- const Locale* icu_locales = icu::Locale::getAvailableLocales(count);
- for (int i = 0; i < count; ++i) {
- all_locales->Set(i, v8::String::New(icu_locales[i].getName()));
- }
-
- return all_locales;
-}
-
-// Use - as tag separator, not _ that ICU uses.
-static std::string NormalizeLocale(const std::string& locale) {
- std::string result(locale);
- // TODO(cira): remove STL dependency.
- std::replace(result.begin(), result.end(), '_', '-');
- return result;
-}
-
-v8::Handle<v8::Value> I18NExtension::JSMaximizedLocale(
- const v8::Arguments& args) {
- if (!args.Length() || !args[0]->IsString()) {
- return v8::Undefined();
- }
-
- UErrorCode status = U_ZERO_ERROR;
- std::string locale_name = *v8::String::Utf8Value(args[0]->ToString());
- char max_locale[ULOC_FULLNAME_CAPACITY];
- uloc_addLikelySubtags(locale_name.c_str(), max_locale,
- sizeof(max_locale), &status);
- if (U_FAILURE(status)) {
- return v8::Undefined();
- }
-
- return v8::String::New(NormalizeLocale(max_locale).c_str());
-}
-
-v8::Handle<v8::Value> I18NExtension::JSMinimizedLocale(
- const v8::Arguments& args) {
- if (!args.Length() || !args[0]->IsString()) {
- return v8::Undefined();
- }
-
- UErrorCode status = U_ZERO_ERROR;
- std::string locale_name = *v8::String::Utf8Value(args[0]->ToString());
- char min_locale[ULOC_FULLNAME_CAPACITY];
- uloc_minimizeSubtags(locale_name.c_str(), min_locale,
- sizeof(min_locale), &status);
- if (U_FAILURE(status)) {
- return v8::Undefined();
- }
-
- return v8::String::New(NormalizeLocale(min_locale).c_str());
-}
-
-// Common code for JSDisplayXXX methods.
-static v8::Handle<v8::Value> GetDisplayItem(const v8::Arguments& args,
- const std::string& item) {
- if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsString()) {
- return v8::Undefined();
- }
-
- std::string base_locale = *v8::String::Utf8Value(args[0]->ToString());
- icu::Locale icu_locale(base_locale.c_str());
- icu::Locale display_locale =
- icu::Locale(*v8::String::Utf8Value(args[1]->ToString()));
- UnicodeString result;
- if (item == "language") {
- icu_locale.getDisplayLanguage(display_locale, result);
- } else if (item == "script") {
- icu_locale.getDisplayScript(display_locale, result);
- } else if (item == "region") {
- icu_locale.getDisplayCountry(display_locale, result);
- } else if (item == "name") {
- icu_locale.getDisplayName(display_locale, result);
- } else {
- return v8::Undefined();
- }
-
- if (result.length()) {
- return v8::String::New(
- reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length());
- }
-
- return v8::Undefined();
-}
-
-v8::Handle<v8::Value> I18NExtension::JSDisplayLanguage(
- const v8::Arguments& args) {
- return GetDisplayItem(args, "language");
-}
-
-v8::Handle<v8::Value> I18NExtension::JSDisplayScript(
- const v8::Arguments& args) {
- return GetDisplayItem(args, "script");
-}
-
-v8::Handle<v8::Value> I18NExtension::JSDisplayRegion(
- const v8::Arguments& args) {
- return GetDisplayItem(args, "region");
-}
-
-v8::Handle<v8::Value> I18NExtension::JSDisplayName(const v8::Arguments& args) {
- return GetDisplayItem(args, "name");
-}
-
I18NExtension* I18NExtension::get() {
if (!extension_) {
extension_ = new I18NExtension();
diff --git a/src/extensions/experimental/i18n-extension.h b/src/extensions/experimental/i18n-extension.h
index 629332b..b4dc7c3 100644
--- a/src/extensions/experimental/i18n-extension.h
+++ b/src/extensions/experimental/i18n-extension.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -36,26 +36,16 @@
class I18NExtension : public v8::Extension {
public:
- I18NExtension() : v8::Extension("v8/i18n", kSource) {}
+ I18NExtension();
+
virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
v8::Handle<v8::String> name);
- // Implementations of window.Locale methods.
- static v8::Handle<v8::Value> JSLocale(const v8::Arguments& args);
- static v8::Handle<v8::Value> JSAvailableLocales(const v8::Arguments& args);
- static v8::Handle<v8::Value> JSMaximizedLocale(const v8::Arguments& args);
- static v8::Handle<v8::Value> JSMinimizedLocale(const v8::Arguments& args);
- static v8::Handle<v8::Value> JSDisplayLanguage(const v8::Arguments& args);
- static v8::Handle<v8::Value> JSDisplayScript(const v8::Arguments& args);
- static v8::Handle<v8::Value> JSDisplayRegion(const v8::Arguments& args);
- static v8::Handle<v8::Value> JSDisplayName(const v8::Arguments& args);
-
// V8 code prefers Register, while Chrome and WebKit use get kind of methods.
static void Register();
static I18NExtension* get();
private:
- static const char* const kSource;
static I18NExtension* extension_;
};
diff --git a/src/extensions/experimental/i18n-locale.cc b/src/extensions/experimental/i18n-locale.cc
new file mode 100644
index 0000000..e5e1cf8
--- /dev/null
+++ b/src/extensions/experimental/i18n-locale.cc
@@ -0,0 +1,172 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "i18n-locale.h"
+
+#include <algorithm>
+#include <string>
+
+#include "unicode/locid.h"
+#include "unicode/uloc.h"
+
+namespace v8 {
+namespace internal {
+
+v8::Handle<v8::Value> I18NLocale::JSLocale(const v8::Arguments& args) {
+ // TODO(cira): Fetch browser locale. Accept en-US as good default for now.
+ // We could possibly pass browser locale as a parameter in the constructor.
+ std::string locale_name("en-US");
+ if (args.Length() == 1 && args[0]->IsString()) {
+ locale_name = *v8::String::Utf8Value(args[0]->ToString());
+ }
+
+ v8::Local<v8::Object> locale = v8::Object::New();
+ locale->Set(v8::String::New("locale"), v8::String::New(locale_name.c_str()));
+
+ icu::Locale icu_locale(locale_name.c_str());
+
+ const char* language = icu_locale.getLanguage();
+ locale->Set(v8::String::New("language"), v8::String::New(language));
+
+ const char* script = icu_locale.getScript();
+ if (strlen(script)) {
+ locale->Set(v8::String::New("script"), v8::String::New(script));
+ }
+
+ const char* region = icu_locale.getCountry();
+ if (strlen(region)) {
+ locale->Set(v8::String::New("region"), v8::String::New(region));
+ }
+
+ return locale;
+}
+
+// TODO(cira): Filter out locales that Chrome doesn't support.
+v8::Handle<v8::Value> I18NLocale::JSAvailableLocales(
+ const v8::Arguments& args) {
+ v8::Local<v8::Array> all_locales = v8::Array::New();
+
+ int count = 0;
+ const icu::Locale* icu_locales = icu::Locale::getAvailableLocales(count);
+ for (int i = 0; i < count; ++i) {
+ all_locales->Set(i, v8::String::New(icu_locales[i].getName()));
+ }
+
+ return all_locales;
+}
+
+// Use - as tag separator, not _ that ICU uses.
+static std::string NormalizeLocale(const std::string& locale) {
+ std::string result(locale);
+ // TODO(cira): remove STL dependency.
+ std::replace(result.begin(), result.end(), '_', '-');
+ return result;
+}
+
+v8::Handle<v8::Value> I18NLocale::JSMaximizedLocale(const v8::Arguments& args) {
+ if (!args.Length() || !args[0]->IsString()) {
+ return v8::Undefined();
+ }
+
+ UErrorCode status = U_ZERO_ERROR;
+ std::string locale_name = *v8::String::Utf8Value(args[0]->ToString());
+ char max_locale[ULOC_FULLNAME_CAPACITY];
+ uloc_addLikelySubtags(locale_name.c_str(), max_locale,
+ sizeof(max_locale), &status);
+ if (U_FAILURE(status)) {
+ return v8::Undefined();
+ }
+
+ return v8::String::New(NormalizeLocale(max_locale).c_str());
+}
+
+v8::Handle<v8::Value> I18NLocale::JSMinimizedLocale(const v8::Arguments& args) {
+ if (!args.Length() || !args[0]->IsString()) {
+ return v8::Undefined();
+ }
+
+ UErrorCode status = U_ZERO_ERROR;
+ std::string locale_name = *v8::String::Utf8Value(args[0]->ToString());
+ char min_locale[ULOC_FULLNAME_CAPACITY];
+ uloc_minimizeSubtags(locale_name.c_str(), min_locale,
+ sizeof(min_locale), &status);
+ if (U_FAILURE(status)) {
+ return v8::Undefined();
+ }
+
+ return v8::String::New(NormalizeLocale(min_locale).c_str());
+}
+
+// Common code for JSDisplayXXX methods.
+static v8::Handle<v8::Value> GetDisplayItem(const v8::Arguments& args,
+ const std::string& item) {
+ if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsString()) {
+ return v8::Undefined();
+ }
+
+ std::string base_locale = *v8::String::Utf8Value(args[0]->ToString());
+ icu::Locale icu_locale(base_locale.c_str());
+ icu::Locale display_locale =
+ icu::Locale(*v8::String::Utf8Value(args[1]->ToString()));
+ icu::UnicodeString result;
+ if (item == "language") {
+ icu_locale.getDisplayLanguage(display_locale, result);
+ } else if (item == "script") {
+ icu_locale.getDisplayScript(display_locale, result);
+ } else if (item == "region") {
+ icu_locale.getDisplayCountry(display_locale, result);
+ } else if (item == "name") {
+ icu_locale.getDisplayName(display_locale, result);
+ } else {
+ return v8::Undefined();
+ }
+
+ if (result.length()) {
+ return v8::String::New(
+ reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length());
+ }
+
+ return v8::Undefined();
+}
+
+v8::Handle<v8::Value> I18NLocale::JSDisplayLanguage(const v8::Arguments& args) {
+ return GetDisplayItem(args, "language");
+}
+
+v8::Handle<v8::Value> I18NLocale::JSDisplayScript(const v8::Arguments& args) {
+ return GetDisplayItem(args, "script");
+}
+
+v8::Handle<v8::Value> I18NLocale::JSDisplayRegion(const v8::Arguments& args) {
+ return GetDisplayItem(args, "region");
+}
+
+v8::Handle<v8::Value> I18NLocale::JSDisplayName(const v8::Arguments& args) {
+ return GetDisplayItem(args, "name");
+}
+
+} } // namespace v8::internal
diff --git a/src/arm/register-allocator-arm.h b/src/extensions/experimental/i18n-locale.h
similarity index 63%
copy from src/arm/register-allocator-arm.h
copy to src/extensions/experimental/i18n-locale.h
index fdbc88f..aa9adbe 100644
--- a/src/arm/register-allocator-arm.h
+++ b/src/extensions/experimental/i18n-locale.h
@@ -1,4 +1,4 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -25,20 +25,29 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#ifndef V8_ARM_REGISTER_ALLOCATOR_ARM_H_
-#define V8_ARM_REGISTER_ALLOCATOR_ARM_H_
+#ifndef V8_EXTENSIONS_EXPERIMENTAL_I18N_LOCALE_H_
+#define V8_EXTENSIONS_EXPERIMENTAL_I18N_LOCALE_H_
+
+#include <v8.h>
namespace v8 {
namespace internal {
-class RegisterAllocatorConstants : public AllStatic {
+class I18NLocale {
public:
- // No registers are currently managed by the register allocator on ARM.
- static const int kNumRegisters = 0;
- static const int kInvalidRegister = -1;
-};
+ I18NLocale() {}
+ // Implementations of window.Locale methods.
+ static v8::Handle<v8::Value> JSLocale(const v8::Arguments& args);
+ static v8::Handle<v8::Value> JSAvailableLocales(const v8::Arguments& args);
+ static v8::Handle<v8::Value> JSMaximizedLocale(const v8::Arguments& args);
+ static v8::Handle<v8::Value> JSMinimizedLocale(const v8::Arguments& args);
+ static v8::Handle<v8::Value> JSDisplayLanguage(const v8::Arguments& args);
+ static v8::Handle<v8::Value> JSDisplayScript(const v8::Arguments& args);
+ static v8::Handle<v8::Value> JSDisplayRegion(const v8::Arguments& args);
+ static v8::Handle<v8::Value> JSDisplayName(const v8::Arguments& args);
+};
} } // namespace v8::internal
-#endif // V8_ARM_REGISTER_ALLOCATOR_ARM_H_
+#endif // V8_EXTENSIONS_EXPERIMENTAL_I18N_LOCALE_H_
diff --git a/src/extensions/experimental/i18n.js b/src/extensions/experimental/i18n.js
new file mode 100644
index 0000000..5a74905
--- /dev/null
+++ b/src/extensions/experimental/i18n.js
@@ -0,0 +1,116 @@
+// Copyright 2006-2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// TODO(cira): Remove v8 prefix from v8Locale once we have stable API.
+v8Locale = function(optLocale) {
+ native function NativeJSLocale();
+ var properties = NativeJSLocale(optLocale);
+ this.locale = properties.locale;
+ this.language = properties.language;
+ this.script = properties.script;
+ this.region = properties.region;
+};
+
+v8Locale.availableLocales = function() {
+ native function NativeJSAvailableLocales();
+ return NativeJSAvailableLocales();
+};
+
+v8Locale.prototype.maximizedLocale = function() {
+ native function NativeJSMaximizedLocale();
+ return new v8Locale(NativeJSMaximizedLocale(this.locale));
+};
+
+v8Locale.prototype.minimizedLocale = function() {
+ native function NativeJSMinimizedLocale();
+ return new v8Locale(NativeJSMinimizedLocale(this.locale));
+};
+
+v8Locale.prototype.displayLocale_ = function(displayLocale) {
+ var result = this.locale;
+ if (displayLocale !== undefined) {
+ result = displayLocale.locale;
+ }
+ return result;
+};
+
+v8Locale.prototype.displayLanguage = function(optDisplayLocale) {
+ var displayLocale = this.displayLocale_(optDisplayLocale);
+ native function NativeJSDisplayLanguage();
+ return NativeJSDisplayLanguage(this.locale, displayLocale);
+};
+
+v8Locale.prototype.displayScript = function(optDisplayLocale) {
+ var displayLocale = this.displayLocale_(optDisplayLocale);
+ native function NativeJSDisplayScript();
+ return NativeJSDisplayScript(this.locale, displayLocale);
+};
+
+v8Locale.prototype.displayRegion = function(optDisplayLocale) {
+ var displayLocale = this.displayLocale_(optDisplayLocale);
+ native function NativeJSDisplayRegion();
+ return NativeJSDisplayRegion(this.locale, displayLocale);
+};
+
+v8Locale.prototype.displayName = function(optDisplayLocale) {
+ var displayLocale = this.displayLocale_(optDisplayLocale);
+ native function NativeJSDisplayName();
+ return NativeJSDisplayName(this.locale, displayLocale);
+};
+
+v8Locale.v8BreakIterator = function(locale, type) {
+ native function NativeJSBreakIterator();
+ var iterator = NativeJSBreakIterator(locale, type);
+ iterator.type = type;
+ return iterator;
+};
+
+v8Locale.v8BreakIterator.BreakType = {
+ 'unknown': -1,
+ 'none': 0,
+ 'number': 100,
+ 'word': 200,
+ 'kana': 300,
+ 'ideo': 400
+};
+
+v8Locale.prototype.v8CreateBreakIterator = function(type) {
+ return new v8Locale.v8BreakIterator(this.locale, type);
+};
+
+// TODO(jungshik): Set |collator.options| to actually recognized / resolved
+// values.
+v8Locale.Collator = function(locale, options) {
+ native function NativeJSCollator();
+ var collator = NativeJSCollator(locale,
+ options === undefined ? {} : options);
+ return collator;
+};
+
+v8Locale.prototype.createCollator = function(options) {
+ return new v8Locale.Collator(this.locale, options);
+};
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index 0bc6409..69139bb 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -96,6 +96,9 @@
//
#define FLAG FLAG_FULL
+// Flags for experimental language features.
+DEFINE_bool(harmony_proxies, false, "enable harmony proxies")
+
// Flags for Crankshaft.
#ifdef V8_TARGET_ARCH_MIPS
DEFINE_bool(crankshaft, false, "use crankshaft")
@@ -162,7 +165,8 @@
DEFINE_bool(enable_sahf, true,
"enable use of SAHF instruction if available (X64 only)")
DEFINE_bool(enable_vfp3, true,
- "enable use of VFP3 instructions if available (ARM only)")
+ "enable use of VFP3 instructions if available - this implies "
+ "enabling ARMv7 instructions (ARM only)")
DEFINE_bool(enable_armv7, true,
"enable use of ARMv7 instructions if available (ARM only)")
DEFINE_bool(enable_fpu, true,
diff --git a/src/frames-inl.h b/src/frames-inl.h
index e6eaec0..5951806 100644
--- a/src/frames-inl.h
+++ b/src/frames-inl.h
@@ -88,6 +88,11 @@
}
+inline StackFrame::StackFrame(StackFrameIterator* iterator)
+ : iterator_(iterator), isolate_(iterator_->isolate()) {
+}
+
+
inline StackHandler* StackFrame::top_handler() const {
return iterator_->handler();
}
@@ -143,15 +148,26 @@
}
+Address JavaScriptFrame::GetParameterSlot(int index) const {
+ int param_count = ComputeParametersCount();
+ ASSERT(-1 <= index && index < param_count);
+ int parameter_offset = (param_count - index - 1) * kPointerSize;
+ return caller_sp() + parameter_offset;
+}
+
+
+Object* JavaScriptFrame::GetParameter(int index) const {
+ return Memory::Object_at(GetParameterSlot(index));
+}
+
+
inline Object* JavaScriptFrame::receiver() const {
- const int offset = JavaScriptFrameConstants::kReceiverOffset;
- return Memory::Object_at(caller_sp() + offset);
+ return GetParameter(-1);
}
inline void JavaScriptFrame::set_receiver(Object* value) {
- const int offset = JavaScriptFrameConstants::kReceiverOffset;
- Memory::Object_at(caller_sp() + offset) = value;
+ Memory::Object_at(GetParameterSlot(-1)) = value;
}
@@ -168,6 +184,13 @@
template<typename Iterator>
+inline JavaScriptFrameIteratorTemp<Iterator>::JavaScriptFrameIteratorTemp(
+ Isolate* isolate)
+ : iterator_(isolate) {
+ if (!done()) Advance();
+}
+
+template<typename Iterator>
inline JavaScriptFrame* JavaScriptFrameIteratorTemp<Iterator>::frame() const {
// TODO(1233797): The frame hierarchy needs to change. It's
// problematic that we can't use the safe-cast operator to cast to
@@ -181,11 +204,9 @@
template<typename Iterator>
JavaScriptFrameIteratorTemp<Iterator>::JavaScriptFrameIteratorTemp(
- StackFrame::Id id) {
- while (!done()) {
- Advance();
- if (frame()->id() == id) return;
- }
+ Isolate* isolate, StackFrame::Id id)
+ : iterator_(isolate) {
+ AdvanceToId(id);
}
@@ -206,6 +227,15 @@
template<typename Iterator>
+void JavaScriptFrameIteratorTemp<Iterator>::AdvanceToId(StackFrame::Id id) {
+ while (!done()) {
+ Advance();
+ if (frame()->id() == id) return;
+ }
+}
+
+
+template<typename Iterator>
void JavaScriptFrameIteratorTemp<Iterator>::Reset() {
iterator_.Reset();
if (!done()) Advance();
diff --git a/src/frames.cc b/src/frames.cc
index 79aa250..e0517c8 100644
--- a/src/frames.cc
+++ b/src/frames.cc
@@ -39,9 +39,6 @@
namespace v8 {
namespace internal {
-
-int SafeStackFrameIterator::active_count_ = 0;
-
// Iterator that supports traversing the stack handlers of a
// particular frame. Needs to know the top of the handler chain.
class StackHandlerIterator BASE_EMBEDDED {
@@ -73,23 +70,34 @@
#define INITIALIZE_SINGLETON(type, field) field##_(this),
StackFrameIterator::StackFrameIterator()
- : STACK_FRAME_TYPE_LIST(INITIALIZE_SINGLETON)
+ : isolate_(Isolate::Current()),
+ STACK_FRAME_TYPE_LIST(INITIALIZE_SINGLETON)
frame_(NULL), handler_(NULL),
- thread_(Isolate::Current()->thread_local_top()),
+ thread_(isolate_->thread_local_top()),
fp_(NULL), sp_(NULL), advance_(&StackFrameIterator::AdvanceWithHandler) {
Reset();
}
-StackFrameIterator::StackFrameIterator(ThreadLocalTop* t)
- : STACK_FRAME_TYPE_LIST(INITIALIZE_SINGLETON)
+StackFrameIterator::StackFrameIterator(Isolate* isolate)
+ : isolate_(isolate),
+ STACK_FRAME_TYPE_LIST(INITIALIZE_SINGLETON)
+ frame_(NULL), handler_(NULL),
+ thread_(isolate_->thread_local_top()),
+ fp_(NULL), sp_(NULL), advance_(&StackFrameIterator::AdvanceWithHandler) {
+ Reset();
+}
+StackFrameIterator::StackFrameIterator(Isolate* isolate, ThreadLocalTop* t)
+ : isolate_(isolate),
+ STACK_FRAME_TYPE_LIST(INITIALIZE_SINGLETON)
frame_(NULL), handler_(NULL), thread_(t),
fp_(NULL), sp_(NULL), advance_(&StackFrameIterator::AdvanceWithHandler) {
Reset();
}
StackFrameIterator::StackFrameIterator(Isolate* isolate,
bool use_top, Address fp, Address sp)
- : STACK_FRAME_TYPE_LIST(INITIALIZE_SINGLETON)
+ : isolate_(isolate),
+ STACK_FRAME_TYPE_LIST(INITIALIZE_SINGLETON)
frame_(NULL), handler_(NULL),
- thread_(use_top ? isolate->thread_local_top() : NULL),
+ thread_(use_top ? isolate_->thread_local_top() : NULL),
fp_(use_top ? NULL : fp), sp_(sp),
advance_(use_top ? &StackFrameIterator::AdvanceWithHandler :
&StackFrameIterator::AdvanceWithoutHandler) {
@@ -147,7 +155,7 @@
state.sp = sp_;
state.pc_address =
reinterpret_cast<Address*>(StandardFrame::ComputePCAddress(fp_));
- type = StackFrame::ComputeType(&state);
+ type = StackFrame::ComputeType(isolate(), &state);
}
if (SingletonFor(type) == NULL) return;
frame_ = SingletonFor(type, &state);
@@ -188,6 +196,12 @@
}
+StackTraceFrameIterator::StackTraceFrameIterator(Isolate* isolate)
+ : JavaScriptFrameIterator(isolate) {
+ if (!done() && !IsValidFrame()) Advance();
+}
+
+
void StackTraceFrameIterator::Advance() {
while (true) {
JavaScriptFrameIterator::Advance();
@@ -221,10 +235,24 @@
}
+SafeStackFrameIterator::ActiveCountMaintainer::ActiveCountMaintainer(
+ Isolate* isolate)
+ : isolate_(isolate) {
+ isolate_->set_safe_stack_iterator_counter(
+ isolate_->safe_stack_iterator_counter() + 1);
+}
+
+
+SafeStackFrameIterator::ActiveCountMaintainer::~ActiveCountMaintainer() {
+ isolate_->set_safe_stack_iterator_counter(
+ isolate_->safe_stack_iterator_counter() - 1);
+}
+
+
SafeStackFrameIterator::SafeStackFrameIterator(
Isolate* isolate,
Address fp, Address sp, Address low_bound, Address high_bound) :
- maintainer_(),
+ maintainer_(isolate),
stack_validator_(low_bound, high_bound),
is_valid_top_(IsValidTop(isolate, low_bound, high_bound)),
is_valid_fp_(IsWithinBounds(low_bound, high_bound, fp)),
@@ -233,6 +261,10 @@
iterator_(isolate, is_valid_top_, is_valid_fp_ ? fp : NULL, sp) {
}
+bool SafeStackFrameIterator::is_active(Isolate* isolate) {
+ return isolate->safe_stack_iterator_counter() > 0;
+}
+
bool SafeStackFrameIterator::IsValidTop(Isolate* isolate,
Address low_bound, Address high_bound) {
@@ -333,10 +365,10 @@
#endif
-Code* StackFrame::GetSafepointData(Address pc,
+Code* StackFrame::GetSafepointData(Isolate* isolate,
+ Address pc,
SafepointEntry* safepoint_entry,
unsigned* stack_slots) {
- Isolate* isolate = Isolate::Current();
PcToCodeCache::PcToCodeCacheEntry* entry =
isolate->pc_to_code_cache()->GetCacheEntry(pc);
SafepointEntry cached_safepoint_entry = entry->safepoint_entry;
@@ -377,7 +409,7 @@
}
-StackFrame::Type StackFrame::ComputeType(State* state) {
+StackFrame::Type StackFrame::ComputeType(Isolate* isolate, State* state) {
ASSERT(state->fp != NULL);
if (StandardFrame::IsArgumentsAdaptorFrame(state->fp)) {
return ARGUMENTS_ADAPTOR;
@@ -392,9 +424,8 @@
// frames as normal JavaScript frames to avoid having to look
// into the heap to determine the state. This is safe as long
// as nobody tries to GC...
- if (SafeStackFrameIterator::is_active()) return JAVA_SCRIPT;
- Code::Kind kind = GetContainingCode(Isolate::Current(),
- *(state->pc_address))->kind();
+ if (SafeStackFrameIterator::is_active(isolate)) return JAVA_SCRIPT;
+ Code::Kind kind = GetContainingCode(isolate, *(state->pc_address))->kind();
ASSERT(kind == Code::FUNCTION || kind == Code::OPTIMIZED_FUNCTION);
return (kind == Code::OPTIMIZED_FUNCTION) ? OPTIMIZED : JAVA_SCRIPT;
}
@@ -405,7 +436,7 @@
StackFrame::Type StackFrame::GetCallerState(State* state) const {
ComputeCallerState(state);
- return ComputeType(state);
+ return ComputeType(isolate(), state);
}
@@ -465,7 +496,7 @@
void ExitFrame::Iterate(ObjectVisitor* v) const {
// The arguments are traversed as part of the expression stack of
// the calling frame.
- IteratePc(v, pc_address(), LookupCode(Isolate::Current()));
+ IteratePc(v, pc_address(), LookupCode());
v->VisitPointer(&code_slot());
}
@@ -539,18 +570,16 @@
// Make sure that we're not doing "safe" stack frame iteration. We cannot
// possibly find pointers in optimized frames in that state.
- ASSERT(!SafeStackFrameIterator::is_active());
+ ASSERT(!SafeStackFrameIterator::is_active(isolate()));
// Compute the safepoint information.
unsigned stack_slots = 0;
SafepointEntry safepoint_entry;
Code* code = StackFrame::GetSafepointData(
- pc(), &safepoint_entry, &stack_slots);
+ isolate(), pc(), &safepoint_entry, &stack_slots);
unsigned slot_space = stack_slots * kPointerSize;
- // Visit the outgoing parameters. This is usually dealt with by the
- // callee, but while GC'ing we artificially lower the number of
- // arguments to zero and let the caller deal with it.
+ // Visit the outgoing parameters.
Object** parameters_base = &Memory::Object_at(sp());
Object** parameters_limit = &Memory::Object_at(
fp() + JavaScriptFrameConstants::kFunctionOffset - slot_space);
@@ -604,21 +633,6 @@
// Visit the return address in the callee and incoming arguments.
IteratePc(v, pc_address(), code);
- IterateArguments(v);
-}
-
-
-Object* JavaScriptFrame::GetParameter(int index) const {
- ASSERT(index >= 0 && index < ComputeParametersCount());
- const int offset = JavaScriptFrameConstants::kParam0Offset;
- return Memory::Object_at(caller_sp() + offset - (index * kPointerSize));
-}
-
-
-int JavaScriptFrame::ComputeParametersCount() const {
- Address base = caller_sp() + JavaScriptFrameConstants::kReceiverOffset;
- Address limit = fp() + JavaScriptFrameConstants::kSavedRegistersOffset;
- return static_cast<int>((base - limit) / kPointerSize);
}
@@ -638,27 +652,17 @@
}
+int JavaScriptFrame::GetNumberOfIncomingArguments() const {
+ ASSERT(!SafeStackFrameIterator::is_active(isolate()) &&
+ isolate()->heap()->gc_state() == Heap::NOT_IN_GC);
+
+ JSFunction* function = JSFunction::cast(this->function());
+ return function->shared()->formal_parameter_count();
+}
+
+
Address JavaScriptFrame::GetCallerStackPointer() const {
- int arguments;
- if (SafeStackFrameIterator::is_active() ||
- HEAP->gc_state() != Heap::NOT_IN_GC) {
- // If the we are currently iterating the safe stack the
- // arguments for frames are traversed as if they were
- // expression stack elements of the calling frame. The reason for
- // this rather strange decision is that we cannot access the
- // function during mark-compact GCs when objects may have been marked.
- // In fact accessing heap objects (like function->shared() below)
- // at all during GC is problematic.
- arguments = 0;
- } else {
- // Compute the number of arguments by getting the number of formal
- // parameters of the function. We must remember to take the
- // receiver into account (+1).
- JSFunction* function = JSFunction::cast(this->function());
- arguments = function->shared()->formal_parameter_count() + 1;
- }
- const int offset = StandardFrameConstants::kCallerSPOffset;
- return fp() + offset + (arguments * kPointerSize);
+ return fp() + StandardFrameConstants::kCallerSPOffset;
}
@@ -670,7 +674,7 @@
void JavaScriptFrame::Summarize(List<FrameSummary>* functions) {
ASSERT(functions->length() == 0);
- Code* code_pointer = LookupCode(Isolate::Current());
+ Code* code_pointer = LookupCode();
int offset = static_cast<int>(pc() - code_pointer->address());
FrameSummary summary(receiver(),
JSFunction::cast(function()),
@@ -789,7 +793,7 @@
// back to a slow search in this case to find the original optimized
// code object.
if (!code->contains(pc())) {
- code = Isolate::Current()->pc_to_code_cache()->GcSafeFindCodeForPc(pc());
+ code = isolate()->pc_to_code_cache()->GcSafeFindCodeForPc(pc());
}
ASSERT(code != NULL);
ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION);
@@ -836,9 +840,7 @@
Address ArgumentsAdaptorFrame::GetCallerStackPointer() const {
- const int arguments = Smi::cast(GetExpression(0))->value();
- const int offset = StandardFrameConstants::kCallerSPOffset;
- return fp() + offset + (arguments + 1) * kPointerSize;
+ return fp() + StandardFrameConstants::kCallerSPOffset;
}
@@ -850,7 +852,7 @@
Code* ArgumentsAdaptorFrame::unchecked_code() const {
- return Isolate::Current()->builtins()->builtin(
+ return isolate()->builtins()->builtin(
Builtins::kArgumentsAdaptorTrampoline);
}
@@ -1045,14 +1047,14 @@
ASSERT(!it.done());
StackHandler* handler = it.handler();
ASSERT(handler->is_entry());
- handler->Iterate(v, LookupCode(Isolate::Current()));
+ handler->Iterate(v, LookupCode());
#ifdef DEBUG
// Make sure that the entry frame does not contain more than one
// stack handler.
it.Advance();
ASSERT(it.done());
#endif
- IteratePc(v, pc_address(), LookupCode(Isolate::Current()));
+ IteratePc(v, pc_address(), LookupCode());
}
@@ -1069,7 +1071,7 @@
v->VisitPointers(base, reinterpret_cast<Object**>(address));
base = reinterpret_cast<Object**>(address + StackHandlerConstants::kSize);
// Traverse the pointers in the handler itself.
- handler->Iterate(v, LookupCode(Isolate::Current()));
+ handler->Iterate(v, LookupCode());
}
v->VisitPointers(base, limit);
}
@@ -1077,18 +1079,7 @@
void JavaScriptFrame::Iterate(ObjectVisitor* v) const {
IterateExpressions(v);
- IteratePc(v, pc_address(), LookupCode(Isolate::Current()));
- IterateArguments(v);
-}
-
-
-void JavaScriptFrame::IterateArguments(ObjectVisitor* v) const {
- // Traverse callee-saved registers, receiver, and parameters.
- const int kBaseOffset = JavaScriptFrameConstants::kSavedRegistersOffset;
- const int kLimitOffset = JavaScriptFrameConstants::kReceiverOffset;
- Object** base = &Memory::Object_at(fp() + kBaseOffset);
- Object** limit = &Memory::Object_at(caller_sp() + kLimitOffset) + 1;
- v->VisitPointers(base, limit);
+ IteratePc(v, pc_address(), LookupCode());
}
@@ -1096,7 +1087,7 @@
// Internal frames only have object pointers on the expression stack
// as they never have any arguments.
IterateExpressions(v);
- IteratePc(v, pc_address(), LookupCode(Isolate::Current()));
+ IteratePc(v, pc_address(), LookupCode());
}
diff --git a/src/frames.h b/src/frames.h
index bee95cc..da9009b 100644
--- a/src/frames.h
+++ b/src/frames.h
@@ -158,10 +158,12 @@
Address* pc_address;
};
- // Copy constructor; it breaks the connection to host iterator.
+ // Copy constructor; it breaks the connection to host iterator
+ // (as an iterator usually lives on stack).
StackFrame(const StackFrame& original) {
this->state_ = original.state_;
this->iterator_ = NULL;
+ this->isolate_ = original.isolate_;
}
// Type testers.
@@ -205,8 +207,8 @@
virtual Code* unchecked_code() const = 0;
// Get the code associated with this frame.
- Code* LookupCode(Isolate* isolate) const {
- return GetContainingCode(isolate, pc());
+ Code* LookupCode() const {
+ return GetContainingCode(isolate(), pc());
}
// Get the code object that contains the given pc.
@@ -215,7 +217,8 @@
// Get the code object containing the given pc and fill in the
// safepoint entry and the number of stack slots. The pc must be at
// a safepoint.
- static Code* GetSafepointData(Address pc,
+ static Code* GetSafepointData(Isolate* isolate,
+ Address pc,
SafepointEntry* safepoint_entry,
unsigned* stack_slots);
@@ -230,9 +233,11 @@
int index) const { }
protected:
- explicit StackFrame(StackFrameIterator* iterator) : iterator_(iterator) { }
+ inline explicit StackFrame(StackFrameIterator* iterator);
virtual ~StackFrame() { }
+ Isolate* isolate() const { return isolate_; }
+
// Compute the stack pointer for the calling frame.
virtual Address GetCallerStackPointer() const = 0;
@@ -245,10 +250,11 @@
inline StackHandler* top_handler() const;
// Compute the stack frame type for the given state.
- static Type ComputeType(State* state);
+ static Type ComputeType(Isolate* isolate, State* state);
private:
const StackFrameIterator* iterator_;
+ Isolate* isolate_;
State state_;
// Fill in the state of the calling frame.
@@ -257,6 +263,8 @@
// Get the type and the state of the calling frame.
virtual Type GetCallerState(State* state) const;
+ static const intptr_t kIsolateTag = 1;
+
friend class StackFrameIterator;
friend class StackHandlerIterator;
friend class SafeStackFrameIterator;
@@ -430,7 +438,7 @@
Handle<Object> receiver() { return receiver_; }
Handle<JSFunction> function() { return function_; }
Handle<Code> code() { return code_; }
- Address pc() { return reinterpret_cast<Address>(*code_) + offset_; }
+ Address pc() { return code_->address() + offset_; }
int offset() { return offset_; }
bool is_constructor() { return is_constructor_; }
@@ -455,8 +463,11 @@
inline void set_receiver(Object* value);
// Access the parameters.
- Object* GetParameter(int index) const;
- int ComputeParametersCount() const;
+ inline Address GetParameterSlot(int index) const;
+ inline Object* GetParameter(int index) const;
+ inline int ComputeParametersCount() const {
+ return GetNumberOfIncomingArguments();
+ }
// Check if this frame is a constructor frame invoked through 'new'.
bool IsConstructor() const;
@@ -494,6 +505,8 @@
virtual Address GetCallerStackPointer() const;
+ virtual int GetNumberOfIncomingArguments() const;
+
// Garbage collection support. Iterates over incoming arguments,
// receiver, and any callee-saved registers.
void IterateArguments(ObjectVisitor* v) const;
@@ -554,6 +567,10 @@
explicit ArgumentsAdaptorFrame(StackFrameIterator* iterator)
: JavaScriptFrame(iterator) { }
+ virtual int GetNumberOfIncomingArguments() const {
+ return Smi::cast(GetExpression(0))->value();
+ }
+
virtual Address GetCallerStackPointer() const;
private:
@@ -609,11 +626,15 @@
class StackFrameIterator BASE_EMBEDDED {
public:
- // An iterator that iterates over the current thread's stack.
+ // An iterator that iterates over the current thread's stack,
+ // and uses current isolate.
StackFrameIterator();
+ // An iterator that iterates over the isolate's current thread's stack,
+ explicit StackFrameIterator(Isolate* isolate);
+
// An iterator that iterates over a given thread's stack.
- explicit StackFrameIterator(ThreadLocalTop* thread);
+ StackFrameIterator(Isolate* isolate, ThreadLocalTop* t);
// An iterator that can start from a given FP address.
// If use_top, then work as usual, if fp isn't NULL, use it,
@@ -625,6 +646,8 @@
return frame_;
}
+ Isolate* isolate() const { return isolate_; }
+
bool done() const { return frame_ == NULL; }
void Advance() { (this->*advance_)(); }
@@ -632,6 +655,7 @@
void Reset();
private:
+ Isolate* isolate_;
#define DECLARE_SINGLETON(ignore, type) type type##_;
STACK_FRAME_TYPE_LIST(DECLARE_SINGLETON)
#undef DECLARE_SINGLETON
@@ -667,13 +691,12 @@
public:
JavaScriptFrameIteratorTemp() { if (!done()) Advance(); }
- explicit JavaScriptFrameIteratorTemp(ThreadLocalTop* thread) :
- iterator_(thread) {
- if (!done()) Advance();
- }
+ inline explicit JavaScriptFrameIteratorTemp(Isolate* isolate);
// Skip frames until the frame with the given id is reached.
- explicit JavaScriptFrameIteratorTemp(StackFrame::Id id);
+ explicit JavaScriptFrameIteratorTemp(StackFrame::Id id) { AdvanceToId(id); }
+
+ inline JavaScriptFrameIteratorTemp(Isolate* isolate, StackFrame::Id id);
JavaScriptFrameIteratorTemp(Address fp, Address sp,
Address low_bound, Address high_bound) :
@@ -702,6 +725,8 @@
void Reset();
private:
+ inline void AdvanceToId(StackFrame::Id id);
+
Iterator iterator_;
};
@@ -716,6 +741,7 @@
class StackTraceFrameIterator: public JavaScriptFrameIterator {
public:
StackTraceFrameIterator();
+ explicit StackTraceFrameIterator(Isolate* isolate);
void Advance();
private:
@@ -739,7 +765,7 @@
void Advance();
void Reset();
- static bool is_active() { return active_count_ > 0; }
+ static bool is_active(Isolate* isolate);
static bool IsWithinBounds(
Address low_bound, Address high_bound, Address addr) {
@@ -786,13 +812,13 @@
// heap objects.
class ActiveCountMaintainer BASE_EMBEDDED {
public:
- ActiveCountMaintainer() { active_count_++; }
- ~ActiveCountMaintainer() { active_count_--; }
+ explicit ActiveCountMaintainer(Isolate* isolate);
+ ~ActiveCountMaintainer();
+ private:
+ Isolate* isolate_;
};
ActiveCountMaintainer maintainer_;
- // TODO(isolates): this is dangerous.
- static int active_count_;
StackAddressValidator stack_validator_;
const bool is_valid_top_;
const bool is_valid_fp_;
diff --git a/src/full-codegen.cc b/src/full-codegen.cc
index d509cd5..d6ba56e 100644
--- a/src/full-codegen.cc
+++ b/src/full-codegen.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -27,7 +27,7 @@
#include "v8.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "compiler.h"
#include "debug.h"
#include "full-codegen.h"
@@ -213,12 +213,6 @@
}
-void BreakableStatementChecker::VisitIncrementOperation(
- IncrementOperation* expr) {
- UNREACHABLE();
-}
-
-
void BreakableStatementChecker::VisitProperty(Property* expr) {
// Property load is breakable.
is_breakable_ = true;
@@ -286,7 +280,7 @@
}
CodeGenerator::MakeCodePrologue(info);
const int kInitialBufferSize = 4 * KB;
- MacroAssembler masm(NULL, kInitialBufferSize);
+ MacroAssembler masm(info->isolate(), NULL, kInitialBufferSize);
#ifdef ENABLE_GDB_JIT_INTERFACE
masm.positions_recorder()->StartGDBJITLineInfoRecording();
#endif
@@ -1357,11 +1351,6 @@
}
-void FullCodeGenerator::VisitIncrementOperation(IncrementOperation* expr) {
- UNREACHABLE();
-}
-
-
int FullCodeGenerator::TryFinally::Exit(int stack_depth) {
// The macros used here must preserve the result register.
__ Drop(stack_depth);
diff --git a/src/gdb-jit.cc b/src/gdb-jit.cc
index c8dbf5d..bf8ac19 100644
--- a/src/gdb-jit.cc
+++ b/src/gdb-jit.cc
@@ -1445,11 +1445,16 @@
}
+Mutex* GDBJITInterface::mutex_ = OS::CreateMutex();
+
+
void GDBJITInterface::AddCode(const char* name,
Code* code,
GDBJITInterface::CodeTag tag,
Script* script) {
if (!FLAG_gdbjit) return;
+
+ ScopedLock lock(mutex_);
AssertNoAllocation no_gc;
HashMap::Entry* e = GetEntries()->Lookup(code, HashForCodeObject(code), true);
@@ -1518,6 +1523,7 @@
void GDBJITInterface::RemoveCode(Code* code) {
if (!FLAG_gdbjit) return;
+ ScopedLock lock(mutex_);
HashMap::Entry* e = GetEntries()->Lookup(code,
HashForCodeObject(code),
false);
@@ -1537,6 +1543,7 @@
void GDBJITInterface::RegisterDetailedLineInfo(Code* code,
GDBJITLineInfo* line_info) {
+ ScopedLock lock(mutex_);
ASSERT(!IsLineInfoTagged(line_info));
HashMap::Entry* e = GetEntries()->Lookup(code, HashForCodeObject(code), true);
ASSERT(e->value == NULL);
diff --git a/src/gdb-jit.h b/src/gdb-jit.h
index d46fec6..de6928f 100644
--- a/src/gdb-jit.h
+++ b/src/gdb-jit.h
@@ -126,6 +126,9 @@
static void RemoveCode(Code* code);
static void RegisterDetailedLineInfo(Code* code, GDBJITLineInfo* line_info);
+
+ private:
+ static Mutex* mutex_;
};
#define GDBJIT(action) GDBJITInterface::action
diff --git a/src/global-handles.cc b/src/global-handles.cc
index 4d13859..c4e8f13 100644
--- a/src/global-handles.cc
+++ b/src/global-handles.cc
@@ -558,28 +558,25 @@
void GlobalHandles::AddObjectGroup(Object*** handles,
size_t length,
v8::RetainedObjectInfo* info) {
- ObjectGroup* new_entry = new ObjectGroup(length, info);
- for (size_t i = 0; i < length; ++i) {
- new_entry->objects_.Add(handles[i]);
+ if (length == 0) {
+ if (info != NULL) info->Dispose();
+ return;
}
- object_groups_.Add(new_entry);
+ object_groups_.Add(ObjectGroup::New(handles, length, info));
}
-void GlobalHandles::AddImplicitReferences(HeapObject* parent,
+void GlobalHandles::AddImplicitReferences(HeapObject** parent,
Object*** children,
size_t length) {
- ImplicitRefGroup* new_entry = new ImplicitRefGroup(parent, length);
- for (size_t i = 0; i < length; ++i) {
- new_entry->children_.Add(children[i]);
- }
- implicit_ref_groups_.Add(new_entry);
+ if (length == 0) return;
+ implicit_ref_groups_.Add(ImplicitRefGroup::New(parent, children, length));
}
void GlobalHandles::RemoveObjectGroups() {
for (int i = 0; i < object_groups_.length(); i++) {
- delete object_groups_.at(i);
+ object_groups_.at(i)->Dispose();
}
object_groups_.Clear();
}
@@ -587,7 +584,7 @@
void GlobalHandles::RemoveImplicitRefGroups() {
for (int i = 0; i < implicit_ref_groups_.length(); i++) {
- delete implicit_ref_groups_.at(i);
+ implicit_ref_groups_.at(i)->Dispose();
}
implicit_ref_groups_.Clear();
}
diff --git a/src/global-handles.h b/src/global-handles.h
index b77fcb7..a1a269f 100644
--- a/src/global-handles.h
+++ b/src/global-handles.h
@@ -28,6 +28,8 @@
#ifndef V8_GLOBAL_HANDLES_H_
#define V8_GLOBAL_HANDLES_H_
+#include "../include/v8-profiler.h"
+
#include "list-inl.h"
#include "../include/v8-profiler.h"
@@ -44,37 +46,67 @@
// An object group is treated like a single JS object: if one of object in
// the group is alive, all objects in the same group are considered alive.
// An object group is used to simulate object relationship in a DOM tree.
-class ObjectGroup : public Malloced {
+class ObjectGroup {
public:
- ObjectGroup() : objects_(4) {}
- ObjectGroup(size_t capacity, v8::RetainedObjectInfo* info)
- : objects_(static_cast<int>(capacity)),
- info_(info) { }
- ~ObjectGroup();
+ static ObjectGroup* New(Object*** handles,
+ size_t length,
+ v8::RetainedObjectInfo* info) {
+ ASSERT(length > 0);
+ ObjectGroup* group = reinterpret_cast<ObjectGroup*>(
+ malloc(OFFSET_OF(ObjectGroup, objects_[length])));
+ group->length_ = length;
+ group->info_ = info;
+ CopyWords(group->objects_, handles, static_cast<int>(length));
+ return group;
+ }
- List<Object**> objects_;
+ void Dispose() {
+ if (info_ != NULL) info_->Dispose();
+ free(this);
+ }
+
+ size_t length_;
v8::RetainedObjectInfo* info_;
+ Object** objects_[1]; // Variable sized array.
private:
- DISALLOW_COPY_AND_ASSIGN(ObjectGroup);
+ void* operator new(size_t size);
+ void operator delete(void* p);
+ ~ObjectGroup();
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ObjectGroup);
};
// An implicit references group consists of two parts: a parent object and
// a list of children objects. If the parent is alive, all the children
// are alive too.
-class ImplicitRefGroup : public Malloced {
+class ImplicitRefGroup {
public:
- ImplicitRefGroup() : children_(4) {}
- ImplicitRefGroup(HeapObject* parent, size_t capacity)
- : parent_(parent),
- children_(static_cast<int>(capacity)) { }
+ static ImplicitRefGroup* New(HeapObject** parent,
+ Object*** children,
+ size_t length) {
+ ASSERT(length > 0);
+ ImplicitRefGroup* group = reinterpret_cast<ImplicitRefGroup*>(
+ malloc(OFFSET_OF(ImplicitRefGroup, children_[length])));
+ group->parent_ = parent;
+ group->length_ = length;
+ CopyWords(group->children_, children, static_cast<int>(length));
+ return group;
+ }
- HeapObject* parent_;
- List<Object**> children_;
+ void Dispose() {
+ free(this);
+ }
+
+ HeapObject** parent_;
+ size_t length_;
+ Object** children_[1]; // Variable sized array.
private:
- DISALLOW_COPY_AND_ASSIGN(ImplicitRefGroup);
+ void* operator new(size_t size);
+ void operator delete(void* p);
+ ~ImplicitRefGroup();
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ImplicitRefGroup);
};
@@ -156,7 +188,7 @@
// Add an implicit references' group.
// Should be only used in GC callback function before a collection.
// All groups are destroyed after a mark-compact collection.
- void AddImplicitReferences(HeapObject* parent,
+ void AddImplicitReferences(HeapObject** parent,
Object*** children,
size_t length);
diff --git a/src/handles.cc b/src/handles.cc
index 97a06d9..326de86 100644
--- a/src/handles.cc
+++ b/src/handles.cc
@@ -369,6 +369,17 @@
}
+Handle<Object> GetProperty(Handle<JSObject> obj,
+ Handle<String> name,
+ LookupResult* result) {
+ PropertyAttributes attributes;
+ Isolate* isolate = Isolate::Current();
+ CALL_HEAP_FUNCTION(isolate,
+ obj->GetProperty(*obj, result, *name, &attributes),
+ Object);
+}
+
+
Handle<Object> GetElement(Handle<Object> obj,
uint32_t index) {
Isolate* isolate = Isolate::Current();
diff --git a/src/handles.h b/src/handles.h
index a357a00..3839f37 100644
--- a/src/handles.h
+++ b/src/handles.h
@@ -244,6 +244,11 @@
Handle<Object> GetProperty(Handle<Object> obj,
Handle<Object> key);
+Handle<Object> GetProperty(Handle<JSObject> obj,
+ Handle<String> name,
+ LookupResult* result);
+
+
Handle<Object> GetElement(Handle<Object> obj,
uint32_t index);
diff --git a/src/heap.cc b/src/heap.cc
index 5d1a66e..9a3cfe4 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -30,7 +30,7 @@
#include "accessors.h"
#include "api.h"
#include "bootstrapper.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "compilation-cache.h"
#include "debug.h"
#include "heap-profiler.h"
@@ -941,6 +941,8 @@
gc_state_ = SCAVENGE;
+ SwitchScavengingVisitorsTableIfProfilingWasEnabled();
+
Page::FlipMeaningOfInvalidatedWatermarkFlag(this);
#ifdef DEBUG
VerifyPageWatermarkValidity(old_pointer_space_, ALL_VALID);
@@ -1232,6 +1234,32 @@
}
+enum LoggingAndProfiling {
+ LOGGING_AND_PROFILING_ENABLED,
+ LOGGING_AND_PROFILING_DISABLED
+};
+
+
+typedef void (*ScavengingCallback)(Map* map,
+ HeapObject** slot,
+ HeapObject* object);
+
+
+static Atomic32 scavenging_visitors_table_mode_;
+static VisitorDispatchTable<ScavengingCallback> scavenging_visitors_table_;
+
+
+INLINE(static void DoScavengeObject(Map* map,
+ HeapObject** slot,
+ HeapObject* obj));
+
+
+void DoScavengeObject(Map* map, HeapObject** slot, HeapObject* obj) {
+ scavenging_visitors_table_.GetVisitor(map)(map, slot, obj);
+}
+
+
+template<LoggingAndProfiling logging_and_profiling_mode>
class ScavengingVisitor : public StaticVisitorBase {
public:
static void Initialize() {
@@ -1240,23 +1268,22 @@
table_.Register(kVisitShortcutCandidate, &EvacuateShortcutCandidate);
table_.Register(kVisitByteArray, &EvacuateByteArray);
table_.Register(kVisitFixedArray, &EvacuateFixedArray);
+
table_.Register(kVisitGlobalContext,
&ObjectEvacuationStrategy<POINTER_OBJECT>::
- VisitSpecialized<Context::kSize>);
-
- typedef ObjectEvacuationStrategy<POINTER_OBJECT> PointerObject;
+ template VisitSpecialized<Context::kSize>);
table_.Register(kVisitConsString,
&ObjectEvacuationStrategy<POINTER_OBJECT>::
- VisitSpecialized<ConsString::kSize>);
+ template VisitSpecialized<ConsString::kSize>);
table_.Register(kVisitSharedFunctionInfo,
&ObjectEvacuationStrategy<POINTER_OBJECT>::
- VisitSpecialized<SharedFunctionInfo::kSize>);
+ template VisitSpecialized<SharedFunctionInfo::kSize>);
table_.Register(kVisitJSFunction,
&ObjectEvacuationStrategy<POINTER_OBJECT>::
- VisitSpecialized<JSFunction::kSize>);
+ template VisitSpecialized<JSFunction::kSize>);
table_.RegisterSpecializations<ObjectEvacuationStrategy<DATA_OBJECT>,
kVisitDataObject,
@@ -1271,12 +1298,10 @@
kVisitStructGeneric>();
}
-
- static inline void Scavenge(Map* map, HeapObject** slot, HeapObject* obj) {
- table_.GetVisitor(map)(map, slot, obj);
+ static VisitorDispatchTable<ScavengingCallback>* GetTable() {
+ return &table_;
}
-
private:
enum ObjectContents { DATA_OBJECT, POINTER_OBJECT };
enum SizeRestriction { SMALL, UNKNOWN_SIZE };
@@ -1313,21 +1338,24 @@
// Set the forwarding address.
source->set_map_word(MapWord::FromForwardingAddress(target));
+ if (logging_and_profiling_mode == LOGGING_AND_PROFILING_ENABLED) {
#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
- // Update NewSpace stats if necessary.
- RecordCopiedObject(heap, target);
+ // Update NewSpace stats if necessary.
+ RecordCopiedObject(heap, target);
#endif
- HEAP_PROFILE(heap, ObjectMoveEvent(source->address(), target->address()));
+ HEAP_PROFILE(heap, ObjectMoveEvent(source->address(), target->address()));
#if defined(ENABLE_LOGGING_AND_PROFILING)
- Isolate* isolate = heap->isolate();
- if (isolate->logger()->is_logging() ||
- isolate->cpu_profiler()->is_profiling()) {
- if (target->IsSharedFunctionInfo()) {
- PROFILE(isolate, SharedFunctionInfoMoveEvent(
- source->address(), target->address()));
+ Isolate* isolate = heap->isolate();
+ if (isolate->logger()->is_logging() ||
+ isolate->cpu_profiler()->is_profiling()) {
+ if (target->IsSharedFunctionInfo()) {
+ PROFILE(isolate, SharedFunctionInfoMoveEvent(
+ source->address(), target->address()));
+ }
}
- }
#endif
+ }
+
return target;
}
@@ -1443,7 +1471,7 @@
return;
}
- Scavenge(first->map(), slot, first);
+ DoScavengeObject(first->map(), slot, first);
object->set_map_word(MapWord::FromForwardingAddress(*slot));
return;
}
@@ -1470,13 +1498,51 @@
}
};
- typedef void (*Callback)(Map* map, HeapObject** slot, HeapObject* object);
-
- static VisitorDispatchTable<Callback> table_;
+ static VisitorDispatchTable<ScavengingCallback> table_;
};
-VisitorDispatchTable<ScavengingVisitor::Callback> ScavengingVisitor::table_;
+template<LoggingAndProfiling logging_and_profiling_mode>
+VisitorDispatchTable<ScavengingCallback>
+ ScavengingVisitor<logging_and_profiling_mode>::table_;
+
+
+static void InitializeScavengingVisitorsTables() {
+ ScavengingVisitor<LOGGING_AND_PROFILING_DISABLED>::Initialize();
+ ScavengingVisitor<LOGGING_AND_PROFILING_ENABLED>::Initialize();
+ scavenging_visitors_table_.CopyFrom(
+ ScavengingVisitor<LOGGING_AND_PROFILING_DISABLED>::GetTable());
+ scavenging_visitors_table_mode_ = LOGGING_AND_PROFILING_DISABLED;
+}
+
+
+void Heap::SwitchScavengingVisitorsTableIfProfilingWasEnabled() {
+ if (scavenging_visitors_table_mode_ == LOGGING_AND_PROFILING_ENABLED) {
+ // Table was already updated by some isolate.
+ return;
+ }
+
+ if (isolate()->logger()->is_logging() ||
+ isolate()->cpu_profiler()->is_profiling() ||
+ (isolate()->heap_profiler() != NULL &&
+ isolate()->heap_profiler()->is_profiling())) {
+ // If one of the isolates is doing scavenge at this moment of time
+ // it might see this table in an inconsitent state when
+ // some of the callbacks point to
+ // ScavengingVisitor<LOGGING_AND_PROFILING_ENABLED> and others
+ // to ScavengingVisitor<LOGGING_AND_PROFILING_DISABLED>.
+ // However this does not lead to any bugs as such isolate does not have
+ // profiling enabled and any isolate with enabled profiling is guaranteed
+ // to see the table in the consistent state.
+ scavenging_visitors_table_.CopyFrom(
+ ScavengingVisitor<LOGGING_AND_PROFILING_ENABLED>::GetTable());
+
+ // We use Release_Store to prevent reordering of this write before writes
+ // to the table.
+ Release_Store(&scavenging_visitors_table_mode_,
+ LOGGING_AND_PROFILING_ENABLED);
+ }
+}
void Heap::ScavengeObjectSlow(HeapObject** p, HeapObject* object) {
@@ -1484,7 +1550,7 @@
MapWord first_word = object->map_word();
ASSERT(!first_word.IsForwardingAddress());
Map* map = first_word.ToMap();
- ScavengingVisitor::Scavenge(map, p, object);
+ DoScavengeObject(map, p, object);
}
@@ -3165,7 +3231,7 @@
// Fill these accessors into the dictionary.
DescriptorArray* descs = map->instance_descriptors();
for (int i = 0; i < descs->number_of_descriptors(); i++) {
- PropertyDetails details = descs->GetDetails(i);
+ PropertyDetails details(descs->GetDetails(i));
ASSERT(details.type() == CALLBACKS); // Only accessors are expected.
PropertyDetails d =
PropertyDetails(details.attributes(), CALLBACKS, details.index());
@@ -3320,8 +3386,8 @@
const uc32 kMaxSupportedChar = 0xFFFF;
// Count the number of characters in the UTF-8 string and check if
// it is an ASCII string.
- Access<ScannerConstants::Utf8Decoder>
- decoder(isolate_->scanner_constants()->utf8_decoder());
+ Access<UnicodeCache::Utf8Decoder>
+ decoder(isolate_->unicode_cache()->utf8_decoder());
decoder->Reset(string.start(), string.length());
int chars = 0;
while (decoder->has_more()) {
@@ -4757,10 +4823,10 @@
gc_initializer_mutex->Lock();
static bool initialized_gc = false;
if (!initialized_gc) {
- initialized_gc = true;
- ScavengingVisitor::Initialize();
- NewSpaceScavenger::Initialize();
- MarkCompactCollector::Initialize();
+ initialized_gc = true;
+ InitializeScavengingVisitorsTables();
+ NewSpaceScavenger::Initialize();
+ MarkCompactCollector::Initialize();
}
gc_initializer_mutex->Unlock();
diff --git a/src/heap.h b/src/heap.h
index 88074d7..7a1bed3 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -155,6 +155,7 @@
V(name_symbol, "name") \
V(number_symbol, "number") \
V(Number_symbol, "Number") \
+ V(nan_symbol, "NaN") \
V(RegExp_symbol, "RegExp") \
V(source_symbol, "source") \
V(global_symbol, "global") \
@@ -1451,6 +1452,8 @@
// Allocate empty fixed array.
MUST_USE_RESULT MaybeObject* AllocateEmptyFixedArray();
+ void SwitchScavengingVisitorsTableIfProfilingWasEnabled();
+
// Performs a minor collection in new generation.
void Scavenge();
diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc
index 9bbe164..3b01f57 100644
--- a/src/hydrogen-instructions.cc
+++ b/src/hydrogen-instructions.cc
@@ -726,7 +726,7 @@
void HChange::PrintDataTo(StringStream* stream) {
HUnaryOperation::PrintDataTo(stream);
- stream->Add(" %s to %s", from_.Mnemonic(), to_.Mnemonic());
+ stream->Add(" %s to %s", from_.Mnemonic(), to().Mnemonic());
if (CanTruncateToInt32()) stream->Add(" truncating-int32");
if (CheckFlag(kBailoutOnMinusZero)) stream->Add(" -0?");
@@ -1017,10 +1017,9 @@
HConstant::HConstant(Handle<Object> handle, Representation r)
: handle_(handle),
- constant_type_(HType::TypeFromValue(handle)),
has_int32_value_(false),
- int32_value_(0),
has_double_value_(false),
+ int32_value_(0),
double_value_(0) {
set_representation(r);
SetFlag(kUseGVN);
@@ -1050,6 +1049,23 @@
}
+bool HConstant::ToBoolean() const {
+ // Converts the constant's boolean value according to
+ // ECMAScript section 9.2 ToBoolean conversion.
+ if (HasInteger32Value()) return Integer32Value() != 0;
+ if (HasDoubleValue()) {
+ double v = DoubleValue();
+ return v != 0 && !isnan(v);
+ }
+ if (handle()->IsTrue()) return true;
+ if (handle()->IsFalse()) return false;
+ if (handle()->IsUndefined()) return false;
+ if (handle()->IsNull()) return false;
+ if (handle()->IsString() &&
+ String::cast(*handle())->length() == 0) return false;
+ return true;
+}
+
void HConstant::PrintDataTo(StringStream* stream) {
handle()->ShortPrint(stream);
}
@@ -1342,18 +1358,29 @@
}
-void HLoadGlobal::PrintDataTo(StringStream* stream) {
+void HLoadGlobalCell::PrintDataTo(StringStream* stream) {
stream->Add("[%p]", *cell());
if (check_hole_value()) stream->Add(" (deleteable/read-only)");
}
-void HStoreGlobal::PrintDataTo(StringStream* stream) {
+void HLoadGlobalGeneric::PrintDataTo(StringStream* stream) {
+ stream->Add("%o ", *name());
+}
+
+
+void HStoreGlobalCell::PrintDataTo(StringStream* stream) {
stream->Add("[%p] = ", *cell());
value()->PrintNameTo(stream);
}
+void HStoreGlobalGeneric::PrintDataTo(StringStream* stream) {
+ stream->Add("%o = ", *name());
+ value()->PrintNameTo(stream);
+}
+
+
void HLoadContextSlot::PrintDataTo(StringStream* stream) {
value()->PrintNameTo(stream);
stream->Add("[%d]", slot_index());
@@ -1407,7 +1434,7 @@
HType HConstant::CalculateInferredType() {
- return constant_type_;
+ return HType::TypeFromValue(handle_);
}
diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h
index fed4b8b..e32a09c 100644
--- a/src/hydrogen-instructions.h
+++ b/src/hydrogen-instructions.h
@@ -114,6 +114,7 @@
V(HasCachedArrayIndex) \
V(InstanceOf) \
V(InstanceOfKnownGlobal) \
+ V(InvokeFunction) \
V(IsNull) \
V(IsObject) \
V(IsSmi) \
@@ -124,7 +125,8 @@
V(LoadElements) \
V(LoadExternalArrayPointer) \
V(LoadFunctionPrototype) \
- V(LoadGlobal) \
+ V(LoadGlobalCell) \
+ V(LoadGlobalGeneric) \
V(LoadKeyedFastElement) \
V(LoadKeyedGeneric) \
V(LoadKeyedSpecializedArrayElement) \
@@ -147,12 +149,14 @@
V(Simulate) \
V(StackCheck) \
V(StoreContextSlot) \
- V(StoreGlobal) \
+ V(StoreGlobalCell) \
+ V(StoreGlobalGeneric) \
V(StoreKeyedFastElement) \
V(StoreKeyedSpecializedArrayElement) \
V(StoreKeyedGeneric) \
V(StoreNamedField) \
V(StoreNamedGeneric) \
+ V(StringAdd) \
V(StringCharCodeAt) \
V(StringCharFromCode) \
V(StringLength) \
@@ -272,7 +276,7 @@
return kind_ == other.kind_;
}
- Kind kind() const { return kind_; }
+ Kind kind() const { return static_cast<Kind>(kind_); }
bool IsNone() const { return kind_ == kNone; }
bool IsTagged() const { return kind_ == kTagged; }
bool IsInteger32() const { return kind_ == kInteger32; }
@@ -286,7 +290,10 @@
private:
explicit Representation(Kind k) : kind_(k) { }
- Kind kind_;
+ // Make sure kind fits in int8.
+ STATIC_ASSERT(kNumRepresentations <= (1 << kBitsPerByte));
+
+ int8_t kind_;
};
@@ -393,9 +400,12 @@
kUninitialized = 0x1fff // 0001 1111 1111 1111
};
+ // Make sure type fits in int16.
+ STATIC_ASSERT(kUninitialized < (1 << (2 * kBitsPerByte)));
+
explicit HType(Type t) : type_(t) { }
- Type type_;
+ int16_t type_;
};
@@ -609,8 +619,8 @@
int id_;
Representation representation_;
- SmallPointerList<HValue> uses_;
HType type_;
+ SmallPointerList<HValue> uses_;
Range* range_;
int flags_;
@@ -931,7 +941,7 @@
Representation from,
Representation to,
bool is_truncating)
- : HUnaryOperation(value), from_(from), to_(to) {
+ : HUnaryOperation(value), from_(from) {
ASSERT(!from.IsNone() && !to.IsNone());
ASSERT(!from.Equals(to));
set_representation(to);
@@ -946,7 +956,7 @@
virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);
Representation from() const { return from_; }
- Representation to() const { return to_; }
+ Representation to() const { return representation(); }
virtual Representation RequiredInputRepresentation(int index) const {
return from_;
}
@@ -968,16 +978,14 @@
private:
Representation from_;
- Representation to_;
};
class HSimulate: public HInstruction {
public:
- HSimulate(int ast_id, int pop_count, int environment_length)
+ HSimulate(int ast_id, int pop_count)
: ast_id_(ast_id),
pop_count_(pop_count),
- environment_length_(environment_length),
values_(2),
assigned_indexes_(2) {}
virtual ~HSimulate() {}
@@ -991,7 +999,6 @@
ast_id_ = id;
}
- int environment_length() const { return environment_length_; }
int pop_count() const { return pop_count_; }
const ZoneList<HValue*>* values() const { return &values_; }
int GetAssignedIndexAt(int index) const {
@@ -1037,7 +1044,6 @@
}
int ast_id_;
int pop_count_;
- int environment_length_;
ZoneList<HValue*> values_;
ZoneList<int> assigned_indexes_;
};
@@ -1239,6 +1245,23 @@
};
+class HInvokeFunction: public HBinaryCall {
+ public:
+ HInvokeFunction(HValue* context, HValue* function, int argument_count)
+ : HBinaryCall(context, function, argument_count) {
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) const {
+ return Representation::Tagged();
+ }
+
+ HValue* context() { return first(); }
+ HValue* function() { return second(); }
+
+ DECLARE_CONCRETE_INSTRUCTION(InvokeFunction, "invoke_function")
+};
+
+
class HCallConstantFunction: public HCall<0> {
public:
HCallConstantFunction(Handle<JSFunction> function, int argument_count)
@@ -1703,6 +1726,16 @@
virtual void Verify();
#endif
+ virtual HValue* Canonicalize() {
+ if (!value()->type().IsUninitialized() &&
+ value()->type().IsString() &&
+ first() == FIRST_STRING_TYPE &&
+ last() == LAST_STRING_TYPE) {
+ return NULL;
+ }
+ return this;
+ }
+
static HCheckInstanceType* NewIsJSObjectOrJSFunction(HValue* value);
InstanceType first() const { return first_; }
@@ -1744,6 +1777,18 @@
virtual void Verify();
#endif
+ virtual HValue* Canonicalize() {
+ HType value_type = value()->type();
+ if (!value_type.IsUninitialized() &&
+ (value_type.IsHeapNumber() ||
+ value_type.IsString() ||
+ value_type.IsBoolean() ||
+ value_type.IsNonPrimitive())) {
+ return NULL;
+ }
+ return this;
+ }
+
DECLARE_CONCRETE_INSTRUCTION(CheckNonSmi, "check_non_smi")
protected:
@@ -1963,6 +2008,8 @@
}
bool HasStringValue() const { return handle_->IsString(); }
+ bool ToBoolean() const;
+
virtual intptr_t Hashcode() {
ASSERT(!HEAP->allow_allocation(false));
return reinterpret_cast<intptr_t>(*handle());
@@ -1984,14 +2031,13 @@
private:
Handle<Object> handle_;
- HType constant_type_;
// The following two values represent the int32 and the double value of the
// given constant if there is a lossless conversion between the constant
// and the specific representation.
- bool has_int32_value_;
+ bool has_int32_value_ : 1;
+ bool has_double_value_ : 1;
int32_t int32_value_;
- bool has_double_value_;
double double_value_;
};
@@ -2809,9 +2855,9 @@
};
-class HLoadGlobal: public HTemplateInstruction<0> {
+class HLoadGlobalCell: public HTemplateInstruction<0> {
public:
- HLoadGlobal(Handle<JSGlobalPropertyCell> cell, bool check_hole_value)
+ HLoadGlobalCell(Handle<JSGlobalPropertyCell> cell, bool check_hole_value)
: cell_(cell), check_hole_value_(check_hole_value) {
set_representation(Representation::Tagged());
SetFlag(kUseGVN);
@@ -2832,11 +2878,11 @@
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(LoadGlobal, "load_global")
+ DECLARE_CONCRETE_INSTRUCTION(LoadGlobalCell, "load_global_cell")
protected:
virtual bool DataEquals(HValue* other) {
- HLoadGlobal* b = HLoadGlobal::cast(other);
+ HLoadGlobalCell* b = HLoadGlobalCell::cast(other);
return cell_.is_identical_to(b->cell());
}
@@ -2846,11 +2892,43 @@
};
-class HStoreGlobal: public HUnaryOperation {
+class HLoadGlobalGeneric: public HBinaryOperation {
public:
- HStoreGlobal(HValue* value,
- Handle<JSGlobalPropertyCell> cell,
- bool check_hole_value)
+ HLoadGlobalGeneric(HValue* context,
+ HValue* global_object,
+ Handle<Object> name,
+ bool for_typeof)
+ : HBinaryOperation(context, global_object),
+ name_(name),
+ for_typeof_(for_typeof) {
+ set_representation(Representation::Tagged());
+ SetAllSideEffects();
+ }
+
+ HValue* context() { return OperandAt(0); }
+ HValue* global_object() { return OperandAt(1); }
+ Handle<Object> name() const { return name_; }
+ bool for_typeof() const { return for_typeof_; }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual Representation RequiredInputRepresentation(int index) const {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadGlobalGeneric, "load_global_generic")
+
+ private:
+ Handle<Object> name_;
+ bool for_typeof_;
+};
+
+
+class HStoreGlobalCell: public HUnaryOperation {
+ public:
+ HStoreGlobalCell(HValue* value,
+ Handle<JSGlobalPropertyCell> cell,
+ bool check_hole_value)
: HUnaryOperation(value),
cell_(cell),
check_hole_value_(check_hole_value) {
@@ -2865,7 +2943,7 @@
}
virtual void PrintDataTo(StringStream* stream);
- DECLARE_CONCRETE_INSTRUCTION(StoreGlobal, "store_global")
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalCell, "store_global_cell")
private:
Handle<JSGlobalPropertyCell> cell_;
@@ -2873,6 +2951,42 @@
};
+class HStoreGlobalGeneric: public HTemplateInstruction<3> {
+ public:
+ HStoreGlobalGeneric(HValue* context,
+ HValue* global_object,
+ Handle<Object> name,
+ HValue* value,
+ bool strict_mode)
+ : name_(name),
+ strict_mode_(strict_mode) {
+ SetOperandAt(0, context);
+ SetOperandAt(1, global_object);
+ SetOperandAt(2, value);
+ set_representation(Representation::Tagged());
+ SetAllSideEffects();
+ }
+
+ HValue* context() { return OperandAt(0); }
+ HValue* global_object() { return OperandAt(1); }
+ Handle<Object> name() const { return name_; }
+ HValue* value() { return OperandAt(2); }
+ bool strict_mode() { return strict_mode_; }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual Representation RequiredInputRepresentation(int index) const {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalGeneric, "store_global_generic")
+
+ private:
+ Handle<Object> name_;
+ bool strict_mode_;
+};
+
+
class HLoadContextSlot: public HUnaryOperation {
public:
HLoadContextSlot(HValue* context , int slot_index)
@@ -3201,8 +3315,10 @@
HStoreNamedGeneric(HValue* context,
HValue* object,
Handle<String> name,
- HValue* value)
- : name_(name) {
+ HValue* value,
+ bool strict_mode)
+ : name_(name),
+ strict_mode_(strict_mode) {
SetOperandAt(0, object);
SetOperandAt(1, value);
SetOperandAt(2, context);
@@ -3213,6 +3329,7 @@
HValue* value() { return OperandAt(1); }
HValue* context() { return OperandAt(2); }
Handle<String> name() { return name_; }
+ bool strict_mode() { return strict_mode_; }
virtual void PrintDataTo(StringStream* stream);
@@ -3224,6 +3341,7 @@
private:
Handle<String> name_;
+ bool strict_mode_;
};
@@ -3301,7 +3419,9 @@
HStoreKeyedGeneric(HValue* context,
HValue* object,
HValue* key,
- HValue* value) {
+ HValue* value,
+ bool strict_mode)
+ : strict_mode_(strict_mode) {
SetOperandAt(0, object);
SetOperandAt(1, key);
SetOperandAt(2, value);
@@ -3313,6 +3433,7 @@
HValue* key() { return OperandAt(1); }
HValue* value() { return OperandAt(2); }
HValue* context() { return OperandAt(3); }
+ bool strict_mode() { return strict_mode_; }
virtual Representation RequiredInputRepresentation(int index) const {
return Representation::Tagged();
@@ -3321,6 +3442,32 @@
virtual void PrintDataTo(StringStream* stream);
DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store_keyed_generic")
+
+ private:
+ bool strict_mode_;
+};
+
+
+class HStringAdd: public HBinaryOperation {
+ public:
+ HStringAdd(HValue* left, HValue* right) : HBinaryOperation(left, right) {
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ SetFlag(kDependsOnMaps);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) const {
+ return Representation::Tagged();
+ }
+
+ virtual HType CalculateInferredType() {
+ return HType::String();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string_add")
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
};
diff --git a/src/hydrogen.cc b/src/hydrogen.cc
index 2383192..f6c47f3 100644
--- a/src/hydrogen.cc
+++ b/src/hydrogen.cc
@@ -75,7 +75,7 @@
void HBasicBlock::AttachLoopInformation() {
ASSERT(!IsLoopHeader());
- loop_information_ = new HLoopInformation(this);
+ loop_information_ = new(zone()) HLoopInformation(this);
}
@@ -107,7 +107,7 @@
ASSERT(!instr->IsLinked());
ASSERT(!IsFinished());
if (first_ == NULL) {
- HBlockEntry* entry = new HBlockEntry();
+ HBlockEntry* entry = new(zone()) HBlockEntry();
entry->InitializeAsFirst(this);
first_ = last_ = entry;
}
@@ -120,7 +120,7 @@
ASSERT(HasEnvironment());
HEnvironment* environment = last_environment();
- HDeoptimize* instr = new HDeoptimize(environment->length());
+ HDeoptimize* instr = new(zone()) HDeoptimize(environment->length());
for (int i = 0; i < environment->length(); i++) {
HValue* val = environment->values()->at(i);
@@ -140,8 +140,7 @@
int push_count = environment->push_count();
int pop_count = environment->pop_count();
- int length = environment->length();
- HSimulate* instr = new HSimulate(id, pop_count, length);
+ HSimulate* instr = new(zone()) HSimulate(id, pop_count);
for (int i = push_count - 1; i >= 0; --i) {
instr->AddPushedValue(environment->ExpressionStackAt(i));
}
@@ -169,11 +168,11 @@
void HBasicBlock::Goto(HBasicBlock* block, bool include_stack_check) {
if (block->IsInlineReturnTarget()) {
- AddInstruction(new HLeaveInlined);
+ AddInstruction(new(zone()) HLeaveInlined);
last_environment_ = last_environment()->outer();
}
AddSimulate(AstNode::kNoNumber);
- HGoto* instr = new HGoto(block);
+ HGoto* instr = new(zone()) HGoto(block);
instr->set_include_stack_check(include_stack_check);
Finish(instr);
}
@@ -182,11 +181,11 @@
void HBasicBlock::AddLeaveInlined(HValue* return_value, HBasicBlock* target) {
ASSERT(target->IsInlineReturnTarget());
ASSERT(return_value != NULL);
- AddInstruction(new HLeaveInlined);
+ AddInstruction(new(zone()) HLeaveInlined);
last_environment_ = last_environment()->outer();
last_environment()->Push(return_value);
AddSimulate(AstNode::kNoNumber);
- HGoto* instr = new HGoto(target);
+ HGoto* instr = new(zone()) HGoto(target);
Finish(instr);
}
@@ -243,7 +242,7 @@
void HBasicBlock::RegisterPredecessor(HBasicBlock* pred) {
- if (!predecessors_.is_empty()) {
+ if (HasPredecessor()) {
// Only loop header blocks can have a predecessor added after
// instructions have been added to the block (they have phis for all
// values in the environment, these phis may be eliminated later).
@@ -494,8 +493,8 @@
HConstant* HGraph::GetConstant(SetOncePointer<HConstant>* pointer,
Object* value) {
if (!pointer->is_set()) {
- HConstant* constant = new HConstant(Handle<Object>(value),
- Representation::Tagged());
+ HConstant* constant = new(zone()) HConstant(Handle<Object>(value),
+ Representation::Tagged());
constant->InsertAfter(GetConstantUndefined());
pointer->set(constant);
}
@@ -581,8 +580,9 @@
blocks_(8),
values_(16),
phi_list_(NULL) {
- start_environment_ = new HEnvironment(NULL, info->scope(), info->closure());
- start_environment_->set_ast_id(info->function()->id());
+ start_environment_ =
+ new(zone()) HEnvironment(NULL, info->scope(), info->closure());
+ start_environment_->set_ast_id(AstNode::kFunctionEntryId);
entry_block_ = CreateBasicBlock();
entry_block_->SetInitialEnvironment(start_environment_);
}
@@ -606,7 +606,7 @@
if (!FLAG_use_lithium) return Handle<Code>::null();
- MacroAssembler assembler(NULL, 0);
+ MacroAssembler assembler(info->isolate(), NULL, 0);
LCodeGen generator(chunk, &assembler, info);
if (FLAG_eliminate_empty_blocks) {
@@ -631,7 +631,7 @@
HBasicBlock* HGraph::CreateBasicBlock() {
- HBasicBlock* result = new HBasicBlock(this);
+ HBasicBlock* result = new(zone()) HBasicBlock(this);
blocks_.Add(result);
return result;
}
@@ -1273,6 +1273,7 @@
HGraph* graph() { return graph_; }
CompilationInfo* info() { return info_; }
+ Zone* zone() { return graph_->zone(); }
HGraph* graph_;
CompilationInfo* info_;
@@ -1290,7 +1291,7 @@
if (FLAG_loop_invariant_code_motion) {
LoopInvariantCodeMotion();
}
- HValueMap* map = new HValueMap();
+ HValueMap* map = new(zone()) HValueMap();
AnalyzeBlock(graph_->blocks()->at(0), map);
}
@@ -1457,7 +1458,7 @@
for (int i = 0; i < length; ++i) {
HBasicBlock* dominated = block->dominated_blocks()->at(i);
// No need to copy the map for the last child in the dominator tree.
- HValueMap* successor_map = (i == length - 1) ? map : map->Copy();
+ HValueMap* successor_map = (i == length - 1) ? map : map->Copy(zone());
// If the dominated block is not a successor to this block we have to
// kill everything killed on any path between this block and the
@@ -1495,6 +1496,8 @@
void AddDependantsToWorklist(HValue* current);
void InferBasedOnUses(HValue* current);
+ Zone* zone() { return graph_->zone(); }
+
HGraph* graph_;
ZoneList<HValue*> worklist_;
BitVector in_worklist_;
@@ -1606,7 +1609,7 @@
ScopedVector<BitVector*> connected_phis(num_phis);
for (int i = 0; i < num_phis; i++) {
phi_list->at(i)->InitRealUses(i);
- connected_phis[i] = new BitVector(num_phis);
+ connected_phis[i] = new(zone()) BitVector(num_phis);
connected_phis[i]->Add(i);
}
@@ -1771,7 +1774,8 @@
}
if (new_value == NULL) {
- new_value = new HChange(value, value->representation(), to, is_truncating);
+ new_value =
+ new(zone()) HChange(value, value->representation(), to, is_truncating);
}
new_value->InsertBefore(next);
@@ -1804,7 +1808,7 @@
ZoneList<Representation>* to_convert_reps) {
Representation r = current->representation();
if (r.IsNone()) return;
- if (current->uses()->length() == 0) return;
+ if (current->uses()->is_empty()) return;
// Collect the representation changes in a sorted list. This allows
// us to avoid duplicate changes without searching the list.
@@ -1980,7 +1984,10 @@
// Implementation of utility classes to represent an expression's context in
// the AST.
AstContext::AstContext(HGraphBuilder* owner, Expression::Context kind)
- : owner_(owner), kind_(kind), outer_(owner->ast_context()) {
+ : owner_(owner),
+ kind_(kind),
+ outer_(owner->ast_context()),
+ for_typeof_(false) {
owner->set_ast_context(this); // Push.
#ifdef DEBUG
original_length_ = owner->environment()->length();
@@ -2059,7 +2066,7 @@
HGraphBuilder* builder = owner();
HBasicBlock* empty_true = builder->graph()->CreateBasicBlock();
HBasicBlock* empty_false = builder->graph()->CreateBasicBlock();
- HTest* test = new HTest(value, empty_true, empty_false);
+ HTest* test = new(zone()) HTest(value, empty_true, empty_false);
builder->current_block()->Finish(test);
empty_true->Goto(if_true(), false);
@@ -2069,37 +2076,17 @@
// HGraphBuilder infrastructure for bailing out and checking bailouts.
-#define BAILOUT(reason) \
+#define CHECK_BAILOUT(call) \
do { \
- Bailout(reason); \
- return; \
- } while (false)
-
-
-#define CHECK_BAILOUT \
- do { \
+ call; \
if (HasStackOverflow()) return; \
} while (false)
-#define VISIT_FOR_EFFECT(expr) \
- do { \
- VisitForEffect(expr); \
- if (HasStackOverflow()) return; \
- } while (false)
-
-
-#define VISIT_FOR_VALUE(expr) \
- do { \
- VisitForValue(expr); \
- if (HasStackOverflow()) return; \
- } while (false)
-
-
-#define VISIT_FOR_CONTROL(expr, true_block, false_block) \
+#define CHECK_ALIVE(call) \
do { \
- VisitForControl(expr, true_block, false_block); \
- if (HasStackOverflow()) return; \
+ call; \
+ if (HasStackOverflow() || current_block() == NULL) return; \
} while (false)
@@ -2124,6 +2111,14 @@
}
+void HGraphBuilder::VisitForTypeOf(Expression* expr) {
+ ValueContext for_value(this);
+ for_value.set_for_typeof(true);
+ Visit(expr);
+}
+
+
+
void HGraphBuilder::VisitForControl(Expression* expr,
HBasicBlock* true_block,
HBasicBlock* false_block) {
@@ -2133,28 +2128,27 @@
void HGraphBuilder::VisitArgument(Expression* expr) {
- VISIT_FOR_VALUE(expr);
- Push(AddInstruction(new HPushArgument(Pop())));
+ CHECK_ALIVE(VisitForValue(expr));
+ Push(AddInstruction(new(zone()) HPushArgument(Pop())));
}
void HGraphBuilder::VisitArgumentList(ZoneList<Expression*>* arguments) {
for (int i = 0; i < arguments->length(); i++) {
- VisitArgument(arguments->at(i));
- if (HasStackOverflow() || current_block() == NULL) return;
+ CHECK_ALIVE(VisitArgument(arguments->at(i)));
}
}
void HGraphBuilder::VisitExpressions(ZoneList<Expression*>* exprs) {
for (int i = 0; i < exprs->length(); ++i) {
- VISIT_FOR_VALUE(exprs->at(i));
+ CHECK_ALIVE(VisitForValue(exprs->at(i)));
}
}
HGraph* HGraphBuilder::CreateGraph() {
- graph_ = new HGraph(info());
+ graph_ = new(zone()) HGraph(info());
if (FLAG_hydrogen_stats) HStatistics::Instance()->Initialize(info());
{
@@ -2168,7 +2162,7 @@
}
SetupScope(scope);
VisitDeclarations(scope->declarations());
- AddInstruction(new HStackCheck());
+ AddInstruction(new(zone()) HStackCheck());
// Add an edge to the body entry. This is warty: the graph's start
// environment will be used by the Lithium translation as the initial
@@ -2188,13 +2182,13 @@
HEnvironment* initial_env = environment()->CopyWithoutHistory();
HBasicBlock* body_entry = CreateBasicBlock(initial_env);
current_block()->Goto(body_entry);
- body_entry->SetJoinId(info()->function()->id());
+ body_entry->SetJoinId(AstNode::kFunctionEntryId);
set_current_block(body_entry);
VisitStatements(info()->function()->body());
if (HasStackOverflow()) return NULL;
if (current_block() != NULL) {
- HReturn* instr = new HReturn(graph()->GetConstantUndefined());
+ HReturn* instr = new(zone()) HReturn(graph()->GetConstantUndefined());
current_block()->FinishExit(instr);
set_current_block(NULL);
}
@@ -2271,7 +2265,7 @@
}
while (!arguments.is_empty()) {
- AddInstruction(new HPushArgument(arguments.RemoveLast()));
+ AddInstruction(new(zone()) HPushArgument(arguments.RemoveLast()));
}
return call;
}
@@ -2279,9 +2273,9 @@
void HGraphBuilder::SetupScope(Scope* scope) {
// We don't yet handle the function name for named function expressions.
- if (scope->function() != NULL) BAILOUT("named function expression");
+ if (scope->function() != NULL) return Bailout("named function expression");
- HConstant* undefined_constant = new HConstant(
+ HConstant* undefined_constant = new(zone()) HConstant(
isolate()->factory()->undefined_value(), Representation::Tagged());
AddInstruction(undefined_constant);
graph_->set_undefined_constant(undefined_constant);
@@ -2290,7 +2284,7 @@
// parameter index 0.
int count = scope->num_parameters() + 1;
for (int i = 0; i < count; ++i) {
- HInstruction* parameter = AddInstruction(new HParameter(i));
+ HInstruction* parameter = AddInstruction(new(zone()) HParameter(i));
environment()->Bind(i, parameter);
}
@@ -2305,9 +2299,9 @@
if (!scope->arguments()->IsStackAllocated() ||
(scope->arguments_shadow() != NULL &&
!scope->arguments_shadow()->IsStackAllocated())) {
- BAILOUT("context-allocated arguments");
+ return Bailout("context-allocated arguments");
}
- HArgumentsObject* object = new HArgumentsObject;
+ HArgumentsObject* object = new(zone()) HArgumentsObject;
AddInstruction(object);
graph()->SetArgumentsObject(object);
environment()->Bind(scope->arguments(), object);
@@ -2320,8 +2314,7 @@
void HGraphBuilder::VisitStatements(ZoneList<Statement*>* statements) {
for (int i = 0; i < statements->length(); i++) {
- Visit(statements->at(i));
- if (HasStackOverflow() || current_block() == NULL) break;
+ CHECK_ALIVE(Visit(statements->at(i)));
}
}
@@ -2343,10 +2336,12 @@
void HGraphBuilder::VisitBlock(Block* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
BreakAndContinueInfo break_info(stmt);
{ BreakAndContinueScope push(&break_info, this);
- VisitStatements(stmt->statements());
- CHECK_BAILOUT;
+ CHECK_BAILOUT(VisitStatements(stmt->statements()));
}
HBasicBlock* break_block = break_info.break_block();
if (break_block != NULL) {
@@ -2358,15 +2353,24 @@
void HGraphBuilder::VisitExpressionStatement(ExpressionStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
VisitForEffect(stmt->expression());
}
void HGraphBuilder::VisitEmptyStatement(EmptyStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
}
void HGraphBuilder::VisitIfStatement(IfStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
if (stmt->condition()->ToBooleanIsTrue()) {
AddSimulate(stmt->ThenId());
Visit(stmt->then_statement());
@@ -2376,20 +2380,27 @@
} else {
HBasicBlock* cond_true = graph()->CreateBasicBlock();
HBasicBlock* cond_false = graph()->CreateBasicBlock();
- VISIT_FOR_CONTROL(stmt->condition(), cond_true, cond_false);
- cond_true->SetJoinId(stmt->ThenId());
- cond_false->SetJoinId(stmt->ElseId());
+ CHECK_BAILOUT(VisitForControl(stmt->condition(), cond_true, cond_false));
- set_current_block(cond_true);
- Visit(stmt->then_statement());
- CHECK_BAILOUT;
- HBasicBlock* other = current_block();
+ if (cond_true->HasPredecessor()) {
+ cond_true->SetJoinId(stmt->ThenId());
+ set_current_block(cond_true);
+ CHECK_BAILOUT(Visit(stmt->then_statement()));
+ cond_true = current_block();
+ } else {
+ cond_true = NULL;
+ }
- set_current_block(cond_false);
- Visit(stmt->else_statement());
- CHECK_BAILOUT;
+ if (cond_false->HasPredecessor()) {
+ cond_false->SetJoinId(stmt->ElseId());
+ set_current_block(cond_false);
+ CHECK_BAILOUT(Visit(stmt->else_statement()));
+ cond_false = current_block();
+ } else {
+ cond_false = NULL;
+ }
- HBasicBlock* join = CreateJoin(other, current_block(), stmt->id());
+ HBasicBlock* join = CreateJoin(cond_true, cond_false, stmt->id());
set_current_block(join);
}
}
@@ -2427,6 +2438,9 @@
void HGraphBuilder::VisitContinueStatement(ContinueStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
HBasicBlock* continue_block = break_scope()->Get(stmt->target(), CONTINUE);
current_block()->Goto(continue_block);
set_current_block(NULL);
@@ -2434,6 +2448,9 @@
void HGraphBuilder::VisitBreakStatement(BreakStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
HBasicBlock* break_block = break_scope()->Get(stmt->target(), BREAK);
current_block()->Goto(break_block);
set_current_block(NULL);
@@ -2441,12 +2458,15 @@
void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
AstContext* context = call_context();
if (context == NULL) {
// Not an inlined return, so an actual one.
- VISIT_FOR_VALUE(stmt->expression());
+ CHECK_ALIVE(VisitForValue(stmt->expression()));
HValue* result = environment()->Pop();
- current_block()->FinishExit(new HReturn(result));
+ current_block()->FinishExit(new(zone()) HReturn(result));
set_current_block(NULL);
} else {
// Return from an inlined function, visit the subexpression in the
@@ -2457,11 +2477,11 @@
test->if_true(),
test->if_false());
} else if (context->IsEffect()) {
- VISIT_FOR_EFFECT(stmt->expression());
+ CHECK_ALIVE(VisitForEffect(stmt->expression()));
current_block()->Goto(function_return(), false);
} else {
ASSERT(context->IsValue());
- VISIT_FOR_VALUE(stmt->expression());
+ CHECK_ALIVE(VisitForValue(stmt->expression()));
HValue* return_value = environment()->Pop();
current_block()->AddLeaveInlined(return_value, function_return());
}
@@ -2471,26 +2491,35 @@
void HGraphBuilder::VisitWithEnterStatement(WithEnterStatement* stmt) {
- BAILOUT("WithEnterStatement");
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout("WithEnterStatement");
}
void HGraphBuilder::VisitWithExitStatement(WithExitStatement* stmt) {
- BAILOUT("WithExitStatement");
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout("WithExitStatement");
}
void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
// We only optimize switch statements with smi-literal smi comparisons,
// with a bounded number of clauses.
const int kCaseClauseLimit = 128;
ZoneList<CaseClause*>* clauses = stmt->cases();
int clause_count = clauses->length();
if (clause_count > kCaseClauseLimit) {
- BAILOUT("SwitchStatement: too many clauses");
+ return Bailout("SwitchStatement: too many clauses");
}
- VISIT_FOR_VALUE(stmt->tag());
+ CHECK_ALIVE(VisitForValue(stmt->tag()));
AddSimulate(stmt->EntryId());
HValue* tag_value = Pop();
HBasicBlock* first_test_block = current_block();
@@ -2501,7 +2530,7 @@
CaseClause* clause = clauses->at(i);
if (clause->is_default()) continue;
if (!clause->label()->IsSmiLiteral()) {
- BAILOUT("SwitchStatement: non-literal switch label");
+ return Bailout("SwitchStatement: non-literal switch label");
}
// Unconditionally deoptimize on the first non-smi compare.
@@ -2513,15 +2542,16 @@
}
// Otherwise generate a compare and branch.
- VISIT_FOR_VALUE(clause->label());
+ CHECK_ALIVE(VisitForValue(clause->label()));
HValue* label_value = Pop();
- HCompare* compare = new HCompare(tag_value, label_value, Token::EQ_STRICT);
+ HCompare* compare =
+ new(zone()) HCompare(tag_value, label_value, Token::EQ_STRICT);
compare->SetInputRepresentation(Representation::Integer32());
ASSERT(!compare->HasSideEffects());
AddInstruction(compare);
HBasicBlock* body_block = graph()->CreateBasicBlock();
HBasicBlock* next_test_block = graph()->CreateBasicBlock();
- HTest* branch = new HTest(compare, body_block, next_test_block);
+ HTest* branch = new(zone()) HTest(compare, body_block, next_test_block);
current_block()->Finish(branch);
set_current_block(next_test_block);
}
@@ -2574,8 +2604,7 @@
set_current_block(join);
}
- VisitStatements(clause->statements());
- CHECK_BAILOUT;
+ CHECK_BAILOUT(VisitStatements(clause->statements()));
fall_through_block = current_block();
}
}
@@ -2607,7 +2636,7 @@
HBasicBlock* non_osr_entry = graph()->CreateBasicBlock();
HBasicBlock* osr_entry = graph()->CreateBasicBlock();
HValue* true_value = graph()->GetConstantTrue();
- HTest* test = new HTest(true_value, non_osr_entry, osr_entry);
+ HTest* test = new(zone()) HTest(true_value, non_osr_entry, osr_entry);
current_block()->Finish(test);
HBasicBlock* loop_predecessor = graph()->CreateBasicBlock();
@@ -2621,13 +2650,13 @@
ASSERT(count ==
(environment()->parameter_count() + environment()->local_count()));
for (int i = 0; i < count; ++i) {
- HUnknownOSRValue* unknown = new HUnknownOSRValue;
+ HUnknownOSRValue* unknown = new(zone()) HUnknownOSRValue;
AddInstruction(unknown);
environment()->Bind(i, unknown);
}
AddSimulate(osr_entry_id);
- AddInstruction(new HOsrEntry(osr_entry_id));
+ AddInstruction(new(zone()) HOsrEntry(osr_entry_id));
current_block()->Goto(loop_predecessor);
loop_predecessor->SetJoinId(statement->EntryId());
set_current_block(loop_predecessor);
@@ -2635,6 +2664,9 @@
void HGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
ASSERT(current_block() != NULL);
PreProcessOsrEntry(stmt);
HBasicBlock* loop_entry = CreateLoopHeaderBlock();
@@ -2643,8 +2675,7 @@
BreakAndContinueInfo break_info(stmt);
{ BreakAndContinueScope push(&break_info, this);
- Visit(stmt->body());
- CHECK_BAILOUT;
+ CHECK_BAILOUT(Visit(stmt->body()));
}
HBasicBlock* body_exit =
JoinContinue(stmt, current_block(), break_info.continue_block());
@@ -2655,9 +2686,17 @@
// back edge.
body_exit = graph()->CreateBasicBlock();
loop_successor = graph()->CreateBasicBlock();
- VISIT_FOR_CONTROL(stmt->cond(), body_exit, loop_successor);
- body_exit->SetJoinId(stmt->BackEdgeId());
- loop_successor->SetJoinId(stmt->ExitId());
+ CHECK_BAILOUT(VisitForControl(stmt->cond(), body_exit, loop_successor));
+ if (body_exit->HasPredecessor()) {
+ body_exit->SetJoinId(stmt->BackEdgeId());
+ } else {
+ body_exit = NULL;
+ }
+ if (loop_successor->HasPredecessor()) {
+ loop_successor->SetJoinId(stmt->ExitId());
+ } else {
+ loop_successor = NULL;
+ }
}
HBasicBlock* loop_exit = CreateLoop(stmt,
loop_entry,
@@ -2669,6 +2708,9 @@
void HGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
ASSERT(current_block() != NULL);
PreProcessOsrEntry(stmt);
HBasicBlock* loop_entry = CreateLoopHeaderBlock();
@@ -2680,16 +2722,22 @@
if (!stmt->cond()->ToBooleanIsTrue()) {
HBasicBlock* body_entry = graph()->CreateBasicBlock();
loop_successor = graph()->CreateBasicBlock();
- VISIT_FOR_CONTROL(stmt->cond(), body_entry, loop_successor);
- body_entry->SetJoinId(stmt->BodyId());
- loop_successor->SetJoinId(stmt->ExitId());
- set_current_block(body_entry);
+ CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
+ if (body_entry->HasPredecessor()) {
+ body_entry->SetJoinId(stmt->BodyId());
+ set_current_block(body_entry);
+ }
+ if (loop_successor->HasPredecessor()) {
+ loop_successor->SetJoinId(stmt->ExitId());
+ } else {
+ loop_successor = NULL;
+ }
}
BreakAndContinueInfo break_info(stmt);
- { BreakAndContinueScope push(&break_info, this);
- Visit(stmt->body());
- CHECK_BAILOUT;
+ if (current_block() != NULL) {
+ BreakAndContinueScope push(&break_info, this);
+ CHECK_BAILOUT(Visit(stmt->body()));
}
HBasicBlock* body_exit =
JoinContinue(stmt, current_block(), break_info.continue_block());
@@ -2703,9 +2751,11 @@
void HGraphBuilder::VisitForStatement(ForStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
if (stmt->init() != NULL) {
- Visit(stmt->init());
- CHECK_BAILOUT;
+ CHECK_ALIVE(Visit(stmt->init()));
}
ASSERT(current_block() != NULL);
PreProcessOsrEntry(stmt);
@@ -2717,24 +2767,29 @@
if (stmt->cond() != NULL) {
HBasicBlock* body_entry = graph()->CreateBasicBlock();
loop_successor = graph()->CreateBasicBlock();
- VISIT_FOR_CONTROL(stmt->cond(), body_entry, loop_successor);
- body_entry->SetJoinId(stmt->BodyId());
- loop_successor->SetJoinId(stmt->ExitId());
- set_current_block(body_entry);
+ CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
+ if (body_entry->HasPredecessor()) {
+ body_entry->SetJoinId(stmt->BodyId());
+ set_current_block(body_entry);
+ }
+ if (loop_successor->HasPredecessor()) {
+ loop_successor->SetJoinId(stmt->ExitId());
+ } else {
+ loop_successor = NULL;
+ }
}
BreakAndContinueInfo break_info(stmt);
- { BreakAndContinueScope push(&break_info, this);
- Visit(stmt->body());
- CHECK_BAILOUT;
+ if (current_block() != NULL) {
+ BreakAndContinueScope push(&break_info, this);
+ CHECK_BAILOUT(Visit(stmt->body()));
}
HBasicBlock* body_exit =
JoinContinue(stmt, current_block(), break_info.continue_block());
if (stmt->next() != NULL && body_exit != NULL) {
set_current_block(body_exit);
- Visit(stmt->next());
- CHECK_BAILOUT;
+ CHECK_BAILOUT(Visit(stmt->next()));
body_exit = current_block();
}
@@ -2748,100 +2803,147 @@
void HGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
- BAILOUT("ForInStatement");
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout("ForInStatement");
}
void HGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
- BAILOUT("TryCatchStatement");
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout("TryCatchStatement");
}
void HGraphBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
- BAILOUT("TryFinallyStatement");
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout("TryFinallyStatement");
}
void HGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) {
- BAILOUT("DebuggerStatement");
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout("DebuggerStatement");
+}
+
+
+static Handle<SharedFunctionInfo> SearchSharedFunctionInfo(
+ Code* unoptimized_code, FunctionLiteral* expr) {
+ int start_position = expr->start_position();
+ RelocIterator it(unoptimized_code);
+ for (;!it.done(); it.next()) {
+ RelocInfo* rinfo = it.rinfo();
+ if (rinfo->rmode() != RelocInfo::EMBEDDED_OBJECT) continue;
+ Object* obj = rinfo->target_object();
+ if (obj->IsSharedFunctionInfo()) {
+ SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
+ if (shared->start_position() == start_position) {
+ return Handle<SharedFunctionInfo>(shared);
+ }
+ }
+ }
+
+ return Handle<SharedFunctionInfo>();
}
void HGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
Handle<SharedFunctionInfo> shared_info =
- Compiler::BuildFunctionInfo(expr, info()->script());
- CHECK_BAILOUT;
+ SearchSharedFunctionInfo(info()->shared_info()->code(),
+ expr);
+ if (shared_info.is_null()) {
+ shared_info = Compiler::BuildFunctionInfo(expr, info()->script());
+ }
+ // We also have a stack overflow if the recursive compilation did.
+ if (HasStackOverflow()) return;
HFunctionLiteral* instr =
- new HFunctionLiteral(shared_info, expr->pretenure());
+ new(zone()) HFunctionLiteral(shared_info, expr->pretenure());
ast_context()->ReturnInstruction(instr, expr->id());
}
void HGraphBuilder::VisitSharedFunctionInfoLiteral(
SharedFunctionInfoLiteral* expr) {
- BAILOUT("SharedFunctionInfoLiteral");
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout("SharedFunctionInfoLiteral");
}
void HGraphBuilder::VisitConditional(Conditional* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
HBasicBlock* cond_true = graph()->CreateBasicBlock();
HBasicBlock* cond_false = graph()->CreateBasicBlock();
- VISIT_FOR_CONTROL(expr->condition(), cond_true, cond_false);
- cond_true->SetJoinId(expr->ThenId());
- cond_false->SetJoinId(expr->ElseId());
+ CHECK_BAILOUT(VisitForControl(expr->condition(), cond_true, cond_false));
// Visit the true and false subexpressions in the same AST context as the
// whole expression.
- set_current_block(cond_true);
- Visit(expr->then_expression());
- CHECK_BAILOUT;
- HBasicBlock* other = current_block();
+ if (cond_true->HasPredecessor()) {
+ cond_true->SetJoinId(expr->ThenId());
+ set_current_block(cond_true);
+ CHECK_BAILOUT(Visit(expr->then_expression()));
+ cond_true = current_block();
+ } else {
+ cond_true = NULL;
+ }
- set_current_block(cond_false);
- Visit(expr->else_expression());
- CHECK_BAILOUT;
+ if (cond_false->HasPredecessor()) {
+ cond_false->SetJoinId(expr->ElseId());
+ set_current_block(cond_false);
+ CHECK_BAILOUT(Visit(expr->else_expression()));
+ cond_false = current_block();
+ } else {
+ cond_false = NULL;
+ }
if (!ast_context()->IsTest()) {
- HBasicBlock* join = CreateJoin(other, current_block(), expr->id());
+ HBasicBlock* join = CreateJoin(cond_true, cond_false, expr->id());
set_current_block(join);
- if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop());
+ if (join != NULL && !ast_context()->IsEffect()) {
+ ast_context()->ReturnValue(Pop());
+ }
}
}
-void HGraphBuilder::LookupGlobalPropertyCell(Variable* var,
- LookupResult* lookup,
- bool is_store) {
- if (var->is_this()) {
- BAILOUT("global this reference");
- }
- if (!info()->has_global_object()) {
- BAILOUT("no global object to optimize VariableProxy");
+HGraphBuilder::GlobalPropertyAccess HGraphBuilder::LookupGlobalProperty(
+ Variable* var, LookupResult* lookup, bool is_store) {
+ if (var->is_this() || !info()->has_global_object()) {
+ return kUseGeneric;
}
Handle<GlobalObject> global(info()->global_object());
global->Lookup(*var->name(), lookup);
- if (!lookup->IsProperty()) {
- BAILOUT("global variable cell not yet introduced");
+ if (!lookup->IsProperty() ||
+ lookup->type() != NORMAL ||
+ (is_store && lookup->IsReadOnly()) ||
+ lookup->holder() != *global) {
+ return kUseGeneric;
}
- if (lookup->type() != NORMAL) {
- BAILOUT("global variable has accessors");
- }
- if (is_store && lookup->IsReadOnly()) {
- BAILOUT("read-only global variable");
- }
- if (lookup->holder() != *global) {
- BAILOUT("global property on prototype of global object");
- }
+
+ return kUseCell;
}
HValue* HGraphBuilder::BuildContextChainWalk(Variable* var) {
ASSERT(var->IsContextSlot());
- HInstruction* context = new HContext;
+ HInstruction* context = new(zone()) HContext;
AddInstruction(context);
int length = info()->scope()->ContextChainLength(var->scope());
while (length-- > 0) {
- context = new HOuterContext(context);
+ context = new(zone()) HOuterContext(context);
AddInstruction(context);
}
return context;
@@ -2849,66 +2951,94 @@
void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
Variable* variable = expr->AsVariable();
if (variable == NULL) {
- BAILOUT("reference to rewritten variable");
+ return Bailout("reference to rewritten variable");
} else if (variable->IsStackAllocated()) {
if (environment()->Lookup(variable)->CheckFlag(HValue::kIsArguments)) {
- BAILOUT("unsupported context for arguments object");
+ return Bailout("unsupported context for arguments object");
}
ast_context()->ReturnValue(environment()->Lookup(variable));
} else if (variable->IsContextSlot()) {
if (variable->mode() == Variable::CONST) {
- BAILOUT("reference to const context slot");
+ return Bailout("reference to const context slot");
}
HValue* context = BuildContextChainWalk(variable);
int index = variable->AsSlot()->index();
- HLoadContextSlot* instr = new HLoadContextSlot(context, index);
+ HLoadContextSlot* instr = new(zone()) HLoadContextSlot(context, index);
ast_context()->ReturnInstruction(instr, expr->id());
} else if (variable->is_global()) {
LookupResult lookup;
- LookupGlobalPropertyCell(variable, &lookup, false);
- CHECK_BAILOUT;
+ GlobalPropertyAccess type = LookupGlobalProperty(variable, &lookup, false);
- Handle<GlobalObject> global(info()->global_object());
- // TODO(3039103): Handle global property load through an IC call when access
- // checks are enabled.
- if (global->IsAccessCheckNeeded()) {
- BAILOUT("global object requires access check");
+ if (type == kUseCell &&
+ info()->global_object()->IsAccessCheckNeeded()) {
+ type = kUseGeneric;
}
- Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
- bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly();
- HLoadGlobal* instr = new HLoadGlobal(cell, check_hole);
- ast_context()->ReturnInstruction(instr, expr->id());
+
+ if (type == kUseCell) {
+ Handle<GlobalObject> global(info()->global_object());
+ Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
+ bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly();
+ HLoadGlobalCell* instr = new(zone()) HLoadGlobalCell(cell, check_hole);
+ ast_context()->ReturnInstruction(instr, expr->id());
+ } else {
+ HContext* context = new(zone()) HContext;
+ AddInstruction(context);
+ HGlobalObject* global_object = new(zone()) HGlobalObject(context);
+ AddInstruction(global_object);
+ HLoadGlobalGeneric* instr =
+ new(zone()) HLoadGlobalGeneric(context,
+ global_object,
+ variable->name(),
+ ast_context()->is_for_typeof());
+ instr->set_position(expr->position());
+ ASSERT(instr->HasSideEffects());
+ ast_context()->ReturnInstruction(instr, expr->id());
+ }
} else {
- BAILOUT("reference to a variable which requires dynamic lookup");
+ return Bailout("reference to a variable which requires dynamic lookup");
}
}
void HGraphBuilder::VisitLiteral(Literal* expr) {
- HConstant* instr = new HConstant(expr->handle(), Representation::Tagged());
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ HConstant* instr =
+ new(zone()) HConstant(expr->handle(), Representation::Tagged());
ast_context()->ReturnInstruction(instr, expr->id());
}
void HGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
- HRegExpLiteral* instr = new HRegExpLiteral(expr->pattern(),
- expr->flags(),
- expr->literal_index());
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ HRegExpLiteral* instr = new(zone()) HRegExpLiteral(expr->pattern(),
+ expr->flags(),
+ expr->literal_index());
ast_context()->ReturnInstruction(instr, expr->id());
}
void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
- HContext* context = new HContext;
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
- HObjectLiteral* literal = (new HObjectLiteral(context,
- expr->constant_properties(),
- expr->fast_elements(),
- expr->literal_index(),
- expr->depth(),
- expr->has_function()));
+ HObjectLiteral* literal =
+ new(zone()) HObjectLiteral(context,
+ expr->constant_properties(),
+ expr->fast_elements(),
+ expr->literal_index(),
+ expr->depth(),
+ expr->has_function());
// The object is expected in the bailout environment during computation
// of the property values and is the value of the entire expression.
PushAndAdd(literal);
@@ -2929,15 +3059,20 @@
case ObjectLiteral::Property::COMPUTED:
if (key->handle()->IsSymbol()) {
if (property->emit_store()) {
- VISIT_FOR_VALUE(value);
+ CHECK_ALIVE(VisitForValue(value));
HValue* value = Pop();
Handle<String> name = Handle<String>::cast(key->handle());
HStoreNamedGeneric* store =
- new HStoreNamedGeneric(context, literal, name, value);
+ new(zone()) HStoreNamedGeneric(
+ context,
+ literal,
+ name,
+ value,
+ function_strict_mode());
AddInstruction(store);
AddSimulate(key->id());
} else {
- VISIT_FOR_EFFECT(value);
+ CHECK_ALIVE(VisitForEffect(value));
}
break;
}
@@ -2945,7 +3080,7 @@
case ObjectLiteral::Property::PROTOTYPE:
case ObjectLiteral::Property::SETTER:
case ObjectLiteral::Property::GETTER:
- BAILOUT("Object literal with complex property");
+ return Bailout("Object literal with complex property");
default: UNREACHABLE();
}
}
@@ -2956,7 +3091,7 @@
// of the object. This makes sure that the original object won't
// be used by other optimized code before it is transformed
// (e.g. because of code motion).
- HToFastProperties* result = new HToFastProperties(Pop());
+ HToFastProperties* result = new(zone()) HToFastProperties(Pop());
AddInstruction(result);
ast_context()->ReturnValue(result);
} else {
@@ -2966,13 +3101,16 @@
void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
ZoneList<Expression*>* subexprs = expr->values();
int length = subexprs->length();
- HArrayLiteral* literal = new HArrayLiteral(expr->constant_elements(),
- length,
- expr->literal_index(),
- expr->depth());
+ HArrayLiteral* literal = new(zone()) HArrayLiteral(expr->constant_elements(),
+ length,
+ expr->literal_index(),
+ expr->depth());
// The array is expected in the bailout environment during computation
// of the property values and is the value of the entire expression.
PushAndAdd(literal);
@@ -2985,19 +3123,20 @@
// is already set in the cloned array.
if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
- VISIT_FOR_VALUE(subexpr);
+ CHECK_ALIVE(VisitForValue(subexpr));
HValue* value = Pop();
- if (!Smi::IsValid(i)) BAILOUT("Non-smi key in array literal");
+ if (!Smi::IsValid(i)) return Bailout("Non-smi key in array literal");
// Load the elements array before the first store.
if (elements == NULL) {
- elements = new HLoadElements(literal);
+ elements = new(zone()) HLoadElements(literal);
AddInstruction(elements);
}
- HValue* key = AddInstruction(new HConstant(Handle<Object>(Smi::FromInt(i)),
- Representation::Integer32()));
- AddInstruction(new HStoreKeyedFastElement(elements, key, value));
+ HValue* key = AddInstruction(
+ new(zone()) HConstant(Handle<Object>(Smi::FromInt(i)),
+ Representation::Integer32()));
+ AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value));
AddSimulate(expr->GetIdForElement(i));
}
ast_context()->ReturnValue(Pop());
@@ -3005,7 +3144,10 @@
void HGraphBuilder::VisitCatchExtensionObject(CatchExtensionObject* expr) {
- BAILOUT("CatchExtensionObject");
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout("CatchExtensionObject");
}
@@ -3041,8 +3183,8 @@
LookupResult* lookup,
bool smi_and_map_check) {
if (smi_and_map_check) {
- AddInstruction(new HCheckNonSmi(object));
- AddInstruction(new HCheckMap(object, type));
+ AddInstruction(new(zone()) HCheckNonSmi(object));
+ AddInstruction(new(zone()) HCheckMap(object, type));
}
int index = ComputeStoredFieldIndex(type, name, lookup);
@@ -3056,7 +3198,7 @@
offset += FixedArray::kHeaderSize;
}
HStoreNamedField* instr =
- new HStoreNamedField(object, name, value, is_in_object, offset);
+ new(zone()) HStoreNamedField(object, name, value, is_in_object, offset);
if (lookup->type() == MAP_TRANSITION) {
Handle<Map> transition(lookup->GetTransitionMapFromMap(*type));
instr->set_transition(transition);
@@ -3071,9 +3213,14 @@
HInstruction* HGraphBuilder::BuildStoreNamedGeneric(HValue* object,
Handle<String> name,
HValue* value) {
- HContext* context = new HContext;
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
- return new HStoreNamedGeneric(context, object, name, value);
+ return new(zone()) HStoreNamedGeneric(
+ context,
+ object,
+ name,
+ value,
+ function_strict_mode());
}
@@ -3114,13 +3261,14 @@
LookupResult lookup;
if (ComputeStoredField(map, name, &lookup)) {
if (count == 0) {
- AddInstruction(new HCheckNonSmi(object)); // Only needed once.
+ AddInstruction(new(zone()) HCheckNonSmi(object)); // Only needed once.
join = graph()->CreateBasicBlock();
}
++count;
HBasicBlock* if_true = graph()->CreateBasicBlock();
HBasicBlock* if_false = graph()->CreateBasicBlock();
- HCompareMap* compare = new HCompareMap(object, map, if_true, if_false);
+ HCompareMap* compare =
+ new(zone()) HCompareMap(object, map, if_true, if_false);
current_block()->Finish(compare);
set_current_block(if_true);
@@ -3178,14 +3326,14 @@
Property* prop = expr->target()->AsProperty();
ASSERT(prop != NULL);
expr->RecordTypeFeedback(oracle());
- VISIT_FOR_VALUE(prop->obj());
+ CHECK_ALIVE(VisitForValue(prop->obj()));
HValue* value = NULL;
HInstruction* instr = NULL;
if (prop->key()->IsPropertyName()) {
// Named store.
- VISIT_FOR_VALUE(expr->value());
+ CHECK_ALIVE(VisitForValue(expr->value()));
value = Pop();
HValue* object = Pop();
@@ -3209,31 +3357,13 @@
} else {
// Keyed store.
- VISIT_FOR_VALUE(prop->key());
- VISIT_FOR_VALUE(expr->value());
+ CHECK_ALIVE(VisitForValue(prop->key()));
+ CHECK_ALIVE(VisitForValue(expr->value()));
value = Pop();
HValue* key = Pop();
HValue* object = Pop();
-
- if (expr->IsMonomorphic()) {
- Handle<Map> receiver_type(expr->GetMonomorphicReceiverType());
- // An object has either fast elements or external array elements, but
- // never both. Pixel array maps that are assigned to pixel array elements
- // are always created with the fast elements flag cleared.
- if (receiver_type->has_external_array_elements()) {
- instr = BuildStoreKeyedSpecializedArrayElement(object,
- key,
- value,
- expr);
- } else if (receiver_type->has_fast_elements()) {
- instr = BuildStoreKeyedFastElement(object, key, value, expr);
- }
- }
- if (instr == NULL) {
- instr = BuildStoreKeyedGeneric(object, key, value);
- }
+ instr = BuildStoreKeyed(object, key, value, expr);
}
-
Push(value);
instr->set_position(expr->position());
AddInstruction(instr);
@@ -3250,16 +3380,31 @@
int position,
int ast_id) {
LookupResult lookup;
- LookupGlobalPropertyCell(var, &lookup, true);
- CHECK_BAILOUT;
-
- bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly();
- Handle<GlobalObject> global(info()->global_object());
- Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
- HInstruction* instr = new HStoreGlobal(value, cell, check_hole);
- instr->set_position(position);
- AddInstruction(instr);
- if (instr->HasSideEffects()) AddSimulate(ast_id);
+ GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, true);
+ if (type == kUseCell) {
+ bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly();
+ Handle<GlobalObject> global(info()->global_object());
+ Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup));
+ HInstruction* instr = new(zone()) HStoreGlobalCell(value, cell, check_hole);
+ instr->set_position(position);
+ AddInstruction(instr);
+ if (instr->HasSideEffects()) AddSimulate(ast_id);
+ } else {
+ HContext* context = new(zone()) HContext;
+ AddInstruction(context);
+ HGlobalObject* global_object = new(zone()) HGlobalObject(context);
+ AddInstruction(global_object);
+ HStoreGlobalGeneric* instr =
+ new(zone()) HStoreGlobalGeneric(context,
+ global_object,
+ var->name(),
+ value,
+ function_strict_mode());
+ instr->set_position(position);
+ AddInstruction(instr);
+ ASSERT(instr->HasSideEffects());
+ if (instr->HasSideEffects()) AddSimulate(ast_id);
+ }
}
@@ -3275,7 +3420,7 @@
BinaryOperation* operation = expr->binary_operation();
if (var != NULL) {
- VISIT_FOR_VALUE(operation);
+ CHECK_ALIVE(VisitForValue(operation));
if (var->is_global()) {
HandleGlobalVariableAssignment(var,
@@ -3287,11 +3432,12 @@
} else if (var->IsContextSlot()) {
HValue* context = BuildContextChainWalk(var);
int index = var->AsSlot()->index();
- HStoreContextSlot* instr = new HStoreContextSlot(context, index, Top());
+ HStoreContextSlot* instr =
+ new(zone()) HStoreContextSlot(context, index, Top());
AddInstruction(instr);
if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId());
} else {
- BAILOUT("compound assignment to lookup slot");
+ return Bailout("compound assignment to lookup slot");
}
ast_context()->ReturnValue(Pop());
@@ -3300,7 +3446,7 @@
if (prop->key()->IsPropertyName()) {
// Named property.
- VISIT_FOR_VALUE(prop->obj());
+ CHECK_ALIVE(VisitForValue(prop->obj()));
HValue* obj = Top();
HInstruction* load = NULL;
@@ -3314,7 +3460,7 @@
PushAndAdd(load);
if (load->HasSideEffects()) AddSimulate(expr->CompoundLoadId());
- VISIT_FOR_VALUE(expr->value());
+ CHECK_ALIVE(VisitForValue(expr->value()));
HValue* right = Pop();
HValue* left = Pop();
@@ -3332,20 +3478,16 @@
} else {
// Keyed property.
- VISIT_FOR_VALUE(prop->obj());
- VISIT_FOR_VALUE(prop->key());
+ CHECK_ALIVE(VisitForValue(prop->obj()));
+ CHECK_ALIVE(VisitForValue(prop->key()));
HValue* obj = environment()->ExpressionStackAt(1);
HValue* key = environment()->ExpressionStackAt(0);
- bool is_fast_elements = prop->IsMonomorphic() &&
- prop->GetMonomorphicReceiverType()->has_fast_elements();
- HInstruction* load = is_fast_elements
- ? BuildLoadKeyedFastElement(obj, key, prop)
- : BuildLoadKeyedGeneric(obj, key);
+ HInstruction* load = BuildLoadKeyed(obj, key, prop);
PushAndAdd(load);
if (load->HasSideEffects()) AddSimulate(expr->CompoundLoadId());
- VISIT_FOR_VALUE(expr->value());
+ CHECK_ALIVE(VisitForValue(expr->value()));
HValue* right = Pop();
HValue* left = Pop();
@@ -3353,9 +3495,8 @@
PushAndAdd(instr);
if (instr->HasSideEffects()) AddSimulate(operation->id());
- HInstruction* store = is_fast_elements
- ? BuildStoreKeyedFastElement(obj, key, instr, prop)
- : BuildStoreKeyedGeneric(obj, key, instr);
+ expr->RecordTypeFeedback(oracle());
+ HInstruction* store = BuildStoreKeyed(obj, key, instr, expr);
AddInstruction(store);
// Drop the simulated receiver, key, and value. Return the value.
Drop(3);
@@ -3365,12 +3506,15 @@
}
} else {
- BAILOUT("invalid lhs in compound assignment");
+ return Bailout("invalid lhs in compound assignment");
}
}
void HGraphBuilder::VisitAssignment(Assignment* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
VariableProxy* proxy = expr->target()->AsVariableProxy();
Variable* var = proxy->AsVariable();
Property* prop = expr->target()->AsProperty();
@@ -3382,7 +3526,7 @@
}
if (var != NULL) {
- if (proxy->IsArguments()) BAILOUT("assignment to arguments");
+ if (proxy->IsArguments()) return Bailout("assignment to arguments");
// Handle the assignment.
if (var->IsStackAllocated()) {
@@ -3396,23 +3540,24 @@
if (rhs_var != NULL && rhs_var->IsStackAllocated()) {
value = environment()->Lookup(rhs_var);
} else {
- VISIT_FOR_VALUE(expr->value());
+ CHECK_ALIVE(VisitForValue(expr->value()));
value = Pop();
}
Bind(var, value);
ast_context()->ReturnValue(value);
} else if (var->IsContextSlot() && var->mode() != Variable::CONST) {
- VISIT_FOR_VALUE(expr->value());
+ CHECK_ALIVE(VisitForValue(expr->value()));
HValue* context = BuildContextChainWalk(var);
int index = var->AsSlot()->index();
- HStoreContextSlot* instr = new HStoreContextSlot(context, index, Top());
+ HStoreContextSlot* instr =
+ new(zone()) HStoreContextSlot(context, index, Top());
AddInstruction(instr);
if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId());
ast_context()->ReturnValue(Pop());
} else if (var->is_global()) {
- VISIT_FOR_VALUE(expr->value());
+ CHECK_ALIVE(VisitForValue(expr->value()));
HandleGlobalVariableAssignment(var,
Top(),
expr->position(),
@@ -3420,30 +3565,33 @@
ast_context()->ReturnValue(Pop());
} else {
- BAILOUT("assignment to LOOKUP or const CONTEXT variable");
+ return Bailout("assignment to LOOKUP or const CONTEXT variable");
}
} else if (prop != NULL) {
HandlePropertyAssignment(expr);
} else {
- BAILOUT("invalid left-hand side in assignment");
+ return Bailout("invalid left-hand side in assignment");
}
}
void HGraphBuilder::VisitThrow(Throw* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
// We don't optimize functions with invalid left-hand sides in
// assignments, count operations, or for-in. Consequently throw can
// currently only occur in an effect context.
ASSERT(ast_context()->IsEffect());
- VISIT_FOR_VALUE(expr->exception());
+ CHECK_ALIVE(VisitForValue(expr->exception()));
HValue* value = environment()->Pop();
- HThrow* instr = new HThrow(value);
+ HThrow* instr = new(zone()) HThrow(value);
instr->set_position(expr->position());
AddInstruction(instr);
AddSimulate(expr->id());
- current_block()->FinishExit(new HAbnormalExit);
+ current_block()->FinishExit(new(zone()) HAbnormalExit);
set_current_block(NULL);
}
@@ -3454,8 +3602,8 @@
LookupResult* lookup,
bool smi_and_map_check) {
if (smi_and_map_check) {
- AddInstruction(new HCheckNonSmi(object));
- AddInstruction(new HCheckMap(object, type));
+ AddInstruction(new(zone()) HCheckNonSmi(object));
+ AddInstruction(new(zone()) HCheckMap(object, type));
}
int index = lookup->GetLocalFieldIndexFromMap(*type);
@@ -3463,11 +3611,11 @@
// Negative property indices are in-object properties, indexed
// from the end of the fixed part of the object.
int offset = (index * kPointerSize) + type->instance_size();
- return new HLoadNamedField(object, true, offset);
+ return new(zone()) HLoadNamedField(object, true, offset);
} else {
// Non-negative property indices are in the properties array.
int offset = (index * kPointerSize) + FixedArray::kHeaderSize;
- return new HLoadNamedField(object, false, offset);
+ return new(zone()) HLoadNamedField(object, false, offset);
}
}
@@ -3476,9 +3624,9 @@
Property* expr) {
ASSERT(expr->key()->IsPropertyName());
Handle<Object> name = expr->key()->AsLiteral()->handle();
- HContext* context = new HContext;
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
- return new HLoadNamedGeneric(context, obj, name);
+ return new(zone()) HLoadNamedGeneric(context, obj, name);
}
@@ -3495,10 +3643,10 @@
&lookup,
true);
} else if (lookup.IsProperty() && lookup.type() == CONSTANT_FUNCTION) {
- AddInstruction(new HCheckNonSmi(obj));
- AddInstruction(new HCheckMap(obj, map));
+ AddInstruction(new(zone()) HCheckNonSmi(obj));
+ AddInstruction(new(zone()) HCheckMap(obj, map));
Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*map));
- return new HConstant(function, Representation::Tagged());
+ return new(zone()) HConstant(function, Representation::Tagged());
} else {
return BuildLoadNamedGeneric(obj, expr);
}
@@ -3507,9 +3655,9 @@
HInstruction* HGraphBuilder::BuildLoadKeyedGeneric(HValue* object,
HValue* key) {
- HContext* context = new HContext;
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
- return new HLoadKeyedGeneric(context, object, key);
+ return new(zone()) HLoadKeyedGeneric(context, object, key);
}
@@ -3517,23 +3665,23 @@
HValue* key,
Property* expr) {
ASSERT(!expr->key()->IsPropertyName() && expr->IsMonomorphic());
- AddInstruction(new HCheckNonSmi(object));
+ AddInstruction(new(zone()) HCheckNonSmi(object));
Handle<Map> map = expr->GetMonomorphicReceiverType();
ASSERT(map->has_fast_elements());
- AddInstruction(new HCheckMap(object, map));
+ AddInstruction(new(zone()) HCheckMap(object, map));
bool is_array = (map->instance_type() == JS_ARRAY_TYPE);
- HLoadElements* elements = new HLoadElements(object);
+ HLoadElements* elements = new(zone()) HLoadElements(object);
HInstruction* length = NULL;
if (is_array) {
- length = AddInstruction(new HJSArrayLength(object));
- AddInstruction(new HBoundsCheck(key, length));
+ length = AddInstruction(new(zone()) HJSArrayLength(object));
+ AddInstruction(new(zone()) HBoundsCheck(key, length));
AddInstruction(elements);
} else {
AddInstruction(elements);
- length = AddInstruction(new HFixedArrayLength(elements));
- AddInstruction(new HBoundsCheck(key, length));
+ length = AddInstruction(new(zone()) HFixedArrayLength(elements));
+ AddInstruction(new(zone()) HBoundsCheck(key, length));
}
- return new HLoadKeyedFastElement(elements, key);
+ return new(zone()) HLoadKeyedFastElement(elements, key);
}
@@ -3542,33 +3690,55 @@
HValue* key,
Property* expr) {
ASSERT(!expr->key()->IsPropertyName() && expr->IsMonomorphic());
- AddInstruction(new HCheckNonSmi(object));
+ AddInstruction(new(zone()) HCheckNonSmi(object));
Handle<Map> map = expr->GetMonomorphicReceiverType();
ASSERT(!map->has_fast_elements());
ASSERT(map->has_external_array_elements());
- AddInstruction(new HCheckMap(object, map));
- HLoadElements* elements = new HLoadElements(object);
+ AddInstruction(new(zone()) HCheckMap(object, map));
+ HLoadElements* elements = new(zone()) HLoadElements(object);
AddInstruction(elements);
- HInstruction* length = new HExternalArrayLength(elements);
+ HInstruction* length = new(zone()) HExternalArrayLength(elements);
AddInstruction(length);
- AddInstruction(new HBoundsCheck(key, length));
+ AddInstruction(new(zone()) HBoundsCheck(key, length));
HLoadExternalArrayPointer* external_elements =
- new HLoadExternalArrayPointer(elements);
+ new(zone()) HLoadExternalArrayPointer(elements);
AddInstruction(external_elements);
HLoadKeyedSpecializedArrayElement* pixel_array_value =
- new HLoadKeyedSpecializedArrayElement(external_elements,
- key,
- expr->GetExternalArrayType());
+ new(zone()) HLoadKeyedSpecializedArrayElement(
+ external_elements, key, expr->external_array_type());
return pixel_array_value;
}
+HInstruction* HGraphBuilder::BuildLoadKeyed(HValue* obj,
+ HValue* key,
+ Property* prop) {
+ if (prop->IsMonomorphic()) {
+ Handle<Map> receiver_type(prop->GetMonomorphicReceiverType());
+ // An object has either fast elements or pixel array elements, but never
+ // both. Pixel array maps that are assigned to pixel array elements are
+ // always created with the fast elements flag cleared.
+ if (receiver_type->has_external_array_elements()) {
+ return BuildLoadKeyedSpecializedArrayElement(obj, key, prop);
+ } else if (receiver_type->has_fast_elements()) {
+ return BuildLoadKeyedFastElement(obj, key, prop);
+ }
+ }
+ return BuildLoadKeyedGeneric(obj, key);
+}
+
+
HInstruction* HGraphBuilder::BuildStoreKeyedGeneric(HValue* object,
HValue* key,
HValue* value) {
- HContext* context = new HContext;
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
- return new HStoreKeyedGeneric(context, object, key, value);
+ return new(zone()) HStoreKeyedGeneric(
+ context,
+ object,
+ key,
+ value,
+ function_strict_mode());
}
@@ -3577,22 +3747,22 @@
HValue* val,
Expression* expr) {
ASSERT(expr->IsMonomorphic());
- AddInstruction(new HCheckNonSmi(object));
+ AddInstruction(new(zone()) HCheckNonSmi(object));
Handle<Map> map = expr->GetMonomorphicReceiverType();
ASSERT(map->has_fast_elements());
- AddInstruction(new HCheckMap(object, map));
- HInstruction* elements = AddInstruction(new HLoadElements(object));
- AddInstruction(new HCheckMap(elements,
- isolate()->factory()->fixed_array_map()));
+ AddInstruction(new(zone()) HCheckMap(object, map));
+ HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object));
+ AddInstruction(new(zone()) HCheckMap(
+ elements, isolate()->factory()->fixed_array_map()));
bool is_array = (map->instance_type() == JS_ARRAY_TYPE);
HInstruction* length = NULL;
if (is_array) {
- length = AddInstruction(new HJSArrayLength(object));
+ length = AddInstruction(new(zone()) HJSArrayLength(object));
} else {
- length = AddInstruction(new HFixedArrayLength(elements));
+ length = AddInstruction(new(zone()) HFixedArrayLength(elements));
}
- AddInstruction(new HBoundsCheck(key, length));
- return new HStoreKeyedFastElement(elements, key, val);
+ AddInstruction(new(zone()) HBoundsCheck(key, length));
+ return new(zone()) HStoreKeyedFastElement(elements, key, val);
}
@@ -3600,25 +3770,48 @@
HValue* object,
HValue* key,
HValue* val,
- Assignment* expr) {
+ Expression* expr) {
ASSERT(expr->IsMonomorphic());
- AddInstruction(new HCheckNonSmi(object));
+ AddInstruction(new(zone()) HCheckNonSmi(object));
Handle<Map> map = expr->GetMonomorphicReceiverType();
ASSERT(!map->has_fast_elements());
ASSERT(map->has_external_array_elements());
- AddInstruction(new HCheckMap(object, map));
- HLoadElements* elements = new HLoadElements(object);
+ AddInstruction(new(zone()) HCheckMap(object, map));
+ HLoadElements* elements = new(zone()) HLoadElements(object);
AddInstruction(elements);
- HInstruction* length = AddInstruction(new HExternalArrayLength(elements));
- AddInstruction(new HBoundsCheck(key, length));
+ HInstruction* length = AddInstruction(
+ new(zone()) HExternalArrayLength(elements));
+ AddInstruction(new(zone()) HBoundsCheck(key, length));
HLoadExternalArrayPointer* external_elements =
- new HLoadExternalArrayPointer(elements);
+ new(zone()) HLoadExternalArrayPointer(elements);
AddInstruction(external_elements);
- return new HStoreKeyedSpecializedArrayElement(
+ return new(zone()) HStoreKeyedSpecializedArrayElement(
external_elements,
key,
val,
- expr->GetExternalArrayType());
+ expr->external_array_type());
+}
+
+
+HInstruction* HGraphBuilder::BuildStoreKeyed(HValue* object,
+ HValue* key,
+ HValue* value,
+ Expression* expr) {
+ if (expr->IsMonomorphic()) {
+ Handle<Map> receiver_type(expr->GetMonomorphicReceiverType());
+ // An object has either fast elements or external array elements, but
+ // never both. Pixel array maps that are assigned to pixel array elements
+ // are always created with the fast elements flag cleared.
+ if (receiver_type->has_external_array_elements()) {
+ return BuildStoreKeyedSpecializedArrayElement(object,
+ key,
+ value,
+ expr);
+ } else if (receiver_type->has_fast_elements()) {
+ return BuildStoreKeyedFastElement(object, key, value, expr);
+ }
+ }
+ return BuildStoreKeyedGeneric(object, key, value);
}
@@ -3634,18 +3827,19 @@
if (expr->key()->IsPropertyName()) {
Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
if (!name->IsEqualTo(CStrVector("length"))) return false;
- HInstruction* elements = AddInstruction(new HArgumentsElements);
- result = new HArgumentsLength(elements);
+ HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
+ result = new(zone()) HArgumentsLength(elements);
} else {
Push(graph()->GetArgumentsObject());
VisitForValue(expr->key());
- if (HasStackOverflow()) return false;
+ if (HasStackOverflow() || current_block() == NULL) return true;
HValue* key = Pop();
Drop(1); // Arguments object.
- HInstruction* elements = AddInstruction(new HArgumentsElements);
- HInstruction* length = AddInstruction(new HArgumentsLength(elements));
- AddInstruction(new HBoundsCheck(key, length));
- result = new HAccessArgumentsAt(elements, length, key);
+ HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
+ HInstruction* length = AddInstruction(
+ new(zone()) HArgumentsLength(elements));
+ AddInstruction(new(zone()) HBoundsCheck(key, length));
+ result = new(zone()) HAccessArgumentsAt(elements, length, key);
}
ast_context()->ReturnInstruction(result, expr->id());
return true;
@@ -3653,39 +3847,43 @@
void HGraphBuilder::VisitProperty(Property* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
expr->RecordTypeFeedback(oracle());
if (TryArgumentsAccess(expr)) return;
- CHECK_BAILOUT;
- VISIT_FOR_VALUE(expr->obj());
+ CHECK_ALIVE(VisitForValue(expr->obj()));
HInstruction* instr = NULL;
if (expr->IsArrayLength()) {
HValue* array = Pop();
- AddInstruction(new HCheckNonSmi(array));
- AddInstruction(new HCheckInstanceType(array, JS_ARRAY_TYPE, JS_ARRAY_TYPE));
- instr = new HJSArrayLength(array);
+ AddInstruction(new(zone()) HCheckNonSmi(array));
+ AddInstruction(new(zone()) HCheckInstanceType(array,
+ JS_ARRAY_TYPE,
+ JS_ARRAY_TYPE));
+ instr = new(zone()) HJSArrayLength(array);
} else if (expr->IsStringLength()) {
HValue* string = Pop();
- AddInstruction(new HCheckNonSmi(string));
- AddInstruction(new HCheckInstanceType(string,
- FIRST_STRING_TYPE,
- LAST_STRING_TYPE));
- instr = new HStringLength(string);
+ AddInstruction(new(zone()) HCheckNonSmi(string));
+ AddInstruction(new(zone()) HCheckInstanceType(string,
+ FIRST_STRING_TYPE,
+ LAST_STRING_TYPE));
+ instr = new(zone()) HStringLength(string);
} else if (expr->IsStringAccess()) {
- VISIT_FOR_VALUE(expr->key());
+ CHECK_ALIVE(VisitForValue(expr->key()));
HValue* index = Pop();
HValue* string = Pop();
HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
AddInstruction(char_code);
- instr = new HStringCharFromCode(char_code);
+ instr = new(zone()) HStringCharFromCode(char_code);
} else if (expr->IsFunctionPrototype()) {
HValue* function = Pop();
- AddInstruction(new HCheckNonSmi(function));
- instr = new HLoadFunctionPrototype(function);
+ AddInstruction(new(zone()) HCheckNonSmi(function));
+ instr = new(zone()) HLoadFunctionPrototype(function);
} else if (expr->key()->IsPropertyName()) {
Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
@@ -3695,32 +3893,18 @@
if (expr->IsMonomorphic()) {
instr = BuildLoadNamed(obj, expr, types->first(), name);
} else if (types != NULL && types->length() > 1) {
- AddInstruction(new HCheckNonSmi(obj));
- instr = new HLoadNamedFieldPolymorphic(obj, types, name);
+ AddInstruction(new(zone()) HCheckNonSmi(obj));
+ instr = new(zone()) HLoadNamedFieldPolymorphic(obj, types, name);
} else {
instr = BuildLoadNamedGeneric(obj, expr);
}
} else {
- VISIT_FOR_VALUE(expr->key());
+ CHECK_ALIVE(VisitForValue(expr->key()));
HValue* key = Pop();
HValue* obj = Pop();
-
- if (expr->IsMonomorphic()) {
- Handle<Map> receiver_type(expr->GetMonomorphicReceiverType());
- // An object has either fast elements or pixel array elements, but never
- // both. Pixel array maps that are assigned to pixel array elements are
- // always created with the fast elements flag cleared.
- if (receiver_type->has_external_array_elements()) {
- instr = BuildLoadKeyedSpecializedArrayElement(obj, key, expr);
- } else if (receiver_type->has_fast_elements()) {
- instr = BuildLoadKeyedFastElement(obj, key, expr);
- }
- }
- if (instr == NULL) {
- instr = BuildLoadKeyedGeneric(obj, key);
- }
+ instr = BuildLoadKeyed(obj, key, expr);
}
instr->set_position(expr->position());
ast_context()->ReturnInstruction(instr, expr->id());
@@ -3735,11 +3919,11 @@
// are overwritten. Therefore it is enough to check the map of the holder and
// its prototypes.
if (smi_and_map_check) {
- AddInstruction(new HCheckNonSmi(receiver));
- AddInstruction(new HCheckMap(receiver, receiver_map));
+ AddInstruction(new(zone()) HCheckNonSmi(receiver));
+ AddInstruction(new(zone()) HCheckMap(receiver, receiver_map));
}
if (!expr->holder().is_null()) {
- AddInstruction(new HCheckPrototypeMaps(
+ AddInstruction(new(zone()) HCheckPrototypeMaps(
Handle<JSObject>(JSObject::cast(receiver_map->prototype())),
expr->holder()));
}
@@ -3760,13 +3944,15 @@
Handle<Map> map = types->at(i);
if (expr->ComputeTarget(map, name)) {
if (count == 0) {
- AddInstruction(new HCheckNonSmi(receiver)); // Only needed once.
+ // Only needed once.
+ AddInstruction(new(zone()) HCheckNonSmi(receiver));
join = graph()->CreateBasicBlock();
}
++count;
HBasicBlock* if_true = graph()->CreateBasicBlock();
HBasicBlock* if_false = graph()->CreateBasicBlock();
- HCompareMap* compare = new HCompareMap(receiver, map, if_true, if_false);
+ HCompareMap* compare =
+ new(zone()) HCompareMap(receiver, map, if_true, if_false);
current_block()->Finish(compare);
set_current_block(if_true);
@@ -3775,12 +3961,13 @@
PrintF("Trying to inline the polymorphic call to %s\n",
*name->ToCString());
}
- if (!FLAG_polymorphic_inlining || !TryInline(expr)) {
- // Check for bailout, as trying to inline might fail due to bailout
- // during hydrogen processing.
- CHECK_BAILOUT;
+ if (FLAG_polymorphic_inlining && TryInline(expr)) {
+ // Trying to inline will signal that we should bailout from the
+ // entire compilation by setting stack overflow on the visitor.
+ if (HasStackOverflow()) return;
+ } else {
HCallConstantFunction* call =
- new HCallConstantFunction(expr->target(), argument_count);
+ new(zone()) HCallConstantFunction(expr->target(), argument_count);
call->set_position(expr->position());
PreProcessCall(call);
AddInstruction(call);
@@ -3798,9 +3985,9 @@
if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
current_block()->FinishExitWithDeoptimization();
} else {
- HContext* context = new HContext;
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
- HCallNamed* call = new HCallNamed(context, name, argument_count);
+ HCallNamed* call = new(zone()) HCallNamed(context, name, argument_count);
call->set_position(expr->position());
PreProcessCall(call);
@@ -3818,22 +4005,30 @@
// even without predecessors to the join block, we set it as the exit
// block and continue by adding instructions there.
ASSERT(join != NULL);
- set_current_block(join);
if (join->HasPredecessor()) {
+ set_current_block(join);
join->SetJoinId(expr->id());
if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop());
+ } else {
+ set_current_block(NULL);
}
}
void HGraphBuilder::TraceInline(Handle<JSFunction> target, const char* reason) {
if (FLAG_trace_inlining) {
- SmartPointer<char> callee = target->shared()->DebugName()->ToCString();
- SmartPointer<char> caller =
- info()->function()->debug_name()->ToCString();
if (reason == NULL) {
+ // We are currently in the context of inlined function thus we have
+ // to go to an outer FunctionState to get caller.
+ SmartPointer<char> callee = target->shared()->DebugName()->ToCString();
+ SmartPointer<char> caller =
+ function_state()->outer()->compilation_info()->function()->
+ debug_name()->ToCString();
PrintF("Inlined %s called from %s.\n", *callee, *caller);
} else {
+ SmartPointer<char> callee = target->shared()->DebugName()->ToCString();
+ SmartPointer<char> caller =
+ info()->function()->debug_name()->ToCString();
PrintF("Did not inline %s called from %s (%s).\n",
*callee, *caller, reason);
}
@@ -3870,11 +4065,16 @@
return false;
}
- // Don't inline deeper than two calls.
+ // Don't inline deeper than kMaxInliningLevels calls.
HEnvironment* env = environment();
- if (env->outer() != NULL && env->outer()->outer() != NULL) {
- TraceInline(target, "inline depth limit reached");
- return false;
+ int current_level = 1;
+ while (env->outer() != NULL) {
+ if (current_level == Compiler::kMaxInliningLevels) {
+ TraceInline(target, "inline depth limit reached");
+ return false;
+ }
+ current_level++;
+ env = env->outer();
}
// Don't inline recursive functions.
@@ -3976,13 +4176,13 @@
body_entry->SetJoinId(expr->ReturnId());
set_current_block(body_entry);
- AddInstruction(new HEnterInlined(target, function));
+ AddInstruction(new(zone()) HEnterInlined(target, function));
VisitStatements(function->body());
if (HasStackOverflow()) {
// Bail out if the inline function did, as we cannot residualize a call
// instead.
TraceInline(target, "inline graph construction failed");
- return false;
+ return true;
}
// Update inlined nodes count.
@@ -4009,7 +4209,7 @@
// TODO(3168478): refactor to avoid this.
HBasicBlock* empty_true = graph()->CreateBasicBlock();
HBasicBlock* empty_false = graph()->CreateBasicBlock();
- HTest* test = new HTest(undefined, empty_true, empty_false);
+ HTest* test = new(zone()) HTest(undefined, empty_true, empty_false);
current_block()->Finish(test);
empty_true->Goto(inlined_test_context()->if_true(), false);
@@ -4038,9 +4238,11 @@
// flow to handle.
set_current_block(NULL);
- } else {
+ } else if (function_return()->HasPredecessor()) {
function_return()->SetJoinId(expr->id());
set_current_block(function_return());
+ } else {
+ set_current_block(NULL);
}
return true;
@@ -4063,7 +4265,7 @@
HValue* index = Pop();
HValue* string = Pop();
ASSERT(!expr->holder().is_null());
- AddInstruction(new HCheckPrototypeMaps(
+ AddInstruction(new(zone()) HCheckPrototypeMaps(
oracle()->GetPrototypeForPrimitiveCheck(STRING_CHECK),
expr->holder()));
HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
@@ -4072,7 +4274,8 @@
return true;
}
AddInstruction(char_code);
- HStringCharFromCode* result = new HStringCharFromCode(char_code);
+ HStringCharFromCode* result =
+ new(zone()) HStringCharFromCode(char_code);
ast_context()->ReturnInstruction(result, expr->id());
return true;
}
@@ -4088,7 +4291,7 @@
AddCheckConstantFunction(expr, receiver, receiver_map, true);
HValue* argument = Pop();
Drop(1); // Receiver.
- HUnaryMathOperation* op = new HUnaryMathOperation(argument, id);
+ HUnaryMathOperation* op = new(zone()) HUnaryMathOperation(argument, id);
op->set_position(expr->position());
ast_context()->ReturnInstruction(op, expr->id());
return true;
@@ -4105,30 +4308,30 @@
if (right->IsConstant() && HConstant::cast(right)->HasDoubleValue()) {
double exponent = HConstant::cast(right)->DoubleValue();
if (exponent == 0.5) {
- result = new HUnaryMathOperation(left, kMathPowHalf);
+ result = new(zone()) HUnaryMathOperation(left, kMathPowHalf);
} else if (exponent == -0.5) {
HConstant* double_one =
- new HConstant(Handle<Object>(Smi::FromInt(1)),
- Representation::Double());
+ new(zone()) HConstant(Handle<Object>(Smi::FromInt(1)),
+ Representation::Double());
AddInstruction(double_one);
HUnaryMathOperation* square_root =
- new HUnaryMathOperation(left, kMathPowHalf);
+ new(zone()) HUnaryMathOperation(left, kMathPowHalf);
AddInstruction(square_root);
// MathPowHalf doesn't have side effects so there's no need for
// an environment simulation here.
ASSERT(!square_root->HasSideEffects());
- result = new HDiv(double_one, square_root);
+ result = new(zone()) HDiv(double_one, square_root);
} else if (exponent == 2.0) {
- result = new HMul(left, left);
+ result = new(zone()) HMul(left, left);
}
} else if (right->IsConstant() &&
HConstant::cast(right)->HasInteger32Value() &&
HConstant::cast(right)->Integer32Value() == 2) {
- result = new HMul(left, left);
+ result = new(zone()) HMul(left, left);
}
if (result == NULL) {
- result = new HPower(left, right);
+ result = new(zone()) HPower(left, right);
}
ast_context()->ReturnInstruction(result, expr->id());
return true;
@@ -4165,19 +4368,19 @@
// Found pattern f.apply(receiver, arguments).
VisitForValue(prop->obj());
- if (HasStackOverflow()) return false;
+ if (HasStackOverflow() || current_block() == NULL) return true;
HValue* function = Pop();
VisitForValue(args->at(0));
- if (HasStackOverflow()) return false;
+ if (HasStackOverflow() || current_block() == NULL) return true;
HValue* receiver = Pop();
- HInstruction* elements = AddInstruction(new HArgumentsElements);
- HInstruction* length = AddInstruction(new HArgumentsLength(elements));
+ HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
+ HInstruction* length = AddInstruction(new(zone()) HArgumentsLength(elements));
AddCheckConstantFunction(expr,
function,
expr->GetReceiverTypes()->first(),
true);
HInstruction* result =
- new HApplyArguments(function, receiver, length, elements);
+ new(zone()) HApplyArguments(function, receiver, length, elements);
result->set_position(expr->position());
ast_context()->ReturnInstruction(result, expr->id());
return true;
@@ -4185,6 +4388,9 @@
void HGraphBuilder::VisitCall(Call* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
Expression* callee = expr->expression();
int argument_count = expr->arguments()->length() + 1; // Plus receiver.
HInstruction* call = NULL;
@@ -4193,21 +4399,21 @@
if (prop != NULL) {
if (!prop->key()->IsPropertyName()) {
// Keyed function call.
- VISIT_FOR_VALUE(prop->obj());
+ CHECK_ALIVE(VisitForValue(prop->obj()));
- VISIT_FOR_VALUE(prop->key());
+ CHECK_ALIVE(VisitForValue(prop->key()));
// Push receiver and key like the non-optimized code generator expects it.
HValue* key = Pop();
HValue* receiver = Pop();
Push(key);
Push(receiver);
- VisitExpressions(expr->arguments());
- CHECK_BAILOUT;
+ CHECK_ALIVE(VisitExpressions(expr->arguments()));
- HContext* context = new HContext;
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
- call = PreProcessCall(new HCallKeyed(context, key, argument_count));
+ call = PreProcessCall(
+ new(zone()) HCallKeyed(context, key, argument_count));
call->set_position(expr->position());
Drop(1); // Key.
ast_context()->ReturnInstruction(call, expr->id());
@@ -4218,11 +4424,9 @@
expr->RecordTypeFeedback(oracle());
if (TryCallApply(expr)) return;
- CHECK_BAILOUT;
- VISIT_FOR_VALUE(prop->obj());
- VisitExpressions(expr->arguments());
- CHECK_BAILOUT;
+ CHECK_ALIVE(VisitForValue(prop->obj()));
+ CHECK_ALIVE(VisitExpressions(expr->arguments()));
Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
@@ -4246,21 +4450,17 @@
// When the target has a custom call IC generator, use the IC,
// because it is likely to generate better code. Also use the IC
// when a primitive receiver check is required.
- HContext* context = new HContext;
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
- call = PreProcessCall(new HCallNamed(context, name, argument_count));
+ call = PreProcessCall(
+ new(zone()) HCallNamed(context, name, argument_count));
} else {
AddCheckConstantFunction(expr, receiver, receiver_map, true);
- if (TryInline(expr)) {
- return;
- } else {
- // Check for bailout, as the TryInline call in the if condition above
- // might return false due to bailout during hydrogen processing.
- CHECK_BAILOUT;
- call = PreProcessCall(new HCallConstantFunction(expr->target(),
- argument_count));
- }
+ if (TryInline(expr)) return;
+ call = PreProcessCall(
+ new(zone()) HCallConstantFunction(expr->target(),
+ argument_count));
}
} else if (types != NULL && types->length() > 1) {
ASSERT(expr->check_type() == RECEIVER_MAP_CHECK);
@@ -4268,9 +4468,10 @@
return;
} else {
- HContext* context = new HContext;
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
- call = PreProcessCall(new HCallNamed(context, name, argument_count));
+ call = PreProcessCall(
+ new(zone()) HCallNamed(context, name, argument_count));
}
} else {
@@ -4279,7 +4480,7 @@
if (!global_call) {
++argument_count;
- VISIT_FOR_VALUE(expr->expression());
+ CHECK_ALIVE(VisitForValue(expr->expression()));
}
if (global_call) {
@@ -4287,27 +4488,29 @@
// If there is a global property cell for the name at compile time and
// access check is not enabled we assume that the function will not change
// and generate optimized code for calling the function.
- if (info()->has_global_object() &&
+ LookupResult lookup;
+ GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, false);
+ if (type == kUseCell &&
!info()->global_object()->IsAccessCheckNeeded()) {
Handle<GlobalObject> global(info()->global_object());
- known_global_function = expr->ComputeGlobalTarget(global, var->name());
+ known_global_function = expr->ComputeGlobalTarget(global, &lookup);
}
if (known_global_function) {
// Push the global object instead of the global receiver because
// code generated by the full code generator expects it.
- HContext* context = new HContext;
- HGlobalObject* global_object = new HGlobalObject(context);
+ HContext* context = new(zone()) HContext;
+ HGlobalObject* global_object = new(zone()) HGlobalObject(context);
AddInstruction(context);
PushAndAdd(global_object);
- VisitExpressions(expr->arguments());
- CHECK_BAILOUT;
+ CHECK_ALIVE(VisitExpressions(expr->arguments()));
- VISIT_FOR_VALUE(expr->expression());
+ CHECK_ALIVE(VisitForValue(expr->expression()));
HValue* function = Pop();
- AddInstruction(new HCheckFunction(function, expr->target()));
+ AddInstruction(new(zone()) HCheckFunction(function, expr->target()));
// Replace the global object with the global receiver.
- HGlobalReceiver* global_receiver = new HGlobalReceiver(global_object);
+ HGlobalReceiver* global_receiver =
+ new(zone()) HGlobalReceiver(global_object);
// Index of the receiver from the top of the expression stack.
const int receiver_index = argument_count - 1;
AddInstruction(global_receiver);
@@ -4315,37 +4518,29 @@
IsGlobalObject());
environment()->SetExpressionStackAt(receiver_index, global_receiver);
- if (TryInline(expr)) {
- return;
- }
- // Check for bailout, as trying to inline might fail due to bailout
- // during hydrogen processing.
- CHECK_BAILOUT;
-
- call = PreProcessCall(new HCallKnownGlobal(expr->target(),
- argument_count));
+ if (TryInline(expr)) return;
+ call = PreProcessCall(new(zone()) HCallKnownGlobal(expr->target(),
+ argument_count));
} else {
- HContext* context = new HContext;
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
- PushAndAdd(new HGlobalObject(context));
- VisitExpressions(expr->arguments());
- CHECK_BAILOUT;
+ PushAndAdd(new(zone()) HGlobalObject(context));
+ CHECK_ALIVE(VisitExpressions(expr->arguments()));
- call = PreProcessCall(new HCallGlobal(context,
+ call = PreProcessCall(new(zone()) HCallGlobal(context,
var->name(),
argument_count));
}
} else {
- HContext* context = new HContext;
- HGlobalObject* global_object = new HGlobalObject(context);
+ HContext* context = new(zone()) HContext;
+ HGlobalObject* global_object = new(zone()) HGlobalObject(context);
AddInstruction(context);
AddInstruction(global_object);
- PushAndAdd(new HGlobalReceiver(global_object));
- VisitExpressions(expr->arguments());
- CHECK_BAILOUT;
+ PushAndAdd(new(zone()) HGlobalReceiver(global_object));
+ CHECK_ALIVE(VisitExpressions(expr->arguments()));
- call = PreProcessCall(new HCallFunction(context, argument_count));
+ call = PreProcessCall(new(zone()) HCallFunction(context, argument_count));
}
}
@@ -4355,20 +4550,22 @@
void HGraphBuilder::VisitCallNew(CallNew* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
// The constructor function is also used as the receiver argument to the
// JS construct call builtin.
- VISIT_FOR_VALUE(expr->expression());
- VisitExpressions(expr->arguments());
- CHECK_BAILOUT;
+ CHECK_ALIVE(VisitForValue(expr->expression()));
+ CHECK_ALIVE(VisitExpressions(expr->arguments()));
- HContext* context = new HContext;
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
// The constructor is both an operand to the instruction and an argument
// to the construct call.
int arg_count = expr->arguments()->length() + 1; // Plus constructor.
HValue* constructor = environment()->ExpressionStackAt(arg_count - 1);
- HCallNew* call = new HCallNew(context, constructor, arg_count);
+ HCallNew* call = new(zone()) HCallNew(context, constructor, arg_count);
call->set_position(expr->position());
PreProcessCall(call);
ast_context()->ReturnInstruction(call, expr->id());
@@ -4391,8 +4588,11 @@
void HGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
if (expr->is_jsruntime()) {
- BAILOUT("call to a JavaScript runtime function");
+ return Bailout("call to a JavaScript runtime function");
}
const Runtime::Function* function = expr->function();
@@ -4412,12 +4612,12 @@
(this->*generator)(expr);
} else {
ASSERT(function->intrinsic_type == Runtime::RUNTIME);
- VisitArgumentList(expr->arguments());
- CHECK_BAILOUT;
+ CHECK_ALIVE(VisitArgumentList(expr->arguments()));
Handle<String> name = expr->name();
int argument_count = expr->arguments()->length();
- HCallRuntime* call = new HCallRuntime(name, function, argument_count);
+ HCallRuntime* call =
+ new(zone()) HCallRuntime(name, function, argument_count);
call->set_position(RelocInfo::kNoPosition);
Drop(argument_count);
ast_context()->ReturnInstruction(call, expr->id());
@@ -4426,9 +4626,12 @@
void HGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
Token::Value op = expr->op();
if (op == Token::VOID) {
- VISIT_FOR_EFFECT(expr->expression());
+ CHECK_ALIVE(VisitForEffect(expr->expression()));
ast_context()->ReturnValue(graph()->GetConstantUndefined());
} else if (op == Token::DELETE) {
Property* prop = expr->expression()->AsProperty();
@@ -4436,7 +4639,7 @@
if (prop == NULL && var == NULL) {
// Result of deleting non-property, non-variable reference is true.
// Evaluate the subexpression for side effects.
- VISIT_FOR_EFFECT(expr->expression());
+ CHECK_ALIVE(VisitForEffect(expr->expression()));
ast_context()->ReturnValue(graph()->GetConstantTrue());
} else if (var != NULL &&
!var->is_global() &&
@@ -4451,17 +4654,17 @@
// to accesses on the arguments object.
ast_context()->ReturnValue(graph()->GetConstantFalse());
} else {
- VISIT_FOR_VALUE(prop->obj());
- VISIT_FOR_VALUE(prop->key());
+ CHECK_ALIVE(VisitForValue(prop->obj()));
+ CHECK_ALIVE(VisitForValue(prop->key()));
HValue* key = Pop();
HValue* obj = Pop();
- HDeleteProperty* instr = new HDeleteProperty(obj, key);
+ HDeleteProperty* instr = new(zone()) HDeleteProperty(obj, key);
ast_context()->ReturnInstruction(instr, expr->id());
}
} else if (var->is_global()) {
- BAILOUT("delete with global variable");
+ return Bailout("delete with global variable");
} else {
- BAILOUT("delete with non-global variable");
+ return Bailout("delete with non-global variable");
}
} else if (op == Token::NOT) {
if (ast_context()->IsTest()) {
@@ -4472,47 +4675,56 @@
} else if (ast_context()->IsValue()) {
HBasicBlock* materialize_false = graph()->CreateBasicBlock();
HBasicBlock* materialize_true = graph()->CreateBasicBlock();
- VISIT_FOR_CONTROL(expr->expression(),
- materialize_false,
- materialize_true);
- materialize_false->SetJoinId(expr->expression()->id());
- materialize_true->SetJoinId(expr->expression()->id());
+ CHECK_BAILOUT(VisitForControl(expr->expression(),
+ materialize_false,
+ materialize_true));
- set_current_block(materialize_false);
- Push(graph()->GetConstantFalse());
- set_current_block(materialize_true);
- Push(graph()->GetConstantTrue());
+ if (materialize_false->HasPredecessor()) {
+ materialize_false->SetJoinId(expr->expression()->id());
+ set_current_block(materialize_false);
+ Push(graph()->GetConstantFalse());
+ } else {
+ materialize_false = NULL;
+ }
+
+ if (materialize_true->HasPredecessor()) {
+ materialize_true->SetJoinId(expr->expression()->id());
+ set_current_block(materialize_true);
+ Push(graph()->GetConstantTrue());
+ } else {
+ materialize_true = NULL;
+ }
HBasicBlock* join =
CreateJoin(materialize_false, materialize_true, expr->id());
set_current_block(join);
- ast_context()->ReturnValue(Pop());
+ if (join != NULL) ast_context()->ReturnValue(Pop());
} else {
ASSERT(ast_context()->IsEffect());
VisitForEffect(expr->expression());
}
} else if (op == Token::TYPEOF) {
- VISIT_FOR_VALUE(expr->expression());
+ CHECK_ALIVE(VisitForTypeOf(expr->expression()));
HValue* value = Pop();
- ast_context()->ReturnInstruction(new HTypeof(value), expr->id());
+ ast_context()->ReturnInstruction(new(zone()) HTypeof(value), expr->id());
} else {
- VISIT_FOR_VALUE(expr->expression());
+ CHECK_ALIVE(VisitForValue(expr->expression()));
HValue* value = Pop();
HInstruction* instr = NULL;
switch (op) {
case Token::BIT_NOT:
- instr = new HBitNot(value);
+ instr = new(zone()) HBitNot(value);
break;
case Token::SUB:
- instr = new HMul(value, graph_->GetConstantMinus1());
+ instr = new(zone()) HMul(value, graph_->GetConstantMinus1());
break;
case Token::ADD:
- instr = new HMul(value, graph_->GetConstant1());
+ instr = new(zone()) HMul(value, graph_->GetConstant1());
break;
default:
- BAILOUT("Value: unsupported unary operation");
+ return Bailout("Value: unsupported unary operation");
break;
}
ast_context()->ReturnInstruction(instr, expr->id());
@@ -4520,26 +4732,21 @@
}
-void HGraphBuilder::VisitIncrementOperation(IncrementOperation* expr) {
- // IncrementOperation is never visited by the visitor. It only
- // occurs as a subexpression of CountOperation.
- UNREACHABLE();
-}
-
-
HInstruction* HGraphBuilder::BuildIncrement(HValue* value, bool increment) {
HConstant* delta = increment
? graph_->GetConstant1()
: graph_->GetConstantMinus1();
- HInstruction* instr = new HAdd(value, delta);
+ HInstruction* instr = new(zone()) HAdd(value, delta);
AssumeRepresentation(instr, Representation::Integer32());
return instr;
}
void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
- IncrementOperation* increment = expr->increment();
- Expression* target = increment->expression();
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ Expression* target = expr->expression();
VariableProxy* proxy = target->AsVariableProxy();
Variable* var = proxy->AsVariable();
Property* prop = target->AsProperty();
@@ -4547,7 +4754,7 @@
bool inc = expr->op() == Token::INC;
if (var != NULL) {
- VISIT_FOR_VALUE(target);
+ CHECK_ALIVE(VisitForValue(target));
// Match the full code generator stack by simulating an extra stack
// element for postfix operations in a non-effect context.
@@ -4567,11 +4774,12 @@
} else if (var->IsContextSlot()) {
HValue* context = BuildContextChainWalk(var);
int index = var->AsSlot()->index();
- HStoreContextSlot* instr = new HStoreContextSlot(context, index, after);
+ HStoreContextSlot* instr =
+ new(zone()) HStoreContextSlot(context, index, after);
AddInstruction(instr);
if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId());
} else {
- BAILOUT("lookup variable in count operation");
+ return Bailout("lookup variable in count operation");
}
Drop(has_extra ? 2 : 1);
ast_context()->ReturnValue(expr->is_postfix() ? before : after);
@@ -4587,7 +4795,7 @@
bool has_extra = expr->is_postfix() && !ast_context()->IsEffect();
if (has_extra) Push(graph_->GetConstantUndefined());
- VISIT_FOR_VALUE(prop->obj());
+ CHECK_ALIVE(VisitForValue(prop->obj()));
HValue* obj = Top();
HInstruction* load = NULL;
@@ -4599,7 +4807,7 @@
load = BuildLoadNamedGeneric(obj, prop);
}
PushAndAdd(load);
- if (load->HasSideEffects()) AddSimulate(increment->id());
+ if (load->HasSideEffects()) AddSimulate(expr->CountId());
HValue* before = Pop();
// There is no deoptimization to after the increment, so we don't need
@@ -4628,19 +4836,14 @@
bool has_extra = expr->is_postfix() && !ast_context()->IsEffect();
if (has_extra) Push(graph_->GetConstantUndefined());
- VISIT_FOR_VALUE(prop->obj());
- VISIT_FOR_VALUE(prop->key());
+ CHECK_ALIVE(VisitForValue(prop->obj()));
+ CHECK_ALIVE(VisitForValue(prop->key()));
HValue* obj = environment()->ExpressionStackAt(1);
HValue* key = environment()->ExpressionStackAt(0);
- bool is_fast_elements = prop->IsMonomorphic() &&
- prop->GetMonomorphicReceiverType()->has_fast_elements();
-
- HInstruction* load = is_fast_elements
- ? BuildLoadKeyedFastElement(obj, key, prop)
- : BuildLoadKeyedGeneric(obj, key);
+ HInstruction* load = BuildLoadKeyed(obj, key, prop);
PushAndAdd(load);
- if (load->HasSideEffects()) AddSimulate(increment->id());
+ if (load->HasSideEffects()) AddSimulate(expr->CountId());
HValue* before = Pop();
// There is no deoptimization to after the increment, so we don't need
@@ -4648,9 +4851,8 @@
HInstruction* after = BuildIncrement(before, inc);
AddInstruction(after);
- HInstruction* store = is_fast_elements
- ? BuildStoreKeyedFastElement(obj, key, after, prop)
- : BuildStoreKeyedGeneric(obj, key, after);
+ expr->RecordTypeFeedback(oracle());
+ HInstruction* store = BuildStoreKeyed(obj, key, after, expr);
AddInstruction(store);
// Drop the key from the bailout environment. Overwrite the receiver
@@ -4666,65 +4868,75 @@
}
} else {
- BAILOUT("invalid lhs in count operation");
+ return Bailout("invalid lhs in count operation");
}
}
HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* string,
HValue* index) {
- AddInstruction(new HCheckNonSmi(string));
- AddInstruction(new HCheckInstanceType(
+ AddInstruction(new(zone()) HCheckNonSmi(string));
+ AddInstruction(new(zone()) HCheckInstanceType(
string, FIRST_STRING_TYPE, LAST_STRING_TYPE));
- HStringLength* length = new HStringLength(string);
+ HStringLength* length = new(zone()) HStringLength(string);
AddInstruction(length);
- AddInstruction(new HBoundsCheck(index, length));
- return new HStringCharCodeAt(string, index);
+ AddInstruction(new(zone()) HBoundsCheck(index, length));
+ return new(zone()) HStringCharCodeAt(string, index);
}
HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr,
HValue* left,
HValue* right) {
+ TypeInfo info = oracle()->BinaryType(expr);
HInstruction* instr = NULL;
switch (expr->op()) {
case Token::ADD:
- instr = new HAdd(left, right);
+ if (info.IsString()) {
+ AddInstruction(new(zone()) HCheckNonSmi(left));
+ AddInstruction(new(zone()) HCheckInstanceType(
+ left, FIRST_STRING_TYPE, LAST_STRING_TYPE));
+ AddInstruction(new(zone()) HCheckNonSmi(right));
+ AddInstruction(new(zone()) HCheckInstanceType(
+ right, FIRST_STRING_TYPE, LAST_STRING_TYPE));
+ instr = new(zone()) HStringAdd(left, right);
+ } else {
+ instr = new(zone()) HAdd(left, right);
+ }
break;
case Token::SUB:
- instr = new HSub(left, right);
+ instr = new(zone()) HSub(left, right);
break;
case Token::MUL:
- instr = new HMul(left, right);
+ instr = new(zone()) HMul(left, right);
break;
case Token::MOD:
- instr = new HMod(left, right);
+ instr = new(zone()) HMod(left, right);
break;
case Token::DIV:
- instr = new HDiv(left, right);
+ instr = new(zone()) HDiv(left, right);
break;
case Token::BIT_XOR:
- instr = new HBitXor(left, right);
+ instr = new(zone()) HBitXor(left, right);
break;
case Token::BIT_AND:
- instr = new HBitAnd(left, right);
+ instr = new(zone()) HBitAnd(left, right);
break;
case Token::BIT_OR:
- instr = new HBitOr(left, right);
+ instr = new(zone()) HBitOr(left, right);
break;
case Token::SAR:
- instr = new HSar(left, right);
+ instr = new(zone()) HSar(left, right);
break;
case Token::SHR:
- instr = new HShr(left, right);
+ instr = new(zone()) HShr(left, right);
break;
case Token::SHL:
- instr = new HShl(left, right);
+ instr = new(zone()) HShl(left, right);
break;
default:
UNREACHABLE();
}
- TypeInfo info = oracle()->BinaryType(expr);
// If we hit an uninitialized binary op stub we will get type info
// for a smi operation. If one of the operands is a constant string
// do not generate code assuming it is a smi operation.
@@ -4761,8 +4973,11 @@
void HGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
if (expr->op() == Token::COMMA) {
- VISIT_FOR_EFFECT(expr->left());
+ CHECK_ALIVE(VisitForEffect(expr->left()));
// Visit the right subexpression in the same AST context as the entire
// expression.
Visit(expr->right());
@@ -4774,32 +4989,38 @@
// Translate left subexpression.
HBasicBlock* eval_right = graph()->CreateBasicBlock();
if (is_logical_and) {
- VISIT_FOR_CONTROL(expr->left(), eval_right, context->if_false());
+ CHECK_BAILOUT(VisitForControl(expr->left(),
+ eval_right,
+ context->if_false()));
} else {
- VISIT_FOR_CONTROL(expr->left(), context->if_true(), eval_right);
+ CHECK_BAILOUT(VisitForControl(expr->left(),
+ context->if_true(),
+ eval_right));
}
- eval_right->SetJoinId(expr->RightId());
// Translate right subexpression by visiting it in the same AST
// context as the entire expression.
- set_current_block(eval_right);
- Visit(expr->right());
+ if (eval_right->HasPredecessor()) {
+ eval_right->SetJoinId(expr->RightId());
+ set_current_block(eval_right);
+ Visit(expr->right());
+ }
} else if (ast_context()->IsValue()) {
- VISIT_FOR_VALUE(expr->left());
+ CHECK_ALIVE(VisitForValue(expr->left()));
ASSERT(current_block() != NULL);
// We need an extra block to maintain edge-split form.
HBasicBlock* empty_block = graph()->CreateBasicBlock();
HBasicBlock* eval_right = graph()->CreateBasicBlock();
HTest* test = is_logical_and
- ? new HTest(Top(), eval_right, empty_block)
- : new HTest(Top(), empty_block, eval_right);
+ ? new(zone()) HTest(Top(), eval_right, empty_block)
+ : new(zone()) HTest(Top(), empty_block, eval_right);
current_block()->Finish(test);
set_current_block(eval_right);
Drop(1); // Value of the left subexpression.
- VISIT_FOR_VALUE(expr->right());
+ CHECK_BAILOUT(VisitForValue(expr->right()));
HBasicBlock* join_block =
CreateJoin(empty_block, current_block(), expr->id());
@@ -4813,33 +5034,42 @@
// extra block to maintain edge-split form.
HBasicBlock* empty_block = graph()->CreateBasicBlock();
HBasicBlock* right_block = graph()->CreateBasicBlock();
- HBasicBlock* join_block = graph()->CreateBasicBlock();
if (is_logical_and) {
- VISIT_FOR_CONTROL(expr->left(), right_block, empty_block);
+ CHECK_BAILOUT(VisitForControl(expr->left(), right_block, empty_block));
} else {
- VISIT_FOR_CONTROL(expr->left(), empty_block, right_block);
+ CHECK_BAILOUT(VisitForControl(expr->left(), empty_block, right_block));
}
+
// TODO(kmillikin): Find a way to fix this. It's ugly that there are
// actually two empty blocks (one here and one inserted by
// TestContext::BuildBranch, and that they both have an HSimulate
// though the second one is not a merge node, and that we really have
// no good AST ID to put on that first HSimulate.
- empty_block->SetJoinId(expr->id());
- right_block->SetJoinId(expr->RightId());
- set_current_block(right_block);
- VISIT_FOR_EFFECT(expr->right());
+ if (empty_block->HasPredecessor()) {
+ empty_block->SetJoinId(expr->id());
+ } else {
+ empty_block = NULL;
+ }
- empty_block->Goto(join_block);
- current_block()->Goto(join_block);
- join_block->SetJoinId(expr->id());
+ if (right_block->HasPredecessor()) {
+ right_block->SetJoinId(expr->RightId());
+ set_current_block(right_block);
+ CHECK_BAILOUT(VisitForEffect(expr->right()));
+ right_block = current_block();
+ } else {
+ right_block = NULL;
+ }
+
+ HBasicBlock* join_block =
+ CreateJoin(empty_block, right_block, expr->id());
set_current_block(join_block);
// We did not materialize any value in the predecessor environments,
// so there is no need to handle it here.
}
} else {
- VISIT_FOR_VALUE(expr->left());
- VISIT_FOR_VALUE(expr->right());
+ CHECK_ALIVE(VisitForValue(expr->left()));
+ CHECK_ALIVE(VisitForValue(expr->right()));
HValue* right = Pop();
HValue* left = Pop();
@@ -4878,13 +5108,16 @@
void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
if (IsClassOfTest(expr)) {
CallRuntime* call = expr->left()->AsCallRuntime();
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
Literal* literal = expr->right()->AsLiteral();
Handle<String> rhs = Handle<String>::cast(literal->handle());
- HInstruction* instr = new HClassOfTest(value, rhs);
+ HInstruction* instr = new(zone()) HClassOfTest(value, rhs);
instr->set_position(expr->position());
ast_context()->ReturnInstruction(instr, expr->id());
return;
@@ -4896,17 +5129,17 @@
if ((expr->op() == Token::EQ || expr->op() == Token::EQ_STRICT) &&
left_unary != NULL && left_unary->op() == Token::TYPEOF &&
right_literal != NULL && right_literal->handle()->IsString()) {
- VISIT_FOR_VALUE(left_unary->expression());
+ CHECK_ALIVE(VisitForTypeOf(left_unary->expression()));
HValue* left = Pop();
- HInstruction* instr = new HTypeofIs(left,
+ HInstruction* instr = new(zone()) HTypeofIs(left,
Handle<String>::cast(right_literal->handle()));
instr->set_position(expr->position());
ast_context()->ReturnInstruction(instr, expr->id());
return;
}
- VISIT_FOR_VALUE(expr->left());
- VISIT_FOR_VALUE(expr->right());
+ CHECK_ALIVE(VisitForValue(expr->left()));
+ CHECK_ALIVE(VisitForValue(expr->right()));
HValue* right = Pop();
HValue* left = Pop();
@@ -4943,32 +5176,32 @@
// If the target is not null we have found a known global function that is
// assumed to stay the same for this instanceof.
if (target.is_null()) {
- HContext* context = new HContext;
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
- instr = new HInstanceOf(context, left, right);
+ instr = new(zone()) HInstanceOf(context, left, right);
} else {
- AddInstruction(new HCheckFunction(right, target));
- instr = new HInstanceOfKnownGlobal(left, target);
+ AddInstruction(new(zone()) HCheckFunction(right, target));
+ instr = new(zone()) HInstanceOfKnownGlobal(left, target);
}
} else if (op == Token::IN) {
- BAILOUT("Unsupported comparison: in");
+ return Bailout("Unsupported comparison: in");
} else if (type_info.IsNonPrimitive()) {
switch (op) {
case Token::EQ:
case Token::EQ_STRICT: {
- AddInstruction(new HCheckNonSmi(left));
+ AddInstruction(new(zone()) HCheckNonSmi(left));
AddInstruction(HCheckInstanceType::NewIsJSObjectOrJSFunction(left));
- AddInstruction(new HCheckNonSmi(right));
+ AddInstruction(new(zone()) HCheckNonSmi(right));
AddInstruction(HCheckInstanceType::NewIsJSObjectOrJSFunction(right));
- instr = new HCompareJSObjectEq(left, right);
+ instr = new(zone()) HCompareJSObjectEq(left, right);
break;
}
default:
- BAILOUT("Unsupported non-primitive compare");
+ return Bailout("Unsupported non-primitive compare");
break;
}
} else {
- HCompare* compare = new HCompare(left, right, op);
+ HCompare* compare = new(zone()) HCompare(left, right, op);
Representation r = ToRepresentation(type_info);
compare->SetInputRepresentation(r);
instr = compare;
@@ -4979,16 +5212,22 @@
void HGraphBuilder::VisitCompareToNull(CompareToNull* expr) {
- VISIT_FOR_VALUE(expr->expression());
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ CHECK_ALIVE(VisitForValue(expr->expression()));
HValue* value = Pop();
- HIsNull* compare = new HIsNull(value, expr->is_strict());
+ HIsNull* compare = new(zone()) HIsNull(value, expr->is_strict());
ast_context()->ReturnInstruction(compare, expr->id());
}
void HGraphBuilder::VisitThisFunction(ThisFunction* expr) {
- BAILOUT("ThisFunction");
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout("ThisFunction");
}
@@ -5003,7 +5242,7 @@
(slot != NULL && slot->type() == Slot::LOOKUP) ||
decl->mode() == Variable::CONST ||
decl->fun() != NULL) {
- BAILOUT("unsupported declaration");
+ return Bailout("unsupported declaration");
}
}
@@ -5012,107 +5251,118 @@
// Support for types.
void HGraphBuilder::GenerateIsSmi(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
- HIsSmi* result = new HIsSmi(value);
+ HIsSmi* result = new(zone()) HIsSmi(value);
ast_context()->ReturnInstruction(result, call->id());
}
void HGraphBuilder::GenerateIsSpecObject(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
HHasInstanceType* result =
- new HHasInstanceType(value, FIRST_JS_OBJECT_TYPE, LAST_TYPE);
+ new(zone()) HHasInstanceType(value, FIRST_JS_OBJECT_TYPE, LAST_TYPE);
ast_context()->ReturnInstruction(result, call->id());
}
void HGraphBuilder::GenerateIsFunction(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
- HHasInstanceType* result = new HHasInstanceType(value, JS_FUNCTION_TYPE);
+ HHasInstanceType* result =
+ new(zone()) HHasInstanceType(value, JS_FUNCTION_TYPE);
ast_context()->ReturnInstruction(result, call->id());
}
void HGraphBuilder::GenerateHasCachedArrayIndex(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
- HHasCachedArrayIndex* result = new HHasCachedArrayIndex(value);
+ HHasCachedArrayIndex* result = new(zone()) HHasCachedArrayIndex(value);
ast_context()->ReturnInstruction(result, call->id());
}
void HGraphBuilder::GenerateIsArray(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
- HHasInstanceType* result = new HHasInstanceType(value, JS_ARRAY_TYPE);
+ HHasInstanceType* result = new(zone()) HHasInstanceType(value, JS_ARRAY_TYPE);
ast_context()->ReturnInstruction(result, call->id());
}
void HGraphBuilder::GenerateIsRegExp(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
- HHasInstanceType* result = new HHasInstanceType(value, JS_REGEXP_TYPE);
+ HHasInstanceType* result =
+ new(zone()) HHasInstanceType(value, JS_REGEXP_TYPE);
ast_context()->ReturnInstruction(result, call->id());
}
void HGraphBuilder::GenerateIsObject(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
- HIsObject* test = new HIsObject(value);
+ HIsObject* test = new(zone()) HIsObject(value);
ast_context()->ReturnInstruction(test, call->id());
}
void HGraphBuilder::GenerateIsNonNegativeSmi(CallRuntime* call) {
- BAILOUT("inlined runtime function: IsNonNegativeSmi");
+ return Bailout("inlined runtime function: IsNonNegativeSmi");
}
void HGraphBuilder::GenerateIsUndetectableObject(CallRuntime* call) {
- BAILOUT("inlined runtime function: IsUndetectableObject");
+ return Bailout("inlined runtime function: IsUndetectableObject");
}
void HGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf(
CallRuntime* call) {
- BAILOUT("inlined runtime function: IsStringWrapperSafeForDefaultValueOf");
+ return Bailout(
+ "inlined runtime function: IsStringWrapperSafeForDefaultValueOf");
}
// Support for construct call checks.
void HGraphBuilder::GenerateIsConstructCall(CallRuntime* call) {
ASSERT(call->arguments()->length() == 0);
- ast_context()->ReturnInstruction(new HIsConstructCall, call->id());
+ if (function_state()->outer() != NULL) {
+ // We are generating graph for inlined function. Currently
+ // constructor inlining is not supported and we can just return
+ // false from %_IsConstructCall().
+ ast_context()->ReturnValue(graph()->GetConstantFalse());
+ } else {
+ ast_context()->ReturnInstruction(new(zone()) HIsConstructCall, call->id());
+ }
}
// Support for arguments.length and arguments[?].
void HGraphBuilder::GenerateArgumentsLength(CallRuntime* call) {
ASSERT(call->arguments()->length() == 0);
- HInstruction* elements = AddInstruction(new HArgumentsElements);
- HArgumentsLength* result = new HArgumentsLength(elements);
+ HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
+ HArgumentsLength* result = new(zone()) HArgumentsLength(elements);
ast_context()->ReturnInstruction(result, call->id());
}
void HGraphBuilder::GenerateArguments(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* index = Pop();
- HInstruction* elements = AddInstruction(new HArgumentsElements);
- HInstruction* length = AddInstruction(new HArgumentsLength(elements));
- HAccessArgumentsAt* result = new HAccessArgumentsAt(elements, length, index);
+ HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
+ HInstruction* length = AddInstruction(new(zone()) HArgumentsLength(elements));
+ HAccessArgumentsAt* result =
+ new(zone()) HAccessArgumentsAt(elements, length, index);
ast_context()->ReturnInstruction(result, call->id());
}
@@ -5121,29 +5371,29 @@
void HGraphBuilder::GenerateClassOf(CallRuntime* call) {
// The special form detected by IsClassOfTest is detected before we get here
// and does not cause a bailout.
- BAILOUT("inlined runtime function: ClassOf");
+ return Bailout("inlined runtime function: ClassOf");
}
void HGraphBuilder::GenerateValueOf(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
- HValueOf* result = new HValueOf(value);
+ HValueOf* result = new(zone()) HValueOf(value);
ast_context()->ReturnInstruction(result, call->id());
}
void HGraphBuilder::GenerateSetValueOf(CallRuntime* call) {
- BAILOUT("inlined runtime function: SetValueOf");
+ return Bailout("inlined runtime function: SetValueOf");
}
// Fast support for charCodeAt(n).
void HGraphBuilder::GenerateStringCharCodeAt(CallRuntime* call) {
ASSERT(call->arguments()->length() == 2);
- VISIT_FOR_VALUE(call->arguments()->at(0));
- VISIT_FOR_VALUE(call->arguments()->at(1));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
HValue* index = Pop();
HValue* string = Pop();
HStringCharCodeAt* result = BuildStringCharCodeAt(string, index);
@@ -5154,9 +5404,9 @@
// Fast support for string.charAt(n) and string[n].
void HGraphBuilder::GenerateStringCharFromCode(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* char_code = Pop();
- HStringCharFromCode* result = new HStringCharFromCode(char_code);
+ HStringCharFromCode* result = new(zone()) HStringCharFromCode(char_code);
ast_context()->ReturnInstruction(result, call->id());
}
@@ -5164,13 +5414,13 @@
// Fast support for string.charAt(n) and string[n].
void HGraphBuilder::GenerateStringCharAt(CallRuntime* call) {
ASSERT(call->arguments()->length() == 2);
- VISIT_FOR_VALUE(call->arguments()->at(0));
- VISIT_FOR_VALUE(call->arguments()->at(1));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
HValue* index = Pop();
HValue* string = Pop();
HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
AddInstruction(char_code);
- HStringCharFromCode* result = new HStringCharFromCode(char_code);
+ HStringCharFromCode* result = new(zone()) HStringCharFromCode(char_code);
ast_context()->ReturnInstruction(result, call->id());
}
@@ -5178,11 +5428,11 @@
// Fast support for object equality testing.
void HGraphBuilder::GenerateObjectEquals(CallRuntime* call) {
ASSERT(call->arguments()->length() == 2);
- VISIT_FOR_VALUE(call->arguments()->at(0));
- VISIT_FOR_VALUE(call->arguments()->at(1));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
HValue* right = Pop();
HValue* left = Pop();
- HCompareJSObjectEq* result = new HCompareJSObjectEq(left, right);
+ HCompareJSObjectEq* result = new(zone()) HCompareJSObjectEq(left, right);
ast_context()->ReturnInstruction(result, call->id());
}
@@ -5195,18 +5445,17 @@
// Fast support for Math.random().
void HGraphBuilder::GenerateRandomHeapNumber(CallRuntime* call) {
- BAILOUT("inlined runtime function: RandomHeapNumber");
+ return Bailout("inlined runtime function: RandomHeapNumber");
}
// Fast support for StringAdd.
void HGraphBuilder::GenerateStringAdd(CallRuntime* call) {
ASSERT_EQ(2, call->arguments()->length());
- VisitArgumentList(call->arguments());
- CHECK_BAILOUT;
- HContext* context = new HContext;
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
- HCallStub* result = new HCallStub(context, CodeStub::StringAdd, 2);
+ HCallStub* result = new(zone()) HCallStub(context, CodeStub::StringAdd, 2);
Drop(2);
ast_context()->ReturnInstruction(result, call->id());
}
@@ -5215,11 +5464,10 @@
// Fast support for SubString.
void HGraphBuilder::GenerateSubString(CallRuntime* call) {
ASSERT_EQ(3, call->arguments()->length());
- VisitArgumentList(call->arguments());
- CHECK_BAILOUT;
- HContext* context = new HContext;
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
- HCallStub* result = new HCallStub(context, CodeStub::SubString, 3);
+ HCallStub* result = new(zone()) HCallStub(context, CodeStub::SubString, 3);
Drop(3);
ast_context()->ReturnInstruction(result, call->id());
}
@@ -5228,11 +5476,11 @@
// Fast support for StringCompare.
void HGraphBuilder::GenerateStringCompare(CallRuntime* call) {
ASSERT_EQ(2, call->arguments()->length());
- VisitArgumentList(call->arguments());
- CHECK_BAILOUT;
- HContext* context = new HContext;
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
- HCallStub* result = new HCallStub(context, CodeStub::StringCompare, 2);
+ HCallStub* result =
+ new(zone()) HCallStub(context, CodeStub::StringCompare, 2);
Drop(2);
ast_context()->ReturnInstruction(result, call->id());
}
@@ -5241,11 +5489,10 @@
// Support for direct calls from JavaScript to native RegExp code.
void HGraphBuilder::GenerateRegExpExec(CallRuntime* call) {
ASSERT_EQ(4, call->arguments()->length());
- VisitArgumentList(call->arguments());
- CHECK_BAILOUT;
- HContext* context = new HContext;
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
- HCallStub* result = new HCallStub(context, CodeStub::RegExpExec, 4);
+ HCallStub* result = new(zone()) HCallStub(context, CodeStub::RegExpExec, 4);
Drop(4);
ast_context()->ReturnInstruction(result, call->id());
}
@@ -5254,12 +5501,11 @@
// Construct a RegExp exec result with two in-object properties.
void HGraphBuilder::GenerateRegExpConstructResult(CallRuntime* call) {
ASSERT_EQ(3, call->arguments()->length());
- VisitArgumentList(call->arguments());
- CHECK_BAILOUT;
- HContext* context = new HContext;
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
HCallStub* result =
- new HCallStub(context, CodeStub::RegExpConstructResult, 3);
+ new(zone()) HCallStub(context, CodeStub::RegExpConstructResult, 3);
Drop(3);
ast_context()->ReturnInstruction(result, call->id());
}
@@ -5267,18 +5513,18 @@
// Support for fast native caches.
void HGraphBuilder::GenerateGetFromCache(CallRuntime* call) {
- BAILOUT("inlined runtime function: GetFromCache");
+ return Bailout("inlined runtime function: GetFromCache");
}
// Fast support for number to string.
void HGraphBuilder::GenerateNumberToString(CallRuntime* call) {
ASSERT_EQ(1, call->arguments()->length());
- VisitArgumentList(call->arguments());
- CHECK_BAILOUT;
- HContext* context = new HContext;
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
- HCallStub* result = new HCallStub(context, CodeStub::NumberToString, 1);
+ HCallStub* result =
+ new(zone()) HCallStub(context, CodeStub::NumberToString, 1);
Drop(1);
ast_context()->ReturnInstruction(result, call->id());
}
@@ -5288,35 +5534,49 @@
// indices. This should only be used if the indices are known to be
// non-negative and within bounds of the elements array at the call site.
void HGraphBuilder::GenerateSwapElements(CallRuntime* call) {
- BAILOUT("inlined runtime function: SwapElements");
+ return Bailout("inlined runtime function: SwapElements");
}
// Fast call for custom callbacks.
void HGraphBuilder::GenerateCallFunction(CallRuntime* call) {
- BAILOUT("inlined runtime function: CallFunction");
+ // 1 ~ The function to call is not itself an argument to the call.
+ int arg_count = call->arguments()->length() - 1;
+ ASSERT(arg_count >= 1); // There's always at least a receiver.
+
+ for (int i = 0; i < arg_count; ++i) {
+ CHECK_ALIVE(VisitArgument(call->arguments()->at(i)));
+ }
+ CHECK_ALIVE(VisitForValue(call->arguments()->last()));
+ HValue* function = Pop();
+ HContext* context = new HContext;
+ AddInstruction(context);
+ HInvokeFunction* result =
+ new(zone()) HInvokeFunction(context, function, arg_count);
+ Drop(arg_count);
+ ast_context()->ReturnInstruction(result, call->id());
}
// Fast call to math functions.
void HGraphBuilder::GenerateMathPow(CallRuntime* call) {
ASSERT_EQ(2, call->arguments()->length());
- VISIT_FOR_VALUE(call->arguments()->at(0));
- VISIT_FOR_VALUE(call->arguments()->at(1));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
HValue* right = Pop();
HValue* left = Pop();
- HPower* result = new HPower(left, right);
+ HPower* result = new(zone()) HPower(left, right);
ast_context()->ReturnInstruction(result, call->id());
}
void HGraphBuilder::GenerateMathSin(CallRuntime* call) {
ASSERT_EQ(1, call->arguments()->length());
- VisitArgumentList(call->arguments());
- CHECK_BAILOUT;
- HContext* context = new HContext;
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
- HCallStub* result = new HCallStub(context, CodeStub::TranscendentalCache, 1);
+ HCallStub* result =
+ new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
result->set_transcendental_type(TranscendentalCache::SIN);
Drop(1);
ast_context()->ReturnInstruction(result, call->id());
@@ -5325,11 +5585,11 @@
void HGraphBuilder::GenerateMathCos(CallRuntime* call) {
ASSERT_EQ(1, call->arguments()->length());
- VisitArgumentList(call->arguments());
- CHECK_BAILOUT;
- HContext* context = new HContext;
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
- HCallStub* result = new HCallStub(context, CodeStub::TranscendentalCache, 1);
+ HCallStub* result =
+ new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
result->set_transcendental_type(TranscendentalCache::COS);
Drop(1);
ast_context()->ReturnInstruction(result, call->id());
@@ -5338,11 +5598,11 @@
void HGraphBuilder::GenerateMathLog(CallRuntime* call) {
ASSERT_EQ(1, call->arguments()->length());
- VisitArgumentList(call->arguments());
- CHECK_BAILOUT;
- HContext* context = new HContext;
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HContext* context = new(zone()) HContext;
AddInstruction(context);
- HCallStub* result = new HCallStub(context, CodeStub::TranscendentalCache, 1);
+ HCallStub* result =
+ new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
result->set_transcendental_type(TranscendentalCache::LOG);
Drop(1);
ast_context()->ReturnInstruction(result, call->id());
@@ -5350,35 +5610,32 @@
void HGraphBuilder::GenerateMathSqrt(CallRuntime* call) {
- BAILOUT("inlined runtime function: MathSqrt");
+ return Bailout("inlined runtime function: MathSqrt");
}
// Check whether two RegExps are equivalent
void HGraphBuilder::GenerateIsRegExpEquivalent(CallRuntime* call) {
- BAILOUT("inlined runtime function: IsRegExpEquivalent");
+ return Bailout("inlined runtime function: IsRegExpEquivalent");
}
void HGraphBuilder::GenerateGetCachedArrayIndex(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
- HGetCachedArrayIndex* result = new HGetCachedArrayIndex(value);
+ HGetCachedArrayIndex* result = new(zone()) HGetCachedArrayIndex(value);
ast_context()->ReturnInstruction(result, call->id());
}
void HGraphBuilder::GenerateFastAsciiArrayJoin(CallRuntime* call) {
- BAILOUT("inlined runtime function: FastAsciiArrayJoin");
+ return Bailout("inlined runtime function: FastAsciiArrayJoin");
}
-#undef BAILOUT
#undef CHECK_BAILOUT
-#undef VISIT_FOR_EFFECT
-#undef VISIT_FOR_VALUE
-#undef ADD_TO_SUBGRAPH
+#undef CHECK_ALIVE
HEnvironment::HEnvironment(HEnvironment* outer,
@@ -5453,7 +5710,7 @@
} else if (values_[i] != other->values_[i]) {
// There is a fresh value on the incoming edge, a phi is needed.
ASSERT(values_[i] != NULL && other->values_[i] != NULL);
- HPhi* phi = new HPhi(i);
+ HPhi* phi = new(block->zone()) HPhi(i);
HValue* old_value = values_[i];
for (int j = 0; j < block->predecessors()->length(); j++) {
phi->AddInput(old_value);
@@ -5510,7 +5767,7 @@
HEnvironment* HEnvironment::Copy() const {
- return new HEnvironment(this);
+ return new(closure()->GetIsolate()->zone()) HEnvironment(this);
}
@@ -5524,7 +5781,7 @@
HEnvironment* HEnvironment::CopyAsLoopHeader(HBasicBlock* loop_header) const {
HEnvironment* new_env = Copy();
for (int i = 0; i < values_.length(); ++i) {
- HPhi* phi = new HPhi(i);
+ HPhi* phi = new(loop_header->zone()) HPhi(i);
phi->AddInput(values_[i]);
new_env->values_[i] = phi;
loop_header->AddPhi(phi);
@@ -5543,7 +5800,9 @@
HEnvironment* outer = Copy();
outer->Drop(arity + 1); // Including receiver.
outer->ClearHistory();
- HEnvironment* inner = new HEnvironment(outer, function->scope(), target);
+ Zone* zone = closure()->GetIsolate()->zone();
+ HEnvironment* inner =
+ new(zone) HEnvironment(outer, function->scope(), target);
// Get the argument values from the original environment.
if (is_speculative) {
for (int i = 0; i <= arity; ++i) { // Include receiver.
@@ -5563,7 +5822,7 @@
inner->SetValueAt(local_base + i, undefined);
}
- inner->set_ast_id(function->id());
+ inner->set_ast_id(AstNode::kFunctionEntryId);
return inner;
}
diff --git a/src/hydrogen.h b/src/hydrogen.h
index e14799a..74c119a 100644
--- a/src/hydrogen.h
+++ b/src/hydrogen.h
@@ -141,6 +141,8 @@
bool IsInlineReturnTarget() const { return is_inline_return_target_; }
void MarkAsInlineReturnTarget() { is_inline_return_target_ = true; }
+ inline Zone* zone();
+
#ifdef DEBUG
void Verify();
#endif
@@ -201,6 +203,9 @@
public:
explicit HGraph(CompilationInfo* info);
+ Isolate* isolate() { return isolate_; }
+ Zone* zone() { return isolate_->zone(); }
+
const ZoneList<HBasicBlock*>* blocks() const { return &blocks_; }
const ZoneList<HPhi*>* phi_list() const { return phi_list_; }
HBasicBlock* entry_block() const { return entry_block_; }
@@ -281,8 +286,6 @@
void InitializeInferredTypes(int from_inclusive, int to_inclusive);
void CheckForBackEdge(HBasicBlock* block, HBasicBlock* successor);
- Isolate* isolate() { return isolate_; }
-
Isolate* isolate_;
int next_block_id_;
HBasicBlock* entry_block_;
@@ -301,6 +304,9 @@
};
+Zone* HBasicBlock::zone() { return graph_->zone(); }
+
+
class HEnvironment: public ZoneObject {
public:
HEnvironment(HEnvironment* outer,
@@ -453,12 +459,17 @@
// the instruction as value.
virtual void ReturnInstruction(HInstruction* instr, int ast_id) = 0;
+ void set_for_typeof(bool for_typeof) { for_typeof_ = for_typeof; }
+ bool is_for_typeof() { return for_typeof_; }
+
protected:
AstContext(HGraphBuilder* owner, Expression::Context kind);
virtual ~AstContext();
HGraphBuilder* owner() const { return owner_; }
+ inline Zone* zone();
+
// We want to be able to assert, in a context-specific way, that the stack
// height makes sense when the context is filled.
#ifdef DEBUG
@@ -469,6 +480,7 @@
HGraphBuilder* owner_;
Expression::Context kind_;
AstContext* outer_;
+ bool for_typeof_;
};
@@ -544,6 +556,8 @@
test_context_ = NULL;
}
+ FunctionState* outer() { return outer_; }
+
private:
HGraphBuilder* owner_;
@@ -624,7 +638,8 @@
break_scope_(NULL),
graph_(NULL),
current_block_(NULL),
- inlined_count_(0) {
+ inlined_count_(0),
+ zone_(info->isolate()->zone()) {
// This is not initialized in the initializer list because the
// constructor for the initial state relies on function_state_ == NULL
// to know it's the initial state.
@@ -694,6 +709,9 @@
void ClearInlinedTestContext() {
function_state()->ClearInlinedTestContext();
}
+ bool function_strict_mode() {
+ return function_state()->compilation_info()->is_strict_mode();
+ }
// Generators for inline runtime functions.
#define INLINE_FUNCTION_GENERATOR_DECLARATION(Name, argc, ressize) \
@@ -735,6 +753,7 @@
void Bind(Variable* var, HValue* value) { environment()->Bind(var, value); }
void VisitForValue(Expression* expr);
+ void VisitForTypeOf(Expression* expr);
void VisitForEffect(Expression* expr);
void VisitForControl(Expression* expr,
HBasicBlock* true_block,
@@ -770,9 +789,13 @@
HBasicBlock* CreateLoopHeaderBlock();
// Helpers for flow graph construction.
- void LookupGlobalPropertyCell(Variable* var,
- LookupResult* lookup,
- bool is_store);
+ enum GlobalPropertyAccess {
+ kUseCell,
+ kUseGeneric
+ };
+ GlobalPropertyAccess LookupGlobalProperty(Variable* var,
+ LookupResult* lookup,
+ bool is_store);
bool TryArgumentsAccess(Property* expr);
bool TryCallApply(Call* expr);
@@ -825,6 +848,10 @@
HInstruction* BuildLoadKeyedGeneric(HValue* object,
HValue* key);
+ HInstruction* BuildLoadKeyed(HValue* obj,
+ HValue* key,
+ Property* prop);
+
HInstruction* BuildLoadNamed(HValue* object,
Property* prop,
Handle<Map> map,
@@ -854,7 +881,12 @@
HValue* object,
HValue* key,
HValue* val,
- Assignment* expr);
+ Expression* expr);
+
+ HInstruction* BuildStoreKeyed(HValue* object,
+ HValue* key,
+ HValue* value,
+ Expression* assignment);
HValue* BuildContextChainWalk(Variable* var);
@@ -863,6 +895,7 @@
Handle<Map> receiver_map,
bool smi_and_map_check);
+ Zone* zone() { return zone_; }
// The translation state of the currently-being-translated function.
FunctionState* function_state_;
@@ -882,6 +915,8 @@
int inlined_count_;
+ Zone* zone_;
+
friend class FunctionState; // Pushes and pops the state stack.
friend class AstContext; // Pushes and pops the AST context stack.
@@ -889,6 +924,9 @@
};
+Zone* AstContext::zone() { return owner_->zone(); }
+
+
class HValueMap: public ZoneObject {
public:
HValueMap()
@@ -911,7 +949,10 @@
}
HValue* Lookup(HValue* value) const;
- HValueMap* Copy() const { return new HValueMap(this); }
+
+ HValueMap* Copy(Zone* zone) const {
+ return new(zone) HValueMap(this);
+ }
private:
// A linked list of HValue* values. Stored in arrays.
diff --git a/src/ia32/assembler-ia32-inl.h b/src/ia32/assembler-ia32-inl.h
index 1da3f81..a9247f4 100644
--- a/src/ia32/assembler-ia32-inl.h
+++ b/src/ia32/assembler-ia32-inl.h
@@ -225,9 +225,9 @@
StaticVisitor::VisitPointer(heap, target_object_address());
CPU::FlushICache(pc_, sizeof(Address));
} else if (RelocInfo::IsCodeTarget(mode)) {
- StaticVisitor::VisitCodeTarget(this);
+ StaticVisitor::VisitCodeTarget(heap, this);
} else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) {
- StaticVisitor::VisitGlobalPropertyCell(this);
+ StaticVisitor::VisitGlobalPropertyCell(heap, this);
} else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
StaticVisitor::VisitExternalReference(target_reference_address());
CPU::FlushICache(pc_, sizeof(Address));
@@ -237,7 +237,7 @@
IsPatchedReturnSequence()) ||
(RelocInfo::IsDebugBreakSlot(mode) &&
IsPatchedDebugBreakSlotSequence()))) {
- StaticVisitor::VisitDebugTarget(this);
+ StaticVisitor::VisitDebugTarget(heap, this);
#endif
} else if (mode == RelocInfo::RUNTIME_ENTRY) {
StaticVisitor::VisitRuntimeEntry(this);
diff --git a/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc
index e6d245e..9273037 100644
--- a/src/ia32/assembler-ia32.cc
+++ b/src/ia32/assembler-ia32.cc
@@ -48,24 +48,37 @@
// -----------------------------------------------------------------------------
// Implementation of CpuFeatures
-CpuFeatures::CpuFeatures()
- : supported_(0),
- enabled_(0),
- found_by_runtime_probing_(0) {
-}
+#ifdef DEBUG
+bool CpuFeatures::initialized_ = false;
+#endif
+uint64_t CpuFeatures::supported_ = 0;
+uint64_t CpuFeatures::found_by_runtime_probing_ = 0;
-// The Probe method needs executable memory, so it uses Heap::CreateCode.
-// Allocation failure is silent and leads to safe default.
-void CpuFeatures::Probe(bool portable) {
- ASSERT(HEAP->HasBeenSetup());
+void CpuFeatures::Probe() {
+ ASSERT(!initialized_);
ASSERT(supported_ == 0);
- if (portable && Serializer::enabled()) {
+#ifdef DEBUG
+ initialized_ = true;
+#endif
+ if (Serializer::enabled()) {
supported_ |= OS::CpuFeaturesImpliedByPlatform();
return; // No features if we might serialize.
}
- Assembler assm(NULL, 0);
+ const int kBufferSize = 4 * KB;
+ VirtualMemory* memory = new VirtualMemory(kBufferSize);
+ if (!memory->IsReserved()) {
+ delete memory;
+ return;
+ }
+ ASSERT(memory->size() >= static_cast<size_t>(kBufferSize));
+ if (!memory->Commit(memory->address(), kBufferSize, true/*executable*/)) {
+ delete memory;
+ return;
+ }
+
+ Assembler assm(NULL, memory->address(), kBufferSize);
Label cpuid, done;
#define __ assm.
// Save old esp, since we are going to modify the stack.
@@ -119,27 +132,15 @@
__ ret(0);
#undef __
- CodeDesc desc;
- assm.GetCode(&desc);
- Object* code;
- { MaybeObject* maybe_code =
- assm.isolate()->heap()->CreateCode(desc,
- Code::ComputeFlags(Code::STUB),
- Handle<Code>::null());
- if (!maybe_code->ToObject(&code)) return;
- }
- if (!code->IsCode()) return;
-
- PROFILE(ISOLATE,
- CodeCreateEvent(Logger::BUILTIN_TAG,
- Code::cast(code), "CpuFeatures::Probe"));
typedef uint64_t (*F0)();
- F0 probe = FUNCTION_CAST<F0>(Code::cast(code)->entry());
+ F0 probe = FUNCTION_CAST<F0>(reinterpret_cast<Address>(memory->address()));
supported_ = probe();
found_by_runtime_probing_ = supported_;
uint64_t os_guarantees = OS::CpuFeaturesImpliedByPlatform();
supported_ |= os_guarantees;
- found_by_runtime_probing_ &= portable ? ~os_guarantees : 0;
+ found_by_runtime_probing_ &= ~os_guarantees;
+
+ delete memory;
}
@@ -297,8 +298,8 @@
static void InitCoverageLog();
#endif
-Assembler::Assembler(void* buffer, int buffer_size)
- : AssemblerBase(Isolate::Current()),
+Assembler::Assembler(Isolate* arg_isolate, void* buffer, int buffer_size)
+ : AssemblerBase(arg_isolate),
positions_recorder_(this),
emit_debug_code_(FLAG_debug_code) {
if (buffer == NULL) {
@@ -386,7 +387,7 @@
void Assembler::cpuid() {
- ASSERT(isolate()->cpu_features()->IsEnabled(CPUID));
+ ASSERT(CpuFeatures::IsEnabled(CPUID));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x0F);
@@ -747,7 +748,7 @@
void Assembler::cmov(Condition cc, Register dst, int32_t imm32) {
- ASSERT(isolate()->cpu_features()->IsEnabled(CMOV));
+ ASSERT(CpuFeatures::IsEnabled(CMOV));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
UNIMPLEMENTED();
@@ -758,7 +759,7 @@
void Assembler::cmov(Condition cc, Register dst, Handle<Object> handle) {
- ASSERT(isolate()->cpu_features()->IsEnabled(CMOV));
+ ASSERT(CpuFeatures::IsEnabled(CMOV));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
UNIMPLEMENTED();
@@ -769,7 +770,7 @@
void Assembler::cmov(Condition cc, Register dst, const Operand& src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(CMOV));
+ ASSERT(CpuFeatures::IsEnabled(CMOV));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
// Opcode: 0f 40 + cc /r.
@@ -1450,7 +1451,7 @@
void Assembler::rdtsc() {
- ASSERT(isolate()->cpu_features()->IsEnabled(RDTSC));
+ ASSERT(CpuFeatures::IsEnabled(RDTSC));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x0F);
@@ -1856,7 +1857,7 @@
void Assembler::fisttp_s(const Operand& adr) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE3));
+ ASSERT(CpuFeatures::IsEnabled(SSE3));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xDB);
@@ -1865,7 +1866,7 @@
void Assembler::fisttp_d(const Operand& adr) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE3));
+ ASSERT(CpuFeatures::IsEnabled(SSE3));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xDD);
@@ -2134,7 +2135,7 @@
void Assembler::cvttss2si(Register dst, const Operand& src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF3);
@@ -2145,7 +2146,7 @@
void Assembler::cvttsd2si(Register dst, const Operand& src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF2);
@@ -2156,7 +2157,7 @@
void Assembler::cvtsi2sd(XMMRegister dst, const Operand& src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF2);
@@ -2167,7 +2168,7 @@
void Assembler::cvtss2sd(XMMRegister dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF3);
@@ -2178,7 +2179,7 @@
void Assembler::cvtsd2ss(XMMRegister dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF2);
@@ -2189,7 +2190,7 @@
void Assembler::addsd(XMMRegister dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF2);
@@ -2200,7 +2201,7 @@
void Assembler::mulsd(XMMRegister dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF2);
@@ -2211,7 +2212,7 @@
void Assembler::subsd(XMMRegister dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF2);
@@ -2222,7 +2223,7 @@
void Assembler::divsd(XMMRegister dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF2);
@@ -2233,7 +2234,7 @@
void Assembler::xorpd(XMMRegister dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
@@ -2264,7 +2265,7 @@
void Assembler::ucomisd(XMMRegister dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
@@ -2275,7 +2276,7 @@
void Assembler::movmskpd(Register dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
@@ -2286,7 +2287,7 @@
void Assembler::cmpltsd(XMMRegister dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF2);
@@ -2298,7 +2299,7 @@
void Assembler::movaps(XMMRegister dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x0F);
@@ -2308,7 +2309,7 @@
void Assembler::movdqa(const Operand& dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
@@ -2319,7 +2320,7 @@
void Assembler::movdqa(XMMRegister dst, const Operand& src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
@@ -2330,7 +2331,7 @@
void Assembler::movdqu(const Operand& dst, XMMRegister src ) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF3);
@@ -2341,7 +2342,7 @@
void Assembler::movdqu(XMMRegister dst, const Operand& src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF3);
@@ -2352,7 +2353,7 @@
void Assembler::movntdqa(XMMRegister dst, const Operand& src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE4_1));
+ ASSERT(CpuFeatures::IsEnabled(SSE4_1));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
@@ -2364,7 +2365,7 @@
void Assembler::movntdq(const Operand& dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
@@ -2400,7 +2401,7 @@
void Assembler::movsd(const Operand& dst, XMMRegister src ) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF2); // double
@@ -2411,7 +2412,7 @@
void Assembler::movsd(XMMRegister dst, const Operand& src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF2); // double
@@ -2422,7 +2423,7 @@
void Assembler::movsd(XMMRegister dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF2);
@@ -2433,7 +2434,7 @@
void Assembler::movss(const Operand& dst, XMMRegister src ) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF3); // float
@@ -2444,7 +2445,7 @@
void Assembler::movss(XMMRegister dst, const Operand& src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF3); // float
@@ -2455,7 +2456,7 @@
void Assembler::movss(XMMRegister dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF3);
@@ -2466,7 +2467,7 @@
void Assembler::movd(XMMRegister dst, const Operand& src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
@@ -2477,7 +2478,7 @@
void Assembler::movd(const Operand& dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
@@ -2488,7 +2489,7 @@
void Assembler::pand(XMMRegister dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
@@ -2499,7 +2500,7 @@
void Assembler::pxor(XMMRegister dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
@@ -2510,7 +2511,7 @@
void Assembler::por(XMMRegister dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
@@ -2521,7 +2522,7 @@
void Assembler::ptest(XMMRegister dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE4_1));
+ ASSERT(CpuFeatures::IsEnabled(SSE4_1));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
@@ -2533,7 +2534,7 @@
void Assembler::psllq(XMMRegister reg, int8_t shift) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
@@ -2545,7 +2546,7 @@
void Assembler::psllq(XMMRegister dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
@@ -2556,7 +2557,7 @@
void Assembler::psrlq(XMMRegister reg, int8_t shift) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
@@ -2568,7 +2569,7 @@
void Assembler::psrlq(XMMRegister dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
@@ -2579,7 +2580,7 @@
void Assembler::pshufd(XMMRegister dst, XMMRegister src, int8_t shuffle) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
@@ -2591,7 +2592,7 @@
void Assembler::pextrd(const Operand& dst, XMMRegister src, int8_t offset) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE4_1));
+ ASSERT(CpuFeatures::IsEnabled(SSE4_1));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
@@ -2604,7 +2605,7 @@
void Assembler::pinsrd(XMMRegister dst, const Operand& src, int8_t offset) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE4_1));
+ ASSERT(CpuFeatures::IsEnabled(SSE4_1));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h
index 8e0c762..079dca7 100644
--- a/src/ia32/assembler-ia32.h
+++ b/src/ia32/assembler-ia32.h
@@ -446,16 +446,15 @@
// } else {
// // Generate standard x87 floating point code.
// }
-class CpuFeatures {
+class CpuFeatures : public AllStatic {
public:
- // Detect features of the target CPU. If the portable flag is set,
- // the method sets safe defaults if the serializer is enabled
- // (snapshots must be portable).
- void Probe(bool portable);
- void Clear() { supported_ = 0; }
+ // Detect features of the target CPU. Set safe defaults if the serializer
+ // is enabled (snapshots must be portable).
+ static void Probe();
// Check whether a feature is supported by the target CPU.
- bool IsSupported(CpuFeature f) const {
+ static bool IsSupported(CpuFeature f) {
+ ASSERT(initialized_);
if (f == SSE2 && !FLAG_enable_sse2) return false;
if (f == SSE3 && !FLAG_enable_sse3) return false;
if (f == SSE4_1 && !FLAG_enable_sse4_1) return false;
@@ -463,46 +462,85 @@
if (f == RDTSC && !FLAG_enable_rdtsc) return false;
return (supported_ & (static_cast<uint64_t>(1) << f)) != 0;
}
+
+#ifdef DEBUG
// Check whether a feature is currently enabled.
- bool IsEnabled(CpuFeature f) const {
- return (enabled_ & (static_cast<uint64_t>(1) << f)) != 0;
+ static bool IsEnabled(CpuFeature f) {
+ ASSERT(initialized_);
+ Isolate* isolate = Isolate::UncheckedCurrent();
+ if (isolate == NULL) {
+ // When no isolate is available, work as if we're running in
+ // release mode.
+ return IsSupported(f);
+ }
+ uint64_t enabled = isolate->enabled_cpu_features();
+ return (enabled & (static_cast<uint64_t>(1) << f)) != 0;
}
+#endif
+
// Enable a specified feature within a scope.
class Scope BASE_EMBEDDED {
#ifdef DEBUG
public:
- explicit Scope(CpuFeature f)
- : cpu_features_(Isolate::Current()->cpu_features()),
- isolate_(Isolate::Current()) {
+ explicit Scope(CpuFeature f) {
uint64_t mask = static_cast<uint64_t>(1) << f;
- ASSERT(cpu_features_->IsSupported(f));
+ ASSERT(CpuFeatures::IsSupported(f));
ASSERT(!Serializer::enabled() ||
- (cpu_features_->found_by_runtime_probing_ & mask) == 0);
- old_enabled_ = cpu_features_->enabled_;
- cpu_features_->enabled_ |= mask;
+ (CpuFeatures::found_by_runtime_probing_ & mask) == 0);
+ isolate_ = Isolate::UncheckedCurrent();
+ old_enabled_ = 0;
+ if (isolate_ != NULL) {
+ old_enabled_ = isolate_->enabled_cpu_features();
+ isolate_->set_enabled_cpu_features(old_enabled_ | mask);
+ }
}
~Scope() {
- ASSERT_EQ(Isolate::Current(), isolate_);
- cpu_features_->enabled_ = old_enabled_;
+ ASSERT_EQ(Isolate::UncheckedCurrent(), isolate_);
+ if (isolate_ != NULL) {
+ isolate_->set_enabled_cpu_features(old_enabled_);
+ }
}
private:
- uint64_t old_enabled_;
- CpuFeatures* cpu_features_;
Isolate* isolate_;
+ uint64_t old_enabled_;
#else
public:
explicit Scope(CpuFeature f) {}
#endif
};
+ class TryForceFeatureScope BASE_EMBEDDED {
+ public:
+ explicit TryForceFeatureScope(CpuFeature f)
+ : old_supported_(CpuFeatures::supported_) {
+ if (CanForce()) {
+ CpuFeatures::supported_ |= (static_cast<uint64_t>(1) << f);
+ }
+ }
+
+ ~TryForceFeatureScope() {
+ if (CanForce()) {
+ CpuFeatures::supported_ = old_supported_;
+ }
+ }
+
+ private:
+ static bool CanForce() {
+ // It's only safe to temporarily force support of CPU features
+ // when there's only a single isolate, which is guaranteed when
+ // the serializer is enabled.
+ return Serializer::enabled();
+ }
+
+ const uint64_t old_supported_;
+ };
+
private:
- CpuFeatures();
-
- uint64_t supported_;
- uint64_t enabled_;
- uint64_t found_by_runtime_probing_;
-
- friend class Isolate;
+#ifdef DEBUG
+ static bool initialized_;
+#endif
+ static uint64_t supported_;
+ static uint64_t found_by_runtime_probing_;
DISALLOW_COPY_AND_ASSIGN(CpuFeatures);
};
@@ -535,7 +573,8 @@
// for code generation and assumes its size to be buffer_size. If the buffer
// is too small, a fatal error occurs. No deallocation of the buffer is done
// upon destruction of the assembler.
- Assembler(void* buffer, int buffer_size);
+ // TODO(vitalyr): the assembler does not need an isolate.
+ Assembler(Isolate* isolate, void* buffer, int buffer_size);
~Assembler();
// Overrides the default provided by FLAG_debug_code.
diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc
index 2970a0e..29c67b5 100644
--- a/src/ia32/builtins-ia32.cc
+++ b/src/ia32/builtins-ia32.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -29,7 +29,7 @@
#if defined(V8_TARGET_ARCH_IA32)
-#include "codegen-inl.h"
+#include "codegen.h"
#include "deoptimizer.h"
#include "full-codegen.h"
@@ -1523,12 +1523,8 @@
void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) {
- // We shouldn't be performing on-stack replacement in the first
- // place if the CPU features we need for the optimized Crankshaft
- // code aren't supported.
- CpuFeatures* cpu_features = masm->isolate()->cpu_features();
- cpu_features->Probe(false);
- if (!cpu_features->IsSupported(SSE2)) {
+ CpuFeatures::TryForceFeatureScope scope(SSE2);
+ if (!CpuFeatures::IsSupported(SSE2)) {
__ Abort("Unreachable code: Cannot optimize without SSE2 support.");
return;
}
diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc
index 96faae9..275e8e2 100644
--- a/src/ia32/code-stubs-ia32.cc
+++ b/src/ia32/code-stubs-ia32.cc
@@ -291,166 +291,6 @@
}
-const char* GenericBinaryOpStub::GetName() {
- if (name_ != NULL) return name_;
- const int kMaxNameLength = 100;
- name_ = Isolate::Current()->bootstrapper()->AllocateAutoDeletedArray(
- kMaxNameLength);
- if (name_ == NULL) return "OOM";
- const char* op_name = Token::Name(op_);
- const char* overwrite_name;
- switch (mode_) {
- case NO_OVERWRITE: overwrite_name = "Alloc"; break;
- case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break;
- case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break;
- default: overwrite_name = "UnknownOverwrite"; break;
- }
-
- OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
- "GenericBinaryOpStub_%s_%s%s_%s%s_%s_%s",
- op_name,
- overwrite_name,
- (flags_ & NO_SMI_CODE_IN_STUB) ? "_NoSmiInStub" : "",
- args_in_registers_ ? "RegArgs" : "StackArgs",
- args_reversed_ ? "_R" : "",
- static_operands_type_.ToString(),
- BinaryOpIC::GetName(runtime_operands_type_));
- return name_;
-}
-
-
-void GenericBinaryOpStub::GenerateCall(
- MacroAssembler* masm,
- Register left,
- Register right) {
- if (!ArgsInRegistersSupported()) {
- // Pass arguments on the stack.
- __ push(left);
- __ push(right);
- } else {
- // The calling convention with registers is left in edx and right in eax.
- Register left_arg = edx;
- Register right_arg = eax;
- if (!(left.is(left_arg) && right.is(right_arg))) {
- if (left.is(right_arg) && right.is(left_arg)) {
- if (IsOperationCommutative()) {
- SetArgsReversed();
- } else {
- __ xchg(left, right);
- }
- } else if (left.is(left_arg)) {
- __ mov(right_arg, right);
- } else if (right.is(right_arg)) {
- __ mov(left_arg, left);
- } else if (left.is(right_arg)) {
- if (IsOperationCommutative()) {
- __ mov(left_arg, right);
- SetArgsReversed();
- } else {
- // Order of moves important to avoid destroying left argument.
- __ mov(left_arg, left);
- __ mov(right_arg, right);
- }
- } else if (right.is(left_arg)) {
- if (IsOperationCommutative()) {
- __ mov(right_arg, left);
- SetArgsReversed();
- } else {
- // Order of moves important to avoid destroying right argument.
- __ mov(right_arg, right);
- __ mov(left_arg, left);
- }
- } else {
- // Order of moves is not important.
- __ mov(left_arg, left);
- __ mov(right_arg, right);
- }
- }
-
- // Update flags to indicate that arguments are in registers.
- SetArgsInRegisters();
- __ IncrementCounter(
- masm->isolate()->counters()->generic_binary_stub_calls_regs(), 1);
- }
-
- // Call the stub.
- __ CallStub(this);
-}
-
-
-void GenericBinaryOpStub::GenerateCall(
- MacroAssembler* masm,
- Register left,
- Smi* right) {
- if (!ArgsInRegistersSupported()) {
- // Pass arguments on the stack.
- __ push(left);
- __ push(Immediate(right));
- } else {
- // The calling convention with registers is left in edx and right in eax.
- Register left_arg = edx;
- Register right_arg = eax;
- if (left.is(left_arg)) {
- __ mov(right_arg, Immediate(right));
- } else if (left.is(right_arg) && IsOperationCommutative()) {
- __ mov(left_arg, Immediate(right));
- SetArgsReversed();
- } else {
- // For non-commutative operations, left and right_arg might be
- // the same register. Therefore, the order of the moves is
- // important here in order to not overwrite left before moving
- // it to left_arg.
- __ mov(left_arg, left);
- __ mov(right_arg, Immediate(right));
- }
-
- // Update flags to indicate that arguments are in registers.
- SetArgsInRegisters();
- __ IncrementCounter(
- masm->isolate()->counters()->generic_binary_stub_calls_regs(), 1);
- }
-
- // Call the stub.
- __ CallStub(this);
-}
-
-
-void GenericBinaryOpStub::GenerateCall(
- MacroAssembler* masm,
- Smi* left,
- Register right) {
- if (!ArgsInRegistersSupported()) {
- // Pass arguments on the stack.
- __ push(Immediate(left));
- __ push(right);
- } else {
- // The calling convention with registers is left in edx and right in eax.
- Register left_arg = edx;
- Register right_arg = eax;
- if (right.is(right_arg)) {
- __ mov(left_arg, Immediate(left));
- } else if (right.is(left_arg) && IsOperationCommutative()) {
- __ mov(right_arg, Immediate(left));
- SetArgsReversed();
- } else {
- // For non-commutative operations, right and left_arg might be
- // the same register. Therefore, the order of the moves is
- // important here in order to not overwrite right before moving
- // it to right_arg.
- __ mov(right_arg, right);
- __ mov(left_arg, Immediate(left));
- }
- // Update flags to indicate that arguments are in registers.
- SetArgsInRegisters();
- Counters* counters = masm->isolate()->counters();
- __ IncrementCounter(counters->generic_binary_stub_calls_regs(), 1);
- }
-
- // Call the stub.
- __ CallStub(this);
-}
-
-
class FloatingPointHelper : public AllStatic {
public:
@@ -534,762 +374,6 @@
};
-void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) {
- // 1. Move arguments into edx, eax except for DIV and MOD, which need the
- // dividend in eax and edx free for the division. Use eax, ebx for those.
- Comment load_comment(masm, "-- Load arguments");
- Register left = edx;
- Register right = eax;
- if (op_ == Token::DIV || op_ == Token::MOD) {
- left = eax;
- right = ebx;
- if (HasArgsInRegisters()) {
- __ mov(ebx, eax);
- __ mov(eax, edx);
- }
- }
- if (!HasArgsInRegisters()) {
- __ mov(right, Operand(esp, 1 * kPointerSize));
- __ mov(left, Operand(esp, 2 * kPointerSize));
- }
-
- if (static_operands_type_.IsSmi()) {
- if (FLAG_debug_code) {
- __ AbortIfNotSmi(left);
- __ AbortIfNotSmi(right);
- }
- if (op_ == Token::BIT_OR) {
- __ or_(right, Operand(left));
- GenerateReturn(masm);
- return;
- } else if (op_ == Token::BIT_AND) {
- __ and_(right, Operand(left));
- GenerateReturn(masm);
- return;
- } else if (op_ == Token::BIT_XOR) {
- __ xor_(right, Operand(left));
- GenerateReturn(masm);
- return;
- }
- }
-
- // 2. Prepare the smi check of both operands by oring them together.
- Comment smi_check_comment(masm, "-- Smi check arguments");
- Label not_smis;
- Register combined = ecx;
- ASSERT(!left.is(combined) && !right.is(combined));
- switch (op_) {
- case Token::BIT_OR:
- // Perform the operation into eax and smi check the result. Preserve
- // eax in case the result is not a smi.
- ASSERT(!left.is(ecx) && !right.is(ecx));
- __ mov(ecx, right);
- __ or_(right, Operand(left)); // Bitwise or is commutative.
- combined = right;
- break;
-
- case Token::BIT_XOR:
- case Token::BIT_AND:
- case Token::ADD:
- case Token::SUB:
- case Token::MUL:
- case Token::DIV:
- case Token::MOD:
- __ mov(combined, right);
- __ or_(combined, Operand(left));
- break;
-
- case Token::SHL:
- case Token::SAR:
- case Token::SHR:
- // Move the right operand into ecx for the shift operation, use eax
- // for the smi check register.
- ASSERT(!left.is(ecx) && !right.is(ecx));
- __ mov(ecx, right);
- __ or_(right, Operand(left));
- combined = right;
- break;
-
- default:
- break;
- }
-
- // 3. Perform the smi check of the operands.
- STATIC_ASSERT(kSmiTag == 0); // Adjust zero check if not the case.
- __ test(combined, Immediate(kSmiTagMask));
- __ j(not_zero, ¬_smis, not_taken);
-
- // 4. Operands are both smis, perform the operation leaving the result in
- // eax and check the result if necessary.
- Comment perform_smi(masm, "-- Perform smi operation");
- Label use_fp_on_smis;
- switch (op_) {
- case Token::BIT_OR:
- // Nothing to do.
- break;
-
- case Token::BIT_XOR:
- ASSERT(right.is(eax));
- __ xor_(right, Operand(left)); // Bitwise xor is commutative.
- break;
-
- case Token::BIT_AND:
- ASSERT(right.is(eax));
- __ and_(right, Operand(left)); // Bitwise and is commutative.
- break;
-
- case Token::SHL:
- // Remove tags from operands (but keep sign).
- __ SmiUntag(left);
- __ SmiUntag(ecx);
- // Perform the operation.
- __ shl_cl(left);
- // Check that the *signed* result fits in a smi.
- __ cmp(left, 0xc0000000);
- __ j(sign, &use_fp_on_smis, not_taken);
- // Tag the result and store it in register eax.
- __ SmiTag(left);
- __ mov(eax, left);
- break;
-
- case Token::SAR:
- // Remove tags from operands (but keep sign).
- __ SmiUntag(left);
- __ SmiUntag(ecx);
- // Perform the operation.
- __ sar_cl(left);
- // Tag the result and store it in register eax.
- __ SmiTag(left);
- __ mov(eax, left);
- break;
-
- case Token::SHR:
- // Remove tags from operands (but keep sign).
- __ SmiUntag(left);
- __ SmiUntag(ecx);
- // Perform the operation.
- __ shr_cl(left);
- // Check that the *unsigned* result fits in a smi.
- // Neither of the two high-order bits can be set:
- // - 0x80000000: high bit would be lost when smi tagging.
- // - 0x40000000: this number would convert to negative when
- // Smi tagging these two cases can only happen with shifts
- // by 0 or 1 when handed a valid smi.
- __ test(left, Immediate(0xc0000000));
- __ j(not_zero, slow, not_taken);
- // Tag the result and store it in register eax.
- __ SmiTag(left);
- __ mov(eax, left);
- break;
-
- case Token::ADD:
- ASSERT(right.is(eax));
- __ add(right, Operand(left)); // Addition is commutative.
- __ j(overflow, &use_fp_on_smis, not_taken);
- break;
-
- case Token::SUB:
- __ sub(left, Operand(right));
- __ j(overflow, &use_fp_on_smis, not_taken);
- __ mov(eax, left);
- break;
-
- case Token::MUL:
- // If the smi tag is 0 we can just leave the tag on one operand.
- STATIC_ASSERT(kSmiTag == 0); // Adjust code below if not the case.
- // We can't revert the multiplication if the result is not a smi
- // so save the right operand.
- __ mov(ebx, right);
- // Remove tag from one of the operands (but keep sign).
- __ SmiUntag(right);
- // Do multiplication.
- __ imul(right, Operand(left)); // Multiplication is commutative.
- __ j(overflow, &use_fp_on_smis, not_taken);
- // Check for negative zero result. Use combined = left | right.
- __ NegativeZeroTest(right, combined, &use_fp_on_smis);
- break;
-
- case Token::DIV:
- // We can't revert the division if the result is not a smi so
- // save the left operand.
- __ mov(edi, left);
- // Check for 0 divisor.
- __ test(right, Operand(right));
- __ j(zero, &use_fp_on_smis, not_taken);
- // Sign extend left into edx:eax.
- ASSERT(left.is(eax));
- __ cdq();
- // Divide edx:eax by right.
- __ idiv(right);
- // Check for the corner case of dividing the most negative smi by
- // -1. We cannot use the overflow flag, since it is not set by idiv
- // instruction.
- STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
- __ cmp(eax, 0x40000000);
- __ j(equal, &use_fp_on_smis);
- // Check for negative zero result. Use combined = left | right.
- __ NegativeZeroTest(eax, combined, &use_fp_on_smis);
- // Check that the remainder is zero.
- __ test(edx, Operand(edx));
- __ j(not_zero, &use_fp_on_smis);
- // Tag the result and store it in register eax.
- __ SmiTag(eax);
- break;
-
- case Token::MOD:
- // Check for 0 divisor.
- __ test(right, Operand(right));
- __ j(zero, ¬_smis, not_taken);
-
- // Sign extend left into edx:eax.
- ASSERT(left.is(eax));
- __ cdq();
- // Divide edx:eax by right.
- __ idiv(right);
- // Check for negative zero result. Use combined = left | right.
- __ NegativeZeroTest(edx, combined, slow);
- // Move remainder to register eax.
- __ mov(eax, edx);
- break;
-
- default:
- UNREACHABLE();
- }
-
- // 5. Emit return of result in eax.
- GenerateReturn(masm);
-
- // 6. For some operations emit inline code to perform floating point
- // operations on known smis (e.g., if the result of the operation
- // overflowed the smi range).
- switch (op_) {
- case Token::SHL: {
- Comment perform_float(masm, "-- Perform float operation on smis");
- __ bind(&use_fp_on_smis);
- if (runtime_operands_type_ != BinaryOpIC::UNINIT_OR_SMI) {
- // Result we want is in left == edx, so we can put the allocated heap
- // number in eax.
- __ AllocateHeapNumber(eax, ecx, ebx, slow);
- // Store the result in the HeapNumber and return.
- if (masm->isolate()->cpu_features()->IsSupported(SSE2)) {
- CpuFeatures::Scope use_sse2(SSE2);
- __ cvtsi2sd(xmm0, Operand(left));
- __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
- } else {
- // It's OK to overwrite the right argument on the stack because we
- // are about to return.
- __ mov(Operand(esp, 1 * kPointerSize), left);
- __ fild_s(Operand(esp, 1 * kPointerSize));
- __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
- }
- GenerateReturn(masm);
- } else {
- ASSERT(runtime_operands_type_ == BinaryOpIC::UNINIT_OR_SMI);
- __ jmp(slow);
- }
- break;
- }
-
- case Token::ADD:
- case Token::SUB:
- case Token::MUL:
- case Token::DIV: {
- Comment perform_float(masm, "-- Perform float operation on smis");
- __ bind(&use_fp_on_smis);
- // Restore arguments to edx, eax.
- switch (op_) {
- case Token::ADD:
- // Revert right = right + left.
- __ sub(right, Operand(left));
- break;
- case Token::SUB:
- // Revert left = left - right.
- __ add(left, Operand(right));
- break;
- case Token::MUL:
- // Right was clobbered but a copy is in ebx.
- __ mov(right, ebx);
- break;
- case Token::DIV:
- // Left was clobbered but a copy is in edi. Right is in ebx for
- // division.
- __ mov(edx, edi);
- __ mov(eax, right);
- break;
- default: UNREACHABLE();
- break;
- }
- if (runtime_operands_type_ != BinaryOpIC::UNINIT_OR_SMI) {
- __ AllocateHeapNumber(ecx, ebx, no_reg, slow);
- if (masm->isolate()->cpu_features()->IsSupported(SSE2)) {
- CpuFeatures::Scope use_sse2(SSE2);
- FloatingPointHelper::LoadSSE2Smis(masm, ebx);
- switch (op_) {
- case Token::ADD: __ addsd(xmm0, xmm1); break;
- case Token::SUB: __ subsd(xmm0, xmm1); break;
- case Token::MUL: __ mulsd(xmm0, xmm1); break;
- case Token::DIV: __ divsd(xmm0, xmm1); break;
- default: UNREACHABLE();
- }
- __ movdbl(FieldOperand(ecx, HeapNumber::kValueOffset), xmm0);
- } else { // SSE2 not available, use FPU.
- FloatingPointHelper::LoadFloatSmis(masm, ebx);
- switch (op_) {
- case Token::ADD: __ faddp(1); break;
- case Token::SUB: __ fsubp(1); break;
- case Token::MUL: __ fmulp(1); break;
- case Token::DIV: __ fdivp(1); break;
- default: UNREACHABLE();
- }
- __ fstp_d(FieldOperand(ecx, HeapNumber::kValueOffset));
- }
- __ mov(eax, ecx);
- GenerateReturn(masm);
- } else {
- ASSERT(runtime_operands_type_ == BinaryOpIC::UNINIT_OR_SMI);
- __ jmp(slow);
- }
- break;
- }
-
- default:
- break;
- }
-
- // 7. Non-smi operands, fall out to the non-smi code with the operands in
- // edx and eax.
- Comment done_comment(masm, "-- Enter non-smi code");
- __ bind(¬_smis);
- switch (op_) {
- case Token::BIT_OR:
- case Token::SHL:
- case Token::SAR:
- case Token::SHR:
- // Right operand is saved in ecx and eax was destroyed by the smi
- // check.
- __ mov(eax, ecx);
- break;
-
- case Token::DIV:
- case Token::MOD:
- // Operands are in eax, ebx at this point.
- __ mov(edx, eax);
- __ mov(eax, ebx);
- break;
-
- default:
- break;
- }
-}
-
-
-void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
- Label call_runtime;
-
- Counters* counters = masm->isolate()->counters();
- __ IncrementCounter(counters->generic_binary_stub_calls(), 1);
-
- if (runtime_operands_type_ == BinaryOpIC::UNINIT_OR_SMI) {
- Label slow;
- if (ShouldGenerateSmiCode()) GenerateSmiCode(masm, &slow);
- __ bind(&slow);
- GenerateTypeTransition(masm);
- }
-
- // Generate fast case smi code if requested. This flag is set when the fast
- // case smi code is not generated by the caller. Generating it here will speed
- // up common operations.
- if (ShouldGenerateSmiCode()) {
- GenerateSmiCode(masm, &call_runtime);
- } else if (op_ != Token::MOD) { // MOD goes straight to runtime.
- if (!HasArgsInRegisters()) {
- GenerateLoadArguments(masm);
- }
- }
-
- // Floating point case.
- if (ShouldGenerateFPCode()) {
- switch (op_) {
- case Token::ADD:
- case Token::SUB:
- case Token::MUL:
- case Token::DIV: {
- if (runtime_operands_type_ == BinaryOpIC::DEFAULT &&
- HasSmiCodeInStub()) {
- // Execution reaches this point when the first non-smi argument occurs
- // (and only if smi code is generated). This is the right moment to
- // patch to HEAP_NUMBERS state. The transition is attempted only for
- // the four basic operations. The stub stays in the DEFAULT state
- // forever for all other operations (also if smi code is skipped).
- GenerateTypeTransition(masm);
- break;
- }
-
- Label not_floats;
- if (masm->isolate()->cpu_features()->IsSupported(SSE2)) {
- CpuFeatures::Scope use_sse2(SSE2);
- if (static_operands_type_.IsNumber()) {
- if (FLAG_debug_code) {
- // Assert at runtime that inputs are only numbers.
- __ AbortIfNotNumber(edx);
- __ AbortIfNotNumber(eax);
- }
- if (static_operands_type_.IsSmi()) {
- if (FLAG_debug_code) {
- __ AbortIfNotSmi(edx);
- __ AbortIfNotSmi(eax);
- }
- FloatingPointHelper::LoadSSE2Smis(masm, ecx);
- } else {
- FloatingPointHelper::LoadSSE2Operands(masm);
- }
- } else {
- FloatingPointHelper::LoadSSE2Operands(masm, ¬_floats);
- }
-
- switch (op_) {
- case Token::ADD: __ addsd(xmm0, xmm1); break;
- case Token::SUB: __ subsd(xmm0, xmm1); break;
- case Token::MUL: __ mulsd(xmm0, xmm1); break;
- case Token::DIV: __ divsd(xmm0, xmm1); break;
- default: UNREACHABLE();
- }
- GenerateHeapResultAllocation(masm, &call_runtime);
- __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
- GenerateReturn(masm);
- } else { // SSE2 not available, use FPU.
- if (static_operands_type_.IsNumber()) {
- if (FLAG_debug_code) {
- // Assert at runtime that inputs are only numbers.
- __ AbortIfNotNumber(edx);
- __ AbortIfNotNumber(eax);
- }
- } else {
- FloatingPointHelper::CheckFloatOperands(masm, ¬_floats, ebx);
- }
- FloatingPointHelper::LoadFloatOperands(
- masm,
- ecx,
- FloatingPointHelper::ARGS_IN_REGISTERS);
- switch (op_) {
- case Token::ADD: __ faddp(1); break;
- case Token::SUB: __ fsubp(1); break;
- case Token::MUL: __ fmulp(1); break;
- case Token::DIV: __ fdivp(1); break;
- default: UNREACHABLE();
- }
- Label after_alloc_failure;
- GenerateHeapResultAllocation(masm, &after_alloc_failure);
- __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
- GenerateReturn(masm);
- __ bind(&after_alloc_failure);
- __ ffree();
- __ jmp(&call_runtime);
- }
- __ bind(¬_floats);
- if (runtime_operands_type_ == BinaryOpIC::DEFAULT &&
- !HasSmiCodeInStub()) {
- // Execution reaches this point when the first non-number argument
- // occurs (and only if smi code is skipped from the stub, otherwise
- // the patching has already been done earlier in this case branch).
- // Try patching to STRINGS for ADD operation.
- if (op_ == Token::ADD) {
- GenerateTypeTransition(masm);
- }
- }
- break;
- }
- case Token::MOD: {
- // For MOD we go directly to runtime in the non-smi case.
- break;
- }
- case Token::BIT_OR:
- case Token::BIT_AND:
- case Token::BIT_XOR:
- case Token::SAR:
- case Token::SHL:
- case Token::SHR: {
- Label non_smi_result;
- FloatingPointHelper::LoadAsIntegers(masm,
- static_operands_type_,
- use_sse3_,
- &call_runtime);
- switch (op_) {
- case Token::BIT_OR: __ or_(eax, Operand(ecx)); break;
- case Token::BIT_AND: __ and_(eax, Operand(ecx)); break;
- case Token::BIT_XOR: __ xor_(eax, Operand(ecx)); break;
- case Token::SAR: __ sar_cl(eax); break;
- case Token::SHL: __ shl_cl(eax); break;
- case Token::SHR: __ shr_cl(eax); break;
- default: UNREACHABLE();
- }
- if (op_ == Token::SHR) {
- // Check if result is non-negative and fits in a smi.
- __ test(eax, Immediate(0xc0000000));
- __ j(not_zero, &call_runtime);
- } else {
- // Check if result fits in a smi.
- __ cmp(eax, 0xc0000000);
- __ j(negative, &non_smi_result);
- }
- // Tag smi result and return.
- __ SmiTag(eax);
- GenerateReturn(masm);
-
- // All ops except SHR return a signed int32 that we load in
- // a HeapNumber.
- if (op_ != Token::SHR) {
- __ bind(&non_smi_result);
- // Allocate a heap number if needed.
- __ mov(ebx, Operand(eax)); // ebx: result
- NearLabel skip_allocation;
- switch (mode_) {
- case OVERWRITE_LEFT:
- case OVERWRITE_RIGHT:
- // If the operand was an object, we skip the
- // allocation of a heap number.
- __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
- 1 * kPointerSize : 2 * kPointerSize));
- __ test(eax, Immediate(kSmiTagMask));
- __ j(not_zero, &skip_allocation, not_taken);
- // Fall through!
- case NO_OVERWRITE:
- __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
- __ bind(&skip_allocation);
- break;
- default: UNREACHABLE();
- }
- // Store the result in the HeapNumber and return.
- if (masm->isolate()->cpu_features()->IsSupported(SSE2)) {
- CpuFeatures::Scope use_sse2(SSE2);
- __ cvtsi2sd(xmm0, Operand(ebx));
- __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
- } else {
- __ mov(Operand(esp, 1 * kPointerSize), ebx);
- __ fild_s(Operand(esp, 1 * kPointerSize));
- __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
- }
- GenerateReturn(masm);
- }
- break;
- }
- default: UNREACHABLE(); break;
- }
- }
-
- // If all else fails, use the runtime system to get the correct
- // result. If arguments was passed in registers now place them on the
- // stack in the correct order below the return address.
-
- // Avoid hitting the string ADD code below when allocation fails in
- // the floating point code above.
- if (op_ != Token::ADD) {
- __ bind(&call_runtime);
- }
-
- if (HasArgsInRegisters()) {
- GenerateRegisterArgsPush(masm);
- }
-
- switch (op_) {
- case Token::ADD: {
- // Test for string arguments before calling runtime.
-
- // If this stub has already generated FP-specific code then the arguments
- // are already in edx, eax
- if (!ShouldGenerateFPCode() && !HasArgsInRegisters()) {
- GenerateLoadArguments(masm);
- }
-
- // Registers containing left and right operands respectively.
- Register lhs, rhs;
- if (HasArgsReversed()) {
- lhs = eax;
- rhs = edx;
- } else {
- lhs = edx;
- rhs = eax;
- }
-
- // Test if left operand is a string.
- NearLabel lhs_not_string;
- __ test(lhs, Immediate(kSmiTagMask));
- __ j(zero, &lhs_not_string);
- __ CmpObjectType(lhs, FIRST_NONSTRING_TYPE, ecx);
- __ j(above_equal, &lhs_not_string);
-
- StringAddStub string_add_left_stub(NO_STRING_CHECK_LEFT_IN_STUB);
- __ TailCallStub(&string_add_left_stub);
-
- NearLabel call_runtime_with_args;
- // Left operand is not a string, test right.
- __ bind(&lhs_not_string);
- __ test(rhs, Immediate(kSmiTagMask));
- __ j(zero, &call_runtime_with_args);
- __ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, ecx);
- __ j(above_equal, &call_runtime_with_args);
-
- StringAddStub string_add_right_stub(NO_STRING_CHECK_RIGHT_IN_STUB);
- __ TailCallStub(&string_add_right_stub);
-
- // Neither argument is a string.
- __ bind(&call_runtime);
- if (HasArgsInRegisters()) {
- GenerateRegisterArgsPush(masm);
- }
- __ bind(&call_runtime_with_args);
- __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION);
- break;
- }
- case Token::SUB:
- __ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION);
- break;
- case Token::MUL:
- __ InvokeBuiltin(Builtins::MUL, JUMP_FUNCTION);
- break;
- case Token::DIV:
- __ InvokeBuiltin(Builtins::DIV, JUMP_FUNCTION);
- break;
- case Token::MOD:
- __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
- break;
- case Token::BIT_OR:
- __ InvokeBuiltin(Builtins::BIT_OR, JUMP_FUNCTION);
- break;
- case Token::BIT_AND:
- __ InvokeBuiltin(Builtins::BIT_AND, JUMP_FUNCTION);
- break;
- case Token::BIT_XOR:
- __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_FUNCTION);
- break;
- case Token::SAR:
- __ InvokeBuiltin(Builtins::SAR, JUMP_FUNCTION);
- break;
- case Token::SHL:
- __ InvokeBuiltin(Builtins::SHL, JUMP_FUNCTION);
- break;
- case Token::SHR:
- __ InvokeBuiltin(Builtins::SHR, JUMP_FUNCTION);
- break;
- default:
- UNREACHABLE();
- }
-}
-
-
-void GenericBinaryOpStub::GenerateHeapResultAllocation(MacroAssembler* masm,
- Label* alloc_failure) {
- Label skip_allocation;
- OverwriteMode mode = mode_;
- if (HasArgsReversed()) {
- if (mode == OVERWRITE_RIGHT) {
- mode = OVERWRITE_LEFT;
- } else if (mode == OVERWRITE_LEFT) {
- mode = OVERWRITE_RIGHT;
- }
- }
- switch (mode) {
- case OVERWRITE_LEFT: {
- // If the argument in edx is already an object, we skip the
- // allocation of a heap number.
- __ test(edx, Immediate(kSmiTagMask));
- __ j(not_zero, &skip_allocation, not_taken);
- // Allocate a heap number for the result. Keep eax and edx intact
- // for the possible runtime call.
- __ AllocateHeapNumber(ebx, ecx, no_reg, alloc_failure);
- // Now edx can be overwritten losing one of the arguments as we are
- // now done and will not need it any more.
- __ mov(edx, Operand(ebx));
- __ bind(&skip_allocation);
- // Use object in edx as a result holder
- __ mov(eax, Operand(edx));
- break;
- }
- case OVERWRITE_RIGHT:
- // If the argument in eax is already an object, we skip the
- // allocation of a heap number.
- __ test(eax, Immediate(kSmiTagMask));
- __ j(not_zero, &skip_allocation, not_taken);
- // Fall through!
- case NO_OVERWRITE:
- // Allocate a heap number for the result. Keep eax and edx intact
- // for the possible runtime call.
- __ AllocateHeapNumber(ebx, ecx, no_reg, alloc_failure);
- // Now eax can be overwritten losing one of the arguments as we are
- // now done and will not need it any more.
- __ mov(eax, ebx);
- __ bind(&skip_allocation);
- break;
- default: UNREACHABLE();
- }
-}
-
-
-void GenericBinaryOpStub::GenerateLoadArguments(MacroAssembler* masm) {
- // If arguments are not passed in registers read them from the stack.
- ASSERT(!HasArgsInRegisters());
- __ mov(eax, Operand(esp, 1 * kPointerSize));
- __ mov(edx, Operand(esp, 2 * kPointerSize));
-}
-
-
-void GenericBinaryOpStub::GenerateReturn(MacroAssembler* masm) {
- // If arguments are not passed in registers remove them from the stack before
- // returning.
- if (!HasArgsInRegisters()) {
- __ ret(2 * kPointerSize); // Remove both operands
- } else {
- __ ret(0);
- }
-}
-
-
-void GenericBinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
- ASSERT(HasArgsInRegisters());
- __ pop(ecx);
- if (HasArgsReversed()) {
- __ push(eax);
- __ push(edx);
- } else {
- __ push(edx);
- __ push(eax);
- }
- __ push(ecx);
-}
-
-
-void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
- // Ensure the operands are on the stack.
- if (HasArgsInRegisters()) {
- GenerateRegisterArgsPush(masm);
- }
-
- __ pop(ecx); // Save return address.
-
- // Left and right arguments are now on top.
- // Push this stub's key. Although the operation and the type info are
- // encoded into the key, the encoding is opaque, so push them too.
- __ push(Immediate(Smi::FromInt(MinorKey())));
- __ push(Immediate(Smi::FromInt(op_)));
- __ push(Immediate(Smi::FromInt(runtime_operands_type_)));
-
- __ push(ecx); // Push return address.
-
- // Patch the caller to an appropriate specialized stub and return the
- // operation result to the caller of the stub.
- __ TailCallExternalReference(
- ExternalReference(IC_Utility(IC::kBinaryOp_Patch), masm->isolate()),
- 5,
- 1);
-}
-
-
-Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
- GenericBinaryOpStub stub(key, type_info);
- return stub.GetCode();
-}
-
-
Handle<Code> GetTypeRecordingBinaryOpStub(int key,
TRBinaryOpIC::TypeInfo type_info,
TRBinaryOpIC::TypeInfo result_type_info) {
@@ -1362,6 +446,9 @@
case TRBinaryOpIC::ODDBALL:
GenerateOddballStub(masm);
break;
+ case TRBinaryOpIC::BOTH_STRING:
+ GenerateBothStringStub(masm);
+ break;
case TRBinaryOpIC::STRING:
GenerateStringStub(masm);
break;
@@ -1660,7 +747,7 @@
// number in eax.
__ AllocateHeapNumber(eax, ecx, ebx, slow);
// Store the result in the HeapNumber and return.
- if (masm->isolate()->cpu_features()->IsSupported(SSE2)) {
+ if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope use_sse2(SSE2);
__ cvtsi2sd(xmm0, Operand(left));
__ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
@@ -1705,7 +792,7 @@
break;
}
__ AllocateHeapNumber(ecx, ebx, no_reg, slow);
- if (masm->isolate()->cpu_features()->IsSupported(SSE2)) {
+ if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope use_sse2(SSE2);
FloatingPointHelper::LoadSSE2Smis(masm, ebx);
switch (op_) {
@@ -1825,6 +912,38 @@
}
+void TypeRecordingBinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
+ Label call_runtime;
+ ASSERT(operands_type_ == TRBinaryOpIC::BOTH_STRING);
+ ASSERT(op_ == Token::ADD);
+ // If both arguments are strings, call the string add stub.
+ // Otherwise, do a transition.
+
+ // Registers containing left and right operands respectively.
+ Register left = edx;
+ Register right = eax;
+
+ // Test if left operand is a string.
+ __ test(left, Immediate(kSmiTagMask));
+ __ j(zero, &call_runtime);
+ __ CmpObjectType(left, FIRST_NONSTRING_TYPE, ecx);
+ __ j(above_equal, &call_runtime);
+
+ // Test if right operand is a string.
+ __ test(right, Immediate(kSmiTagMask));
+ __ j(zero, &call_runtime);
+ __ CmpObjectType(right, FIRST_NONSTRING_TYPE, ecx);
+ __ j(above_equal, &call_runtime);
+
+ StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_stub);
+
+ __ bind(&call_runtime);
+ GenerateTypeTransition(masm);
+}
+
+
void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
Label call_runtime;
ASSERT(operands_type_ == TRBinaryOpIC::INT32);
@@ -1837,7 +956,7 @@
case Token::DIV: {
Label not_floats;
Label not_int32;
- if (masm->isolate()->cpu_features()->IsSupported(SSE2)) {
+ if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope use_sse2(SSE2);
FloatingPointHelper::LoadSSE2Operands(masm, ¬_floats);
FloatingPointHelper::CheckSSE2OperandsAreInt32(masm, ¬_int32, ecx);
@@ -1958,7 +1077,7 @@
default: UNREACHABLE();
}
// Store the result in the HeapNumber and return.
- if (masm->isolate()->cpu_features()->IsSupported(SSE2)) {
+ if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope use_sse2(SSE2);
__ cvtsi2sd(xmm0, Operand(ebx));
__ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
@@ -2036,23 +1155,25 @@
GenerateAddStrings(masm);
}
+ Factory* factory = masm->isolate()->factory();
+
// Convert odd ball arguments to numbers.
NearLabel check, done;
- __ cmp(edx, FACTORY->undefined_value());
+ __ cmp(edx, factory->undefined_value());
__ j(not_equal, &check);
if (Token::IsBitOp(op_)) {
__ xor_(edx, Operand(edx));
} else {
- __ mov(edx, Immediate(FACTORY->nan_value()));
+ __ mov(edx, Immediate(factory->nan_value()));
}
__ jmp(&done);
__ bind(&check);
- __ cmp(eax, FACTORY->undefined_value());
+ __ cmp(eax, factory->undefined_value());
__ j(not_equal, &done);
if (Token::IsBitOp(op_)) {
__ xor_(eax, Operand(eax));
} else {
- __ mov(eax, Immediate(FACTORY->nan_value()));
+ __ mov(eax, Immediate(factory->nan_value()));
}
__ bind(&done);
@@ -2070,7 +1191,7 @@
case Token::MUL:
case Token::DIV: {
Label not_floats;
- if (masm->isolate()->cpu_features()->IsSupported(SSE2)) {
+ if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope use_sse2(SSE2);
FloatingPointHelper::LoadSSE2Operands(masm, ¬_floats);
@@ -2173,7 +1294,7 @@
default: UNREACHABLE();
}
// Store the result in the HeapNumber and return.
- if (masm->isolate()->cpu_features()->IsSupported(SSE2)) {
+ if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope use_sse2(SSE2);
__ cvtsi2sd(xmm0, Operand(ebx));
__ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
@@ -2275,7 +1396,7 @@
case Token::MUL:
case Token::DIV: {
Label not_floats;
- if (masm->isolate()->cpu_features()->IsSupported(SSE2)) {
+ if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope use_sse2(SSE2);
FloatingPointHelper::LoadSSE2Operands(masm, ¬_floats);
@@ -2373,7 +1494,7 @@
default: UNREACHABLE();
}
// Store the result in the HeapNumber and return.
- if (masm->isolate()->cpu_features()->IsSupported(SSE2)) {
+ if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope use_sse2(SSE2);
__ cvtsi2sd(xmm0, Operand(ebx));
__ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
@@ -2572,7 +1693,7 @@
__ bind(&loaded);
} else { // UNTAGGED.
- if (masm->isolate()->cpu_features()->IsSupported(SSE4_1)) {
+ if (CpuFeatures::IsSupported(SSE4_1)) {
CpuFeatures::Scope sse4_scope(SSE4_1);
__ pextrd(Operand(edx), xmm1, 0x1); // copy xmm1[63..32] to edx.
} else {
@@ -2826,8 +1947,7 @@
Label done, right_exponent, normal_exponent;
Register scratch = ebx;
Register scratch2 = edi;
- if (type_info.IsInteger32() &&
- masm->isolate()->cpu_features()->IsEnabled(SSE2)) {
+ if (type_info.IsInteger32() && CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope scope(SSE2);
__ cvttsd2si(ecx, FieldOperand(source, HeapNumber::kValueOffset));
return;
@@ -3375,7 +2495,7 @@
IntegerConvert(masm,
eax,
TypeInfo::Unknown(),
- masm->isolate()->cpu_features()->IsSupported(SSE3),
+ CpuFeatures::IsSupported(SSE3),
&slow);
// Do the bitwise operation and check if the result fits in a smi.
@@ -3398,7 +2518,7 @@
__ AllocateHeapNumber(ebx, edx, edi, &slow);
__ mov(eax, Operand(ebx));
}
- if (masm->isolate()->cpu_features()->IsSupported(SSE2)) {
+ if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope use_sse2(SSE2);
__ cvtsi2sd(xmm0, Operand(ecx));
__ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
@@ -4270,7 +3390,7 @@
FixedArray::kHeaderSize));
__ test(probe, Immediate(kSmiTagMask));
__ j(zero, not_found);
- if (masm->isolate()->cpu_features()->IsSupported(SSE2)) {
+ if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope fscope(SSE2);
__ movdbl(xmm0, FieldOperand(object, HeapNumber::kValueOffset));
__ movdbl(xmm1, FieldOperand(probe, HeapNumber::kValueOffset));
@@ -4509,7 +3629,7 @@
if (include_number_compare_) {
Label non_number_comparison;
Label unordered;
- if (masm->isolate()->cpu_features()->IsSupported(SSE2)) {
+ if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope use_sse2(SSE2);
CpuFeatures::Scope use_cmov(CMOV);
@@ -6455,8 +5575,7 @@
// Inlining the double comparison and falling back to the general compare
// stub if NaN is involved or SS2 or CMOV is unsupported.
- CpuFeatures* cpu_features = masm->isolate()->cpu_features();
- if (cpu_features->IsSupported(SSE2) && cpu_features->IsSupported(CMOV)) {
+ if (CpuFeatures::IsSupported(SSE2) && CpuFeatures::IsSupported(CMOV)) {
CpuFeatures::Scope scope1(SSE2);
CpuFeatures::Scope scope2(CMOV);
diff --git a/src/ia32/code-stubs-ia32.h b/src/ia32/code-stubs-ia32.h
index 31fa645..cf73682 100644
--- a/src/ia32/code-stubs-ia32.h
+++ b/src/ia32/code-stubs-ia32.h
@@ -72,161 +72,6 @@
};
-// Flag that indicates how to generate code for the stub GenericBinaryOpStub.
-enum GenericBinaryFlags {
- NO_GENERIC_BINARY_FLAGS = 0,
- NO_SMI_CODE_IN_STUB = 1 << 0 // Omit smi code in stub.
-};
-
-
-class GenericBinaryOpStub: public CodeStub {
- public:
- GenericBinaryOpStub(Token::Value op,
- OverwriteMode mode,
- GenericBinaryFlags flags,
- TypeInfo operands_type)
- : op_(op),
- mode_(mode),
- flags_(flags),
- args_in_registers_(false),
- args_reversed_(false),
- static_operands_type_(operands_type),
- runtime_operands_type_(BinaryOpIC::UNINIT_OR_SMI),
- name_(NULL) {
- if (static_operands_type_.IsSmi()) {
- mode_ = NO_OVERWRITE;
- }
- use_sse3_ = Isolate::Current()->cpu_features()->IsSupported(SSE3);
- ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
- }
-
- GenericBinaryOpStub(int key, BinaryOpIC::TypeInfo runtime_operands_type)
- : op_(OpBits::decode(key)),
- mode_(ModeBits::decode(key)),
- flags_(FlagBits::decode(key)),
- args_in_registers_(ArgsInRegistersBits::decode(key)),
- args_reversed_(ArgsReversedBits::decode(key)),
- use_sse3_(SSE3Bits::decode(key)),
- static_operands_type_(TypeInfo::ExpandedRepresentation(
- StaticTypeInfoBits::decode(key))),
- runtime_operands_type_(runtime_operands_type),
- name_(NULL) {
- }
-
- // Generate code to call the stub with the supplied arguments. This will add
- // code at the call site to prepare arguments either in registers or on the
- // stack together with the actual call.
- void GenerateCall(MacroAssembler* masm, Register left, Register right);
- void GenerateCall(MacroAssembler* masm, Register left, Smi* right);
- void GenerateCall(MacroAssembler* masm, Smi* left, Register right);
-
- bool ArgsInRegistersSupported() {
- return op_ == Token::ADD || op_ == Token::SUB
- || op_ == Token::MUL || op_ == Token::DIV;
- }
-
- void SetArgsInRegisters() {
- ASSERT(ArgsInRegistersSupported());
- args_in_registers_ = true;
- }
-
- private:
- Token::Value op_;
- OverwriteMode mode_;
- GenericBinaryFlags flags_;
- bool args_in_registers_; // Arguments passed in registers not on the stack.
- bool args_reversed_; // Left and right argument are swapped.
- bool use_sse3_;
-
- // Number type information of operands, determined by code generator.
- TypeInfo static_operands_type_;
-
- // Operand type information determined at runtime.
- BinaryOpIC::TypeInfo runtime_operands_type_;
-
- char* name_;
-
- const char* GetName();
-
-#ifdef DEBUG
- void Print() {
- PrintF("GenericBinaryOpStub %d (op %s), "
- "(mode %d, flags %d, registers %d, reversed %d, type_info %s)\n",
- MinorKey(),
- Token::String(op_),
- static_cast<int>(mode_),
- static_cast<int>(flags_),
- static_cast<int>(args_in_registers_),
- static_cast<int>(args_reversed_),
- static_operands_type_.ToString());
- }
-#endif
-
- // Minor key encoding in 18 bits RRNNNFRASOOOOOOOMM.
- class ModeBits: public BitField<OverwriteMode, 0, 2> {};
- class OpBits: public BitField<Token::Value, 2, 7> {};
- class SSE3Bits: public BitField<bool, 9, 1> {};
- class ArgsInRegistersBits: public BitField<bool, 10, 1> {};
- class ArgsReversedBits: public BitField<bool, 11, 1> {};
- class FlagBits: public BitField<GenericBinaryFlags, 12, 1> {};
- class StaticTypeInfoBits: public BitField<int, 13, 3> {};
- class RuntimeTypeInfoBits: public BitField<BinaryOpIC::TypeInfo, 16, 3> {};
-
- Major MajorKey() { return GenericBinaryOp; }
- int MinorKey() {
- // Encode the parameters in a unique 18 bit value.
- return OpBits::encode(op_)
- | ModeBits::encode(mode_)
- | FlagBits::encode(flags_)
- | SSE3Bits::encode(use_sse3_)
- | ArgsInRegistersBits::encode(args_in_registers_)
- | ArgsReversedBits::encode(args_reversed_)
- | StaticTypeInfoBits::encode(
- static_operands_type_.ThreeBitRepresentation())
- | RuntimeTypeInfoBits::encode(runtime_operands_type_);
- }
-
- void Generate(MacroAssembler* masm);
- void GenerateSmiCode(MacroAssembler* masm, Label* slow);
- void GenerateLoadArguments(MacroAssembler* masm);
- void GenerateReturn(MacroAssembler* masm);
- void GenerateHeapResultAllocation(MacroAssembler* masm, Label* alloc_failure);
- void GenerateRegisterArgsPush(MacroAssembler* masm);
- void GenerateTypeTransition(MacroAssembler* masm);
-
- bool IsOperationCommutative() {
- return (op_ == Token::ADD) || (op_ == Token::MUL);
- }
-
- void SetArgsReversed() { args_reversed_ = true; }
- bool HasSmiCodeInStub() { return (flags_ & NO_SMI_CODE_IN_STUB) == 0; }
- bool HasArgsInRegisters() { return args_in_registers_; }
- bool HasArgsReversed() { return args_reversed_; }
-
- bool ShouldGenerateSmiCode() {
- return HasSmiCodeInStub() &&
- runtime_operands_type_ != BinaryOpIC::HEAP_NUMBERS &&
- runtime_operands_type_ != BinaryOpIC::STRINGS;
- }
-
- bool ShouldGenerateFPCode() {
- return runtime_operands_type_ != BinaryOpIC::STRINGS;
- }
-
- virtual int GetCodeKind() { return Code::BINARY_OP_IC; }
-
- virtual InlineCacheState GetICState() {
- return BinaryOpIC::ToState(runtime_operands_type_);
- }
-
- virtual void FinishCode(Code* code) {
- code->set_binary_op_type(runtime_operands_type_);
- }
-
- friend class CodeGenerator;
-};
-
-
class TypeRecordingBinaryOpStub: public CodeStub {
public:
TypeRecordingBinaryOpStub(Token::Value op, OverwriteMode mode)
@@ -235,7 +80,7 @@
operands_type_(TRBinaryOpIC::UNINITIALIZED),
result_type_(TRBinaryOpIC::UNINITIALIZED),
name_(NULL) {
- use_sse3_ = Isolate::Current()->cpu_features()->IsSupported(SSE3);
+ use_sse3_ = CpuFeatures::IsSupported(SSE3);
ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
}
@@ -308,6 +153,7 @@
void GenerateHeapNumberStub(MacroAssembler* masm);
void GenerateOddballStub(MacroAssembler* masm);
void GenerateStringStub(MacroAssembler* masm);
+ void GenerateBothStringStub(MacroAssembler* masm);
void GenerateGenericStub(MacroAssembler* masm);
void GenerateAddStrings(MacroAssembler* masm);
diff --git a/src/ia32/codegen-ia32-inl.h b/src/ia32/codegen-ia32-inl.h
deleted file mode 100644
index 49c706d..0000000
--- a/src/ia32/codegen-ia32-inl.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-#ifndef V8_IA32_CODEGEN_IA32_INL_H_
-#define V8_IA32_CODEGEN_IA32_INL_H_
-
-namespace v8 {
-namespace internal {
-
-#define __ ACCESS_MASM(masm_)
-
-// Platform-specific inline functions.
-
-void DeferredCode::Jump() { __ jmp(&entry_label_); }
-void DeferredCode::Branch(Condition cc) { __ j(cc, &entry_label_); }
-
-#undef __
-
-} } // namespace v8::internal
-
-#endif // V8_IA32_CODEGEN_IA32_INL_H_
diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc
index cf990a0..572c36c 100644
--- a/src/ia32/codegen-ia32.cc
+++ b/src/ia32/codegen-ia32.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -29,81 +29,15 @@
#if defined(V8_TARGET_ARCH_IA32)
-#include "codegen-inl.h"
-#include "bootstrapper.h"
-#include "code-stubs.h"
-#include "compiler.h"
-#include "debug.h"
-#include "ic-inl.h"
-#include "parser.h"
-#include "regexp-macro-assembler.h"
-#include "register-allocator-inl.h"
-#include "scopes.h"
-#include "virtual-frame-inl.h"
+#include "codegen.h"
namespace v8 {
namespace internal {
-#define __ ACCESS_MASM(masm)
-
-// -------------------------------------------------------------------------
-// Platform-specific FrameRegisterState functions.
-
-void FrameRegisterState::Save(MacroAssembler* masm) const {
- for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
- int action = registers_[i];
- if (action == kPush) {
- __ push(RegisterAllocator::ToRegister(i));
- } else if (action != kIgnore && (action & kSyncedFlag) == 0) {
- __ mov(Operand(ebp, action), RegisterAllocator::ToRegister(i));
- }
- }
-}
-
-
-void FrameRegisterState::Restore(MacroAssembler* masm) const {
- // Restore registers in reverse order due to the stack.
- for (int i = RegisterAllocator::kNumRegisters - 1; i >= 0; i--) {
- int action = registers_[i];
- if (action == kPush) {
- __ pop(RegisterAllocator::ToRegister(i));
- } else if (action != kIgnore) {
- action &= ~kSyncedFlag;
- __ mov(RegisterAllocator::ToRegister(i), Operand(ebp, action));
- }
- }
-}
-
-
-#undef __
-#define __ ACCESS_MASM(masm_)
-
-// -------------------------------------------------------------------------
-// Platform-specific DeferredCode functions.
-
-void DeferredCode::SaveRegisters() {
- frame_state_.Save(masm_);
-}
-
-
-void DeferredCode::RestoreRegisters() {
- frame_state_.Restore(masm_);
-}
-
// -------------------------------------------------------------------------
// Platform-specific RuntimeCallHelper functions.
-void VirtualFrameRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
- frame_state_->Save(masm);
-}
-
-
-void VirtualFrameRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
- frame_state_->Restore(masm);
-}
-
-
void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
masm->EnterInternalFrame();
}
@@ -114,10069 +48,21 @@
}
-// -------------------------------------------------------------------------
-// CodeGenState implementation.
-
-CodeGenState::CodeGenState(CodeGenerator* owner)
- : owner_(owner),
- destination_(NULL),
- previous_(NULL) {
- owner_->set_state(this);
-}
-
-
-CodeGenState::CodeGenState(CodeGenerator* owner,
- ControlDestination* destination)
- : owner_(owner),
- destination_(destination),
- previous_(owner->state()) {
- owner_->set_state(this);
-}
-
-
-CodeGenState::~CodeGenState() {
- ASSERT(owner_->state() == this);
- owner_->set_state(previous_);
-}
-
-// -------------------------------------------------------------------------
-// CodeGenerator implementation.
-
-CodeGenerator::CodeGenerator(MacroAssembler* masm)
- : deferred_(8),
- masm_(masm),
- info_(NULL),
- frame_(NULL),
- allocator_(NULL),
- state_(NULL),
- loop_nesting_(0),
- in_safe_int32_mode_(false),
- safe_int32_mode_enabled_(true),
- function_return_is_shadowed_(false),
- in_spilled_code_(false),
- jit_cookie_((FLAG_mask_constants_with_cookie) ?
- V8::RandomPrivate(Isolate::Current()) : 0) {
-}
-
-
-// Calling conventions:
-// ebp: caller's frame pointer
-// esp: stack pointer
-// edi: called JS function
-// esi: callee's context
-
-void CodeGenerator::Generate(CompilationInfo* info) {
- // Record the position for debugging purposes.
- CodeForFunctionPosition(info->function());
- Comment cmnt(masm_, "[ function compiled by virtual frame code generator");
-
- // Initialize state.
- info_ = info;
- ASSERT(allocator_ == NULL);
- RegisterAllocator register_allocator(this);
- allocator_ = ®ister_allocator;
- ASSERT(frame_ == NULL);
- frame_ = new VirtualFrame();
- set_in_spilled_code(false);
-
- // Adjust for function-level loop nesting.
- ASSERT_EQ(0, loop_nesting_);
- loop_nesting_ = info->is_in_loop() ? 1 : 0;
-
- masm()->isolate()->set_jump_target_compiling_deferred_code(false);
-
- {
- CodeGenState state(this);
-
- // Entry:
- // Stack: receiver, arguments, return address.
- // ebp: caller's frame pointer
- // esp: stack pointer
- // edi: called JS function
- // esi: callee's context
- allocator_->Initialize();
-
-#ifdef DEBUG
- if (strlen(FLAG_stop_at) > 0 &&
- info->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
- frame_->SpillAll();
- __ int3();
- }
-#endif
-
- frame_->Enter();
-
- // Allocate space for locals and initialize them.
- frame_->AllocateStackSlots();
-
- // Allocate the local context if needed.
- int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
- if (heap_slots > 0) {
- Comment cmnt(masm_, "[ allocate local context");
- // Allocate local context.
- // Get outer context and create a new context based on it.
- frame_->PushFunction();
- Result context;
- if (heap_slots <= FastNewContextStub::kMaximumSlots) {
- FastNewContextStub stub(heap_slots);
- context = frame_->CallStub(&stub, 1);
- } else {
- context = frame_->CallRuntime(Runtime::kNewContext, 1);
- }
-
- // Update context local.
- frame_->SaveContextRegister();
-
- // Verify that the runtime call result and esi agree.
- if (FLAG_debug_code) {
- __ cmp(context.reg(), Operand(esi));
- __ Assert(equal, "Runtime::NewContext should end up in esi");
- }
- }
-
- // TODO(1241774): Improve this code:
- // 1) only needed if we have a context
- // 2) no need to recompute context ptr every single time
- // 3) don't copy parameter operand code from SlotOperand!
- {
- Comment cmnt2(masm_, "[ copy context parameters into .context");
- // Note that iteration order is relevant here! If we have the same
- // parameter twice (e.g., function (x, y, x)), and that parameter
- // needs to be copied into the context, it must be the last argument
- // passed to the parameter that needs to be copied. This is a rare
- // case so we don't check for it, instead we rely on the copying
- // order: such a parameter is copied repeatedly into the same
- // context location and thus the last value is what is seen inside
- // the function.
- for (int i = 0; i < scope()->num_parameters(); i++) {
- Variable* par = scope()->parameter(i);
- Slot* slot = par->AsSlot();
- if (slot != NULL && slot->type() == Slot::CONTEXT) {
- // The use of SlotOperand below is safe in unspilled code
- // because the slot is guaranteed to be a context slot.
- //
- // There are no parameters in the global scope.
- ASSERT(!scope()->is_global_scope());
- frame_->PushParameterAt(i);
- Result value = frame_->Pop();
- value.ToRegister();
-
- // SlotOperand loads context.reg() with the context object
- // stored to, used below in RecordWrite.
- Result context = allocator_->Allocate();
- ASSERT(context.is_valid());
- __ mov(SlotOperand(slot, context.reg()), value.reg());
- int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
- Result scratch = allocator_->Allocate();
- ASSERT(scratch.is_valid());
- frame_->Spill(context.reg());
- frame_->Spill(value.reg());
- __ RecordWrite(context.reg(), offset, value.reg(), scratch.reg());
- }
- }
- }
-
- // Store the arguments object. This must happen after context
- // initialization because the arguments object may be stored in
- // the context.
- if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) {
- StoreArgumentsObject(true);
- }
-
- // Initialize ThisFunction reference if present.
- if (scope()->is_function_scope() && scope()->function() != NULL) {
- frame_->Push(FACTORY->the_hole_value());
- StoreToSlot(scope()->function()->AsSlot(), NOT_CONST_INIT);
- }
-
-
- // Initialize the function return target after the locals are set
- // up, because it needs the expected frame height from the frame.
- function_return_.set_direction(JumpTarget::BIDIRECTIONAL);
- function_return_is_shadowed_ = false;
-
- // Generate code to 'execute' declarations and initialize functions
- // (source elements). In case of an illegal redeclaration we need to
- // handle that instead of processing the declarations.
- if (scope()->HasIllegalRedeclaration()) {
- Comment cmnt(masm_, "[ illegal redeclarations");
- scope()->VisitIllegalRedeclaration(this);
- } else {
- Comment cmnt(masm_, "[ declarations");
- ProcessDeclarations(scope()->declarations());
- // Bail out if a stack-overflow exception occurred when processing
- // declarations.
- if (HasStackOverflow()) return;
- }
-
- if (FLAG_trace) {
- frame_->CallRuntime(Runtime::kTraceEnter, 0);
- // Ignore the return value.
- }
- CheckStack();
-
- // Compile the body of the function in a vanilla state. Don't
- // bother compiling all the code if the scope has an illegal
- // redeclaration.
- if (!scope()->HasIllegalRedeclaration()) {
- Comment cmnt(masm_, "[ function body");
-#ifdef DEBUG
- bool is_builtin = info->isolate()->bootstrapper()->IsActive();
- bool should_trace =
- is_builtin ? FLAG_trace_builtin_calls : FLAG_trace_calls;
- if (should_trace) {
- frame_->CallRuntime(Runtime::kDebugTrace, 0);
- // Ignore the return value.
- }
-#endif
- VisitStatements(info->function()->body());
-
- // Handle the return from the function.
- if (has_valid_frame()) {
- // If there is a valid frame, control flow can fall off the end of
- // the body. In that case there is an implicit return statement.
- ASSERT(!function_return_is_shadowed_);
- CodeForReturnPosition(info->function());
- frame_->PrepareForReturn();
- Result undefined(FACTORY->undefined_value());
- if (function_return_.is_bound()) {
- function_return_.Jump(&undefined);
- } else {
- function_return_.Bind(&undefined);
- GenerateReturnSequence(&undefined);
- }
- } else if (function_return_.is_linked()) {
- // If the return target has dangling jumps to it, then we have not
- // yet generated the return sequence. This can happen when (a)
- // control does not flow off the end of the body so we did not
- // compile an artificial return statement just above, and (b) there
- // are return statements in the body but (c) they are all shadowed.
- Result return_value;
- function_return_.Bind(&return_value);
- GenerateReturnSequence(&return_value);
- }
- }
- }
-
- // Adjust for function-level loop nesting.
- ASSERT_EQ(loop_nesting_, info->is_in_loop() ? 1 : 0);
- loop_nesting_ = 0;
-
- // Code generation state must be reset.
- ASSERT(state_ == NULL);
- ASSERT(!function_return_is_shadowed_);
- function_return_.Unuse();
- DeleteFrame();
-
- // Process any deferred code using the register allocator.
- if (!HasStackOverflow()) {
- info->isolate()->set_jump_target_compiling_deferred_code(true);
- ProcessDeferred();
- info->isolate()->set_jump_target_compiling_deferred_code(false);
- }
-
- // There is no need to delete the register allocator, it is a
- // stack-allocated local.
- allocator_ = NULL;
-}
-
-
-Operand CodeGenerator::SlotOperand(Slot* slot, Register tmp) {
- // Currently, this assertion will fail if we try to assign to
- // a constant variable that is constant because it is read-only
- // (such as the variable referring to a named function expression).
- // We need to implement assignments to read-only variables.
- // Ideally, we should do this during AST generation (by converting
- // such assignments into expression statements); however, in general
- // we may not be able to make the decision until past AST generation,
- // that is when the entire program is known.
- ASSERT(slot != NULL);
- int index = slot->index();
- switch (slot->type()) {
- case Slot::PARAMETER:
- return frame_->ParameterAt(index);
-
- case Slot::LOCAL:
- return frame_->LocalAt(index);
-
- case Slot::CONTEXT: {
- // Follow the context chain if necessary.
- ASSERT(!tmp.is(esi)); // do not overwrite context register
- Register context = esi;
- int chain_length = scope()->ContextChainLength(slot->var()->scope());
- for (int i = 0; i < chain_length; i++) {
- // Load the closure.
- // (All contexts, even 'with' contexts, have a closure,
- // and it is the same for all contexts inside a function.
- // There is no need to go to the function context first.)
- __ mov(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
- // Load the function context (which is the incoming, outer context).
- __ mov(tmp, FieldOperand(tmp, JSFunction::kContextOffset));
- context = tmp;
- }
- // We may have a 'with' context now. Get the function context.
- // (In fact this mov may never be the needed, since the scope analysis
- // may not permit a direct context access in this case and thus we are
- // always at a function context. However it is safe to dereference be-
- // cause the function context of a function context is itself. Before
- // deleting this mov we should try to create a counter-example first,
- // though...)
- __ mov(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
- return ContextOperand(tmp, index);
- }
-
- default:
- UNREACHABLE();
- return Operand(eax);
- }
-}
-
-
-Operand CodeGenerator::ContextSlotOperandCheckExtensions(Slot* slot,
- Result tmp,
- JumpTarget* slow) {
- ASSERT(slot->type() == Slot::CONTEXT);
- ASSERT(tmp.is_register());
- Register context = esi;
-
- for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) {
- if (s->num_heap_slots() > 0) {
- if (s->calls_eval()) {
- // Check that extension is NULL.
- __ cmp(ContextOperand(context, Context::EXTENSION_INDEX),
- Immediate(0));
- slow->Branch(not_equal, not_taken);
- }
- __ mov(tmp.reg(), ContextOperand(context, Context::CLOSURE_INDEX));
- __ mov(tmp.reg(), FieldOperand(tmp.reg(), JSFunction::kContextOffset));
- context = tmp.reg();
- }
- }
- // Check that last extension is NULL.
- __ cmp(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0));
- slow->Branch(not_equal, not_taken);
- __ mov(tmp.reg(), ContextOperand(context, Context::FCONTEXT_INDEX));
- return ContextOperand(tmp.reg(), slot->index());
-}
-
-
-// Emit code to load the value of an expression to the top of the
-// frame. If the expression is boolean-valued it may be compiled (or
-// partially compiled) into control flow to the control destination.
-// If force_control is true, control flow is forced.
-void CodeGenerator::LoadCondition(Expression* expr,
- ControlDestination* dest,
- bool force_control) {
- ASSERT(!in_spilled_code());
- int original_height = frame_->height();
-
- { CodeGenState new_state(this, dest);
- Visit(expr);
-
- // If we hit a stack overflow, we may not have actually visited
- // the expression. In that case, we ensure that we have a
- // valid-looking frame state because we will continue to generate
- // code as we unwind the C++ stack.
- //
- // It's possible to have both a stack overflow and a valid frame
- // state (eg, a subexpression overflowed, visiting it returned
- // with a dummied frame state, and visiting this expression
- // returned with a normal-looking state).
- if (HasStackOverflow() &&
- !dest->is_used() &&
- frame_->height() == original_height) {
- dest->Goto(true);
- }
- }
-
- if (force_control && !dest->is_used()) {
- // Convert the TOS value into flow to the control destination.
- ToBoolean(dest);
- }
-
- ASSERT(!(force_control && !dest->is_used()));
- ASSERT(dest->is_used() || frame_->height() == original_height + 1);
-}
-
-
-void CodeGenerator::LoadAndSpill(Expression* expression) {
- ASSERT(in_spilled_code());
- set_in_spilled_code(false);
- Load(expression);
- frame_->SpillAll();
- set_in_spilled_code(true);
-}
-
-
-void CodeGenerator::LoadInSafeInt32Mode(Expression* expr,
- BreakTarget* unsafe_bailout) {
- set_unsafe_bailout(unsafe_bailout);
- set_in_safe_int32_mode(true);
- Load(expr);
- Result value = frame_->Pop();
- ASSERT(frame_->HasNoUntaggedInt32Elements());
- if (expr->GuaranteedSmiResult()) {
- ConvertInt32ResultToSmi(&value);
- } else {
- ConvertInt32ResultToNumber(&value);
- }
- set_in_safe_int32_mode(false);
- set_unsafe_bailout(NULL);
- frame_->Push(&value);
-}
-
-
-void CodeGenerator::LoadWithSafeInt32ModeDisabled(Expression* expr) {
- set_safe_int32_mode_enabled(false);
- Load(expr);
- set_safe_int32_mode_enabled(true);
-}
-
-
-void CodeGenerator::ConvertInt32ResultToSmi(Result* value) {
- ASSERT(value->is_untagged_int32());
- if (value->is_register()) {
- __ add(value->reg(), Operand(value->reg()));
- } else {
- ASSERT(value->is_constant());
- ASSERT(value->handle()->IsSmi());
- }
- value->set_untagged_int32(false);
- value->set_type_info(TypeInfo::Smi());
-}
-
-
-void CodeGenerator::ConvertInt32ResultToNumber(Result* value) {
- ASSERT(value->is_untagged_int32());
- if (value->is_register()) {
- Register val = value->reg();
- JumpTarget done;
- __ add(val, Operand(val));
- done.Branch(no_overflow, value);
- __ sar(val, 1);
- // If there was an overflow, bits 30 and 31 of the original number disagree.
- __ xor_(val, 0x80000000u);
- if (masm()->isolate()->cpu_features()->IsSupported(SSE2)) {
- CpuFeatures::Scope fscope(SSE2);
- __ cvtsi2sd(xmm0, Operand(val));
- } else {
- // Move val to ST[0] in the FPU
- // Push and pop are safe with respect to the virtual frame because
- // all synced elements are below the actual stack pointer.
- __ push(val);
- __ fild_s(Operand(esp, 0));
- __ pop(val);
- }
- Result scratch = allocator_->Allocate();
- ASSERT(scratch.is_register());
- Label allocation_failed;
- __ AllocateHeapNumber(val, scratch.reg(),
- no_reg, &allocation_failed);
- VirtualFrame* clone = new VirtualFrame(frame_);
- scratch.Unuse();
- if (masm()->isolate()->cpu_features()->IsSupported(SSE2)) {
- CpuFeatures::Scope fscope(SSE2);
- __ movdbl(FieldOperand(val, HeapNumber::kValueOffset), xmm0);
- } else {
- __ fstp_d(FieldOperand(val, HeapNumber::kValueOffset));
- }
- done.Jump(value);
-
- // Establish the virtual frame, cloned from where AllocateHeapNumber
- // jumped to allocation_failed.
- RegisterFile empty_regs;
- SetFrame(clone, &empty_regs);
- __ bind(&allocation_failed);
- if (!masm()->isolate()->cpu_features()->IsSupported(SSE2)) {
- // Pop the value from the floating point stack.
- __ fstp(0);
- }
- unsafe_bailout_->Jump();
-
- done.Bind(value);
- } else {
- ASSERT(value->is_constant());
- }
- value->set_untagged_int32(false);
- value->set_type_info(TypeInfo::Integer32());
-}
-
-
-void CodeGenerator::Load(Expression* expr) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- ASSERT(!in_spilled_code());
-
- // If the expression should be a side-effect-free 32-bit int computation,
- // compile that SafeInt32 path, and a bailout path.
- if (!in_safe_int32_mode() &&
- safe_int32_mode_enabled() &&
- expr->side_effect_free() &&
- expr->num_bit_ops() > 2 &&
- masm()->isolate()->cpu_features()->IsSupported(SSE2)) {
- BreakTarget unsafe_bailout;
- JumpTarget done;
- unsafe_bailout.set_expected_height(frame_->height());
- LoadInSafeInt32Mode(expr, &unsafe_bailout);
- done.Jump();
-
- if (unsafe_bailout.is_linked()) {
- unsafe_bailout.Bind();
- LoadWithSafeInt32ModeDisabled(expr);
- }
- done.Bind();
- } else {
- JumpTarget true_target;
- JumpTarget false_target;
- ControlDestination dest(&true_target, &false_target, true);
- LoadCondition(expr, &dest, false);
-
- if (dest.false_was_fall_through()) {
- // The false target was just bound.
- JumpTarget loaded;
- frame_->Push(FACTORY->false_value());
- // There may be dangling jumps to the true target.
- if (true_target.is_linked()) {
- loaded.Jump();
- true_target.Bind();
- frame_->Push(FACTORY->true_value());
- loaded.Bind();
- }
-
- } else if (dest.is_used()) {
- // There is true, and possibly false, control flow (with true as
- // the fall through).
- JumpTarget loaded;
- frame_->Push(FACTORY->true_value());
- if (false_target.is_linked()) {
- loaded.Jump();
- false_target.Bind();
- frame_->Push(FACTORY->false_value());
- loaded.Bind();
- }
-
- } else {
- // We have a valid value on top of the frame, but we still may
- // have dangling jumps to the true and false targets from nested
- // subexpressions (eg, the left subexpressions of the
- // short-circuited boolean operators).
- ASSERT(has_valid_frame());
- if (true_target.is_linked() || false_target.is_linked()) {
- JumpTarget loaded;
- loaded.Jump(); // Don't lose the current TOS.
- if (true_target.is_linked()) {
- true_target.Bind();
- frame_->Push(FACTORY->true_value());
- if (false_target.is_linked()) {
- loaded.Jump();
- }
- }
- if (false_target.is_linked()) {
- false_target.Bind();
- frame_->Push(FACTORY->false_value());
- }
- loaded.Bind();
- }
- }
- }
- ASSERT(has_valid_frame());
- ASSERT(frame_->height() == original_height + 1);
-}
-
-
-void CodeGenerator::LoadGlobal() {
- if (in_spilled_code()) {
- frame_->EmitPush(GlobalObjectOperand());
- } else {
- Result temp = allocator_->Allocate();
- __ mov(temp.reg(), GlobalObjectOperand());
- frame_->Push(&temp);
- }
-}
-
-
-void CodeGenerator::LoadGlobalReceiver() {
- Result temp = allocator_->Allocate();
- Register reg = temp.reg();
- __ mov(reg, GlobalObjectOperand());
- __ mov(reg, FieldOperand(reg, GlobalObject::kGlobalReceiverOffset));
- frame_->Push(&temp);
-}
-
-
-void CodeGenerator::LoadTypeofExpression(Expression* expr) {
- // Special handling of identifiers as subexpressions of typeof.
- Variable* variable = expr->AsVariableProxy()->AsVariable();
- if (variable != NULL && !variable->is_this() && variable->is_global()) {
- // For a global variable we build the property reference
- // <global>.<variable> and perform a (regular non-contextual) property
- // load to make sure we do not get reference errors.
- Slot global(variable, Slot::CONTEXT, Context::GLOBAL_INDEX);
- Literal key(variable->name());
- Property property(&global, &key, RelocInfo::kNoPosition);
- Reference ref(this, &property);
- ref.GetValue();
- } else if (variable != NULL && variable->AsSlot() != NULL) {
- // For a variable that rewrites to a slot, we signal it is the immediate
- // subexpression of a typeof.
- LoadFromSlotCheckForArguments(variable->AsSlot(), INSIDE_TYPEOF);
- } else {
- // Anything else can be handled normally.
- Load(expr);
- }
-}
-
-
-ArgumentsAllocationMode CodeGenerator::ArgumentsMode() {
- if (scope()->arguments() == NULL) return NO_ARGUMENTS_ALLOCATION;
-
- // In strict mode there is no need for shadow arguments.
- ASSERT(scope()->arguments_shadow() != NULL || scope()->is_strict_mode());
-
- // We don't want to do lazy arguments allocation for functions that
- // have heap-allocated contexts, because it interfers with the
- // uninitialized const tracking in the context objects.
- return (scope()->num_heap_slots() > 0 || scope()->is_strict_mode())
- ? EAGER_ARGUMENTS_ALLOCATION
- : LAZY_ARGUMENTS_ALLOCATION;
-}
-
-
-Result CodeGenerator::StoreArgumentsObject(bool initial) {
- ArgumentsAllocationMode mode = ArgumentsMode();
- ASSERT(mode != NO_ARGUMENTS_ALLOCATION);
-
- Comment cmnt(masm_, "[ store arguments object");
- if (mode == LAZY_ARGUMENTS_ALLOCATION && initial) {
- // When using lazy arguments allocation, we store the arguments marker value
- // as a sentinel indicating that the arguments object hasn't been
- // allocated yet.
- frame_->Push(FACTORY->arguments_marker());
- } else {
- ArgumentsAccessStub stub(is_strict_mode()
- ? ArgumentsAccessStub::NEW_STRICT
- : ArgumentsAccessStub::NEW_NON_STRICT);
- frame_->PushFunction();
- frame_->PushReceiverSlotAddress();
- frame_->Push(Smi::FromInt(scope()->num_parameters()));
- Result result = frame_->CallStub(&stub, 3);
- frame_->Push(&result);
- }
-
- Variable* arguments = scope()->arguments();
- Variable* shadow = scope()->arguments_shadow();
-
- ASSERT(arguments != NULL && arguments->AsSlot() != NULL);
- ASSERT((shadow != NULL && shadow->AsSlot() != NULL) ||
- scope()->is_strict_mode());
-
- JumpTarget done;
- bool skip_arguments = false;
- if (mode == LAZY_ARGUMENTS_ALLOCATION && !initial) {
- // We have to skip storing into the arguments slot if it has
- // already been written to. This can happen if the a function
- // has a local variable named 'arguments'.
- LoadFromSlot(arguments->AsSlot(), NOT_INSIDE_TYPEOF);
- Result probe = frame_->Pop();
- if (probe.is_constant()) {
- // We have to skip updating the arguments object if it has
- // been assigned a proper value.
- skip_arguments = !probe.handle()->IsArgumentsMarker();
- } else {
- __ cmp(Operand(probe.reg()), Immediate(FACTORY->arguments_marker()));
- probe.Unuse();
- done.Branch(not_equal);
- }
- }
- if (!skip_arguments) {
- StoreToSlot(arguments->AsSlot(), NOT_CONST_INIT);
- if (mode == LAZY_ARGUMENTS_ALLOCATION) done.Bind();
- }
- if (shadow != NULL) {
- StoreToSlot(shadow->AsSlot(), NOT_CONST_INIT);
- }
- return frame_->Pop();
-}
-
-//------------------------------------------------------------------------------
-// CodeGenerator implementation of variables, lookups, and stores.
-
-Reference::Reference(CodeGenerator* cgen,
- Expression* expression,
- bool persist_after_get)
- : cgen_(cgen),
- expression_(expression),
- type_(ILLEGAL),
- persist_after_get_(persist_after_get) {
- cgen->LoadReference(this);
-}
-
-
-Reference::~Reference() {
- ASSERT(is_unloaded() || is_illegal());
-}
-
-
-void CodeGenerator::LoadReference(Reference* ref) {
- // References are loaded from both spilled and unspilled code. Set the
- // state to unspilled to allow that (and explicitly spill after
- // construction at the construction sites).
- bool was_in_spilled_code = in_spilled_code_;
- in_spilled_code_ = false;
-
- Comment cmnt(masm_, "[ LoadReference");
- Expression* e = ref->expression();
- Property* property = e->AsProperty();
- Variable* var = e->AsVariableProxy()->AsVariable();
-
- if (property != NULL) {
- // The expression is either a property or a variable proxy that rewrites
- // to a property.
- Load(property->obj());
- if (property->key()->IsPropertyName()) {
- ref->set_type(Reference::NAMED);
- } else {
- Load(property->key());
- ref->set_type(Reference::KEYED);
- }
- } else if (var != NULL) {
- // The expression is a variable proxy that does not rewrite to a
- // property. Global variables are treated as named property references.
- if (var->is_global()) {
- // If eax is free, the register allocator prefers it. Thus the code
- // generator will load the global object into eax, which is where
- // LoadIC wants it. Most uses of Reference call LoadIC directly
- // after the reference is created.
- frame_->Spill(eax);
- LoadGlobal();
- ref->set_type(Reference::NAMED);
- } else {
- ASSERT(var->AsSlot() != NULL);
- ref->set_type(Reference::SLOT);
- }
- } else {
- // Anything else is a runtime error.
- Load(e);
- frame_->CallRuntime(Runtime::kThrowReferenceError, 1);
- }
-
- in_spilled_code_ = was_in_spilled_code;
-}
-
-
-// ECMA-262, section 9.2, page 30: ToBoolean(). Pop the top of stack and
-// convert it to a boolean in the condition code register or jump to
-// 'false_target'/'true_target' as appropriate.
-void CodeGenerator::ToBoolean(ControlDestination* dest) {
- Comment cmnt(masm_, "[ ToBoolean");
-
- // The value to convert should be popped from the frame.
- Result value = frame_->Pop();
- value.ToRegister();
-
- if (value.is_integer32()) { // Also takes Smi case.
- Comment cmnt(masm_, "ONLY_INTEGER_32");
- if (FLAG_debug_code) {
- Label ok;
- __ AbortIfNotNumber(value.reg());
- __ test(value.reg(), Immediate(kSmiTagMask));
- __ j(zero, &ok);
- __ fldz();
- __ fld_d(FieldOperand(value.reg(), HeapNumber::kValueOffset));
- __ FCmp();
- __ j(not_zero, &ok);
- __ Abort("Smi was wrapped in HeapNumber in output from bitop");
- __ bind(&ok);
- }
- // In the integer32 case there are no Smis hidden in heap numbers, so we
- // need only test for Smi zero.
- __ test(value.reg(), Operand(value.reg()));
- dest->false_target()->Branch(zero);
- value.Unuse();
- dest->Split(not_zero);
- } else if (value.is_number()) {
- Comment cmnt(masm_, "ONLY_NUMBER");
- // Fast case if TypeInfo indicates only numbers.
- if (FLAG_debug_code) {
- __ AbortIfNotNumber(value.reg());
- }
- // Smi => false iff zero.
- STATIC_ASSERT(kSmiTag == 0);
- __ test(value.reg(), Operand(value.reg()));
- dest->false_target()->Branch(zero);
- __ test(value.reg(), Immediate(kSmiTagMask));
- dest->true_target()->Branch(zero);
- __ fldz();
- __ fld_d(FieldOperand(value.reg(), HeapNumber::kValueOffset));
- __ FCmp();
- value.Unuse();
- dest->Split(not_zero);
- } else {
- // Fast case checks.
- // 'false' => false.
- __ cmp(value.reg(), FACTORY->false_value());
- dest->false_target()->Branch(equal);
-
- // 'true' => true.
- __ cmp(value.reg(), FACTORY->true_value());
- dest->true_target()->Branch(equal);
-
- // 'undefined' => false.
- __ cmp(value.reg(), FACTORY->undefined_value());
- dest->false_target()->Branch(equal);
-
- // Smi => false iff zero.
- STATIC_ASSERT(kSmiTag == 0);
- __ test(value.reg(), Operand(value.reg()));
- dest->false_target()->Branch(zero);
- __ test(value.reg(), Immediate(kSmiTagMask));
- dest->true_target()->Branch(zero);
-
- // Call the stub for all other cases.
- frame_->Push(&value); // Undo the Pop() from above.
- ToBooleanStub stub;
- Result temp = frame_->CallStub(&stub, 1);
- // Convert the result to a condition code.
- __ test(temp.reg(), Operand(temp.reg()));
- temp.Unuse();
- dest->Split(not_equal);
- }
-}
-
-
-// Perform or call the specialized stub for a binary operation. Requires the
-// three registers left, right and dst to be distinct and spilled. This
-// deferred operation has up to three entry points: The main one calls the
-// runtime system. The second is for when the result is a non-Smi. The
-// third is for when at least one of the inputs is non-Smi and we have SSE2.
-class DeferredInlineBinaryOperation: public DeferredCode {
- public:
- DeferredInlineBinaryOperation(Token::Value op,
- Register dst,
- Register left,
- Register right,
- TypeInfo left_info,
- TypeInfo right_info,
- OverwriteMode mode)
- : op_(op), dst_(dst), left_(left), right_(right),
- left_info_(left_info), right_info_(right_info), mode_(mode) {
- set_comment("[ DeferredInlineBinaryOperation");
- ASSERT(!left.is(right));
- }
-
- virtual void Generate();
-
- // This stub makes explicit calls to SaveRegisters(), RestoreRegisters() and
- // Exit().
- virtual bool AutoSaveAndRestore() { return false; }
-
- void JumpToAnswerOutOfRange(Condition cond);
- void JumpToConstantRhs(Condition cond, Smi* smi_value);
- Label* NonSmiInputLabel();
-
- private:
- void GenerateAnswerOutOfRange();
- void GenerateNonSmiInput();
-
- Token::Value op_;
- Register dst_;
- Register left_;
- Register right_;
- TypeInfo left_info_;
- TypeInfo right_info_;
- OverwriteMode mode_;
- Label answer_out_of_range_;
- Label non_smi_input_;
- Label constant_rhs_;
- Smi* smi_value_;
-};
-
-
-Label* DeferredInlineBinaryOperation::NonSmiInputLabel() {
- if (Token::IsBitOp(op_) &&
- masm()->isolate()->cpu_features()->IsSupported(SSE2)) {
- return &non_smi_input_;
- } else {
- return entry_label();
- }
-}
-
-
-void DeferredInlineBinaryOperation::JumpToAnswerOutOfRange(Condition cond) {
- __ j(cond, &answer_out_of_range_);
-}
-
-
-void DeferredInlineBinaryOperation::JumpToConstantRhs(Condition cond,
- Smi* smi_value) {
- smi_value_ = smi_value;
- __ j(cond, &constant_rhs_);
-}
-
-
-void DeferredInlineBinaryOperation::Generate() {
- // Registers are not saved implicitly for this stub, so we should not
- // tread on the registers that were not passed to us.
- if (masm()->isolate()->cpu_features()->IsSupported(SSE2) &&
- ((op_ == Token::ADD) ||
- (op_ == Token::SUB) ||
- (op_ == Token::MUL) ||
- (op_ == Token::DIV))) {
- CpuFeatures::Scope use_sse2(SSE2);
- Label call_runtime, after_alloc_failure;
- Label left_smi, right_smi, load_right, do_op;
- if (!left_info_.IsSmi()) {
- __ test(left_, Immediate(kSmiTagMask));
- __ j(zero, &left_smi);
- if (!left_info_.IsNumber()) {
- __ cmp(FieldOperand(left_, HeapObject::kMapOffset),
- FACTORY->heap_number_map());
- __ j(not_equal, &call_runtime);
- }
- __ movdbl(xmm0, FieldOperand(left_, HeapNumber::kValueOffset));
- if (mode_ == OVERWRITE_LEFT) {
- __ mov(dst_, left_);
- }
- __ jmp(&load_right);
-
- __ bind(&left_smi);
- } else {
- if (FLAG_debug_code) __ AbortIfNotSmi(left_);
- }
- __ SmiUntag(left_);
- __ cvtsi2sd(xmm0, Operand(left_));
- __ SmiTag(left_);
- if (mode_ == OVERWRITE_LEFT) {
- Label alloc_failure;
- __ push(left_);
- __ AllocateHeapNumber(dst_, left_, no_reg, &after_alloc_failure);
- __ pop(left_);
- }
-
- __ bind(&load_right);
- if (!right_info_.IsSmi()) {
- __ test(right_, Immediate(kSmiTagMask));
- __ j(zero, &right_smi);
- if (!right_info_.IsNumber()) {
- __ cmp(FieldOperand(right_, HeapObject::kMapOffset),
- FACTORY->heap_number_map());
- __ j(not_equal, &call_runtime);
- }
- __ movdbl(xmm1, FieldOperand(right_, HeapNumber::kValueOffset));
- if (mode_ == OVERWRITE_RIGHT) {
- __ mov(dst_, right_);
- } else if (mode_ == NO_OVERWRITE) {
- Label alloc_failure;
- __ push(left_);
- __ AllocateHeapNumber(dst_, left_, no_reg, &after_alloc_failure);
- __ pop(left_);
- }
- __ jmp(&do_op);
-
- __ bind(&right_smi);
- } else {
- if (FLAG_debug_code) __ AbortIfNotSmi(right_);
- }
- __ SmiUntag(right_);
- __ cvtsi2sd(xmm1, Operand(right_));
- __ SmiTag(right_);
- if (mode_ == OVERWRITE_RIGHT || mode_ == NO_OVERWRITE) {
- __ push(left_);
- __ AllocateHeapNumber(dst_, left_, no_reg, &after_alloc_failure);
- __ pop(left_);
- }
-
- __ bind(&do_op);
- switch (op_) {
- case Token::ADD: __ addsd(xmm0, xmm1); break;
- case Token::SUB: __ subsd(xmm0, xmm1); break;
- case Token::MUL: __ mulsd(xmm0, xmm1); break;
- case Token::DIV: __ divsd(xmm0, xmm1); break;
- default: UNREACHABLE();
- }
- __ movdbl(FieldOperand(dst_, HeapNumber::kValueOffset), xmm0);
- Exit();
-
-
- __ bind(&after_alloc_failure);
- __ pop(left_);
- __ bind(&call_runtime);
- }
- // Register spilling is not done implicitly for this stub.
- // We can't postpone it any more now though.
- SaveRegisters();
-
- GenericBinaryOpStub stub(op_,
- mode_,
- NO_SMI_CODE_IN_STUB,
- TypeInfo::Combine(left_info_, right_info_));
- stub.GenerateCall(masm_, left_, right_);
- if (!dst_.is(eax)) __ mov(dst_, eax);
- RestoreRegisters();
- Exit();
-
- if (non_smi_input_.is_linked() || constant_rhs_.is_linked()) {
- GenerateNonSmiInput();
- }
- if (answer_out_of_range_.is_linked()) {
- GenerateAnswerOutOfRange();
- }
-}
-
-
-void DeferredInlineBinaryOperation::GenerateNonSmiInput() {
- // We know at least one of the inputs was not a Smi.
- // This is a third entry point into the deferred code.
- // We may not overwrite left_ because we want to be able
- // to call the handling code for non-smi answer and it
- // might want to overwrite the heap number in left_.
- ASSERT(!right_.is(dst_));
- ASSERT(!left_.is(dst_));
- ASSERT(!left_.is(right_));
- // This entry point is used for bit ops where the right hand side
- // is a constant Smi and the left hand side is a heap object. It
- // is also used for bit ops where both sides are unknown, but where
- // at least one of them is a heap object.
- bool rhs_is_constant = constant_rhs_.is_linked();
- // We can't generate code for both cases.
- ASSERT(!non_smi_input_.is_linked() || !constant_rhs_.is_linked());
-
- if (FLAG_debug_code) {
- __ int3(); // We don't fall through into this code.
- }
-
- __ bind(&non_smi_input_);
-
- if (rhs_is_constant) {
- __ bind(&constant_rhs_);
- // In this case the input is a heap object and it is in the dst_ register.
- // The left_ and right_ registers have not been initialized yet.
- __ mov(right_, Immediate(smi_value_));
- __ mov(left_, Operand(dst_));
- if (!masm()->isolate()->cpu_features()->IsSupported(SSE2)) {
- __ jmp(entry_label());
- return;
- } else {
- CpuFeatures::Scope use_sse2(SSE2);
- __ JumpIfNotNumber(dst_, left_info_, entry_label());
- __ ConvertToInt32(dst_, left_, dst_, left_info_, entry_label());
- __ SmiUntag(right_);
- }
- } else {
- // We know we have SSE2 here because otherwise the label is not linked (see
- // NonSmiInputLabel).
- CpuFeatures::Scope use_sse2(SSE2);
- // Handle the non-constant right hand side situation:
- if (left_info_.IsSmi()) {
- // Right is a heap object.
- __ JumpIfNotNumber(right_, right_info_, entry_label());
- __ ConvertToInt32(right_, right_, dst_, right_info_, entry_label());
- __ mov(dst_, Operand(left_));
- __ SmiUntag(dst_);
- } else if (right_info_.IsSmi()) {
- // Left is a heap object.
- __ JumpIfNotNumber(left_, left_info_, entry_label());
- __ ConvertToInt32(dst_, left_, dst_, left_info_, entry_label());
- __ SmiUntag(right_);
- } else {
- // Here we don't know if it's one or both that is a heap object.
- Label only_right_is_heap_object, got_both;
- __ mov(dst_, Operand(left_));
- __ SmiUntag(dst_, &only_right_is_heap_object);
- // Left was a heap object.
- __ JumpIfNotNumber(left_, left_info_, entry_label());
- __ ConvertToInt32(dst_, left_, dst_, left_info_, entry_label());
- __ SmiUntag(right_, &got_both);
- // Both were heap objects.
- __ rcl(right_, 1); // Put tag back.
- __ JumpIfNotNumber(right_, right_info_, entry_label());
- __ ConvertToInt32(right_, right_, no_reg, right_info_, entry_label());
- __ jmp(&got_both);
- __ bind(&only_right_is_heap_object);
- __ JumpIfNotNumber(right_, right_info_, entry_label());
- __ ConvertToInt32(right_, right_, no_reg, right_info_, entry_label());
- __ bind(&got_both);
- }
- }
- ASSERT(op_ == Token::BIT_AND ||
- op_ == Token::BIT_OR ||
- op_ == Token::BIT_XOR ||
- right_.is(ecx));
- switch (op_) {
- case Token::BIT_AND: __ and_(dst_, Operand(right_)); break;
- case Token::BIT_OR: __ or_(dst_, Operand(right_)); break;
- case Token::BIT_XOR: __ xor_(dst_, Operand(right_)); break;
- case Token::SHR: __ shr_cl(dst_); break;
- case Token::SAR: __ sar_cl(dst_); break;
- case Token::SHL: __ shl_cl(dst_); break;
- default: UNREACHABLE();
- }
- if (op_ == Token::SHR) {
- // Check that the *unsigned* result fits in a smi. Neither of
- // the two high-order bits can be set:
- // * 0x80000000: high bit would be lost when smi tagging.
- // * 0x40000000: this number would convert to negative when smi
- // tagging.
- __ test(dst_, Immediate(0xc0000000));
- __ j(not_zero, &answer_out_of_range_);
- } else {
- // Check that the *signed* result fits in a smi.
- __ cmp(dst_, 0xc0000000);
- __ j(negative, &answer_out_of_range_);
- }
- __ SmiTag(dst_);
- Exit();
-}
-
-
-void DeferredInlineBinaryOperation::GenerateAnswerOutOfRange() {
- Label after_alloc_failure2;
- Label allocation_ok;
- __ bind(&after_alloc_failure2);
- // We have to allocate a number, causing a GC, while keeping hold of
- // the answer in dst_. The answer is not a Smi. We can't just call the
- // runtime shift function here because we already threw away the inputs.
- __ xor_(left_, Operand(left_));
- __ shl(dst_, 1); // Put top bit in carry flag and Smi tag the low bits.
- __ rcr(left_, 1); // Rotate with carry.
- __ push(dst_); // Smi tagged low 31 bits.
- __ push(left_); // 0 or 0x80000000, which is Smi tagged in both cases.
- __ CallRuntime(Runtime::kNumberAlloc, 0);
- if (!left_.is(eax)) {
- __ mov(left_, eax);
- }
- __ pop(right_); // High bit.
- __ pop(dst_); // Low 31 bits.
- __ shr(dst_, 1); // Put 0 in top bit.
- __ or_(dst_, Operand(right_));
- __ jmp(&allocation_ok);
-
- // This is the second entry point to the deferred code. It is used only by
- // the bit operations.
- // The dst_ register has the answer. It is not Smi tagged. If mode_ is
- // OVERWRITE_LEFT then left_ must contain either an overwritable heap number
- // or a Smi.
- // Put a heap number pointer in left_.
- __ bind(&answer_out_of_range_);
- SaveRegisters();
- if (mode_ == OVERWRITE_LEFT) {
- __ test(left_, Immediate(kSmiTagMask));
- __ j(not_zero, &allocation_ok);
- }
- // This trashes right_.
- __ AllocateHeapNumber(left_, right_, no_reg, &after_alloc_failure2);
- __ bind(&allocation_ok);
- if (masm()->isolate()->cpu_features()->IsSupported(SSE2) &&
- op_ != Token::SHR) {
- CpuFeatures::Scope use_sse2(SSE2);
- ASSERT(Token::IsBitOp(op_));
- // Signed conversion.
- __ cvtsi2sd(xmm0, Operand(dst_));
- __ movdbl(FieldOperand(left_, HeapNumber::kValueOffset), xmm0);
- } else {
- if (op_ == Token::SHR) {
- __ push(Immediate(0)); // High word of unsigned value.
- __ push(dst_);
- __ fild_d(Operand(esp, 0));
- __ Drop(2);
- } else {
- ASSERT(Token::IsBitOp(op_));
- __ push(dst_);
- __ fild_s(Operand(esp, 0)); // Signed conversion.
- __ pop(dst_);
- }
- __ fstp_d(FieldOperand(left_, HeapNumber::kValueOffset));
- }
- __ mov(dst_, left_);
- RestoreRegisters();
- Exit();
-}
-
-
-static TypeInfo CalculateTypeInfo(TypeInfo operands_type,
- Token::Value op,
- const Result& right,
- const Result& left) {
- // Set TypeInfo of result according to the operation performed.
- // Rely on the fact that smis have a 31 bit payload on ia32.
- STATIC_ASSERT(kSmiValueSize == 31);
- switch (op) {
- case Token::COMMA:
- return right.type_info();
- case Token::OR:
- case Token::AND:
- // Result type can be either of the two input types.
- return operands_type;
- case Token::BIT_AND: {
- // Anding with positive Smis will give you a Smi.
- if (right.is_constant() && right.handle()->IsSmi() &&
- Smi::cast(*right.handle())->value() >= 0) {
- return TypeInfo::Smi();
- } else if (left.is_constant() && left.handle()->IsSmi() &&
- Smi::cast(*left.handle())->value() >= 0) {
- return TypeInfo::Smi();
- }
- return (operands_type.IsSmi())
- ? TypeInfo::Smi()
- : TypeInfo::Integer32();
- }
- case Token::BIT_OR: {
- // Oring with negative Smis will give you a Smi.
- if (right.is_constant() && right.handle()->IsSmi() &&
- Smi::cast(*right.handle())->value() < 0) {
- return TypeInfo::Smi();
- } else if (left.is_constant() && left.handle()->IsSmi() &&
- Smi::cast(*left.handle())->value() < 0) {
- return TypeInfo::Smi();
- }
- return (operands_type.IsSmi())
- ? TypeInfo::Smi()
- : TypeInfo::Integer32();
- }
- case Token::BIT_XOR:
- // Result is always a 32 bit integer. Smi property of inputs is preserved.
- return (operands_type.IsSmi())
- ? TypeInfo::Smi()
- : TypeInfo::Integer32();
- case Token::SAR:
- if (left.is_smi()) return TypeInfo::Smi();
- // Result is a smi if we shift by a constant >= 1, otherwise an integer32.
- // Shift amount is masked with 0x1F (ECMA standard 11.7.2).
- return (right.is_constant() && right.handle()->IsSmi()
- && (Smi::cast(*right.handle())->value() & 0x1F) >= 1)
- ? TypeInfo::Smi()
- : TypeInfo::Integer32();
- case Token::SHR:
- // Result is a smi if we shift by a constant >= 2, an integer32 if
- // we shift by 1, and an unsigned 32-bit integer if we shift by 0.
- if (right.is_constant() && right.handle()->IsSmi()) {
- int shift_amount = Smi::cast(*right.handle())->value() & 0x1F;
- if (shift_amount > 1) {
- return TypeInfo::Smi();
- } else if (shift_amount > 0) {
- return TypeInfo::Integer32();
- }
- }
- return TypeInfo::Number();
- case Token::ADD:
- if (operands_type.IsSmi()) {
- // The Integer32 range is big enough to take the sum of any two Smis.
- return TypeInfo::Integer32();
- } else if (operands_type.IsNumber()) {
- return TypeInfo::Number();
- } else if (left.type_info().IsString() || right.type_info().IsString()) {
- return TypeInfo::String();
- } else {
- return TypeInfo::Unknown();
- }
- case Token::SHL:
- return TypeInfo::Integer32();
- case Token::SUB:
- // The Integer32 range is big enough to take the difference of any two
- // Smis.
- return (operands_type.IsSmi()) ?
- TypeInfo::Integer32() :
- TypeInfo::Number();
- case Token::MUL:
- case Token::DIV:
- case Token::MOD:
- // Result is always a number.
- return TypeInfo::Number();
- default:
- UNREACHABLE();
- }
- UNREACHABLE();
- return TypeInfo::Unknown();
-}
-
-
-void CodeGenerator::GenericBinaryOperation(BinaryOperation* expr,
- OverwriteMode overwrite_mode) {
- Comment cmnt(masm_, "[ BinaryOperation");
- Token::Value op = expr->op();
- Comment cmnt_token(masm_, Token::String(op));
-
- if (op == Token::COMMA) {
- // Simply discard left value.
- frame_->Nip(1);
- return;
- }
-
- Result right = frame_->Pop();
- Result left = frame_->Pop();
-
- if (op == Token::ADD) {
- const bool left_is_string = left.type_info().IsString();
- const bool right_is_string = right.type_info().IsString();
- // Make sure constant strings have string type info.
- ASSERT(!(left.is_constant() && left.handle()->IsString()) ||
- left_is_string);
- ASSERT(!(right.is_constant() && right.handle()->IsString()) ||
- right_is_string);
- if (left_is_string || right_is_string) {
- frame_->Push(&left);
- frame_->Push(&right);
- Result answer;
- if (left_is_string) {
- if (right_is_string) {
- StringAddStub stub(NO_STRING_CHECK_IN_STUB);
- answer = frame_->CallStub(&stub, 2);
- } else {
- StringAddStub stub(NO_STRING_CHECK_LEFT_IN_STUB);
- answer = frame_->CallStub(&stub, 2);
- }
- } else if (right_is_string) {
- StringAddStub stub(NO_STRING_CHECK_RIGHT_IN_STUB);
- answer = frame_->CallStub(&stub, 2);
- }
- answer.set_type_info(TypeInfo::String());
- frame_->Push(&answer);
- return;
- }
- // Neither operand is known to be a string.
- }
-
- bool left_is_smi_constant = left.is_constant() && left.handle()->IsSmi();
- bool left_is_non_smi_constant = left.is_constant() && !left.handle()->IsSmi();
- bool right_is_smi_constant = right.is_constant() && right.handle()->IsSmi();
- bool right_is_non_smi_constant =
- right.is_constant() && !right.handle()->IsSmi();
-
- if (left_is_smi_constant && right_is_smi_constant) {
- // Compute the constant result at compile time, and leave it on the frame.
- int left_int = Smi::cast(*left.handle())->value();
- int right_int = Smi::cast(*right.handle())->value();
- if (FoldConstantSmis(op, left_int, right_int)) return;
- }
-
- // Get number type of left and right sub-expressions.
- TypeInfo operands_type =
- TypeInfo::Combine(left.type_info(), right.type_info());
-
- TypeInfo result_type = CalculateTypeInfo(operands_type, op, right, left);
-
- Result answer;
- if (left_is_non_smi_constant || right_is_non_smi_constant) {
- // Go straight to the slow case, with no smi code.
- GenericBinaryOpStub stub(op,
- overwrite_mode,
- NO_SMI_CODE_IN_STUB,
- operands_type);
- answer = GenerateGenericBinaryOpStubCall(&stub, &left, &right);
- } else if (right_is_smi_constant) {
- answer = ConstantSmiBinaryOperation(expr, &left, right.handle(),
- false, overwrite_mode);
- } else if (left_is_smi_constant) {
- answer = ConstantSmiBinaryOperation(expr, &right, left.handle(),
- true, overwrite_mode);
- } else {
- // Set the flags based on the operation, type and loop nesting level.
- // Bit operations always assume they likely operate on Smis. Still only
- // generate the inline Smi check code if this operation is part of a loop.
- // For all other operations only inline the Smi check code for likely smis
- // if the operation is part of a loop.
- if (loop_nesting() > 0 &&
- (Token::IsBitOp(op) ||
- operands_type.IsInteger32() ||
- expr->type()->IsLikelySmi())) {
- answer = LikelySmiBinaryOperation(expr, &left, &right, overwrite_mode);
- } else {
- GenericBinaryOpStub stub(op,
- overwrite_mode,
- NO_GENERIC_BINARY_FLAGS,
- operands_type);
- answer = GenerateGenericBinaryOpStubCall(&stub, &left, &right);
- }
- }
-
- answer.set_type_info(result_type);
- frame_->Push(&answer);
-}
-
-
-Result CodeGenerator::GenerateGenericBinaryOpStubCall(GenericBinaryOpStub* stub,
- Result* left,
- Result* right) {
- if (stub->ArgsInRegistersSupported()) {
- stub->SetArgsInRegisters();
- return frame_->CallStub(stub, left, right);
- } else {
- frame_->Push(left);
- frame_->Push(right);
- return frame_->CallStub(stub, 2);
- }
-}
-
-
-bool CodeGenerator::FoldConstantSmis(Token::Value op, int left, int right) {
- Object* answer_object = HEAP->undefined_value();
- switch (op) {
- case Token::ADD:
- if (Smi::IsValid(left + right)) {
- answer_object = Smi::FromInt(left + right);
- }
- break;
- case Token::SUB:
- if (Smi::IsValid(left - right)) {
- answer_object = Smi::FromInt(left - right);
- }
- break;
- case Token::MUL: {
- double answer = static_cast<double>(left) * right;
- if (answer >= Smi::kMinValue && answer <= Smi::kMaxValue) {
- // If the product is zero and the non-zero factor is negative,
- // the spec requires us to return floating point negative zero.
- if (answer != 0 || (left >= 0 && right >= 0)) {
- answer_object = Smi::FromInt(static_cast<int>(answer));
- }
- }
- }
- break;
- case Token::DIV:
- case Token::MOD:
- break;
- case Token::BIT_OR:
- answer_object = Smi::FromInt(left | right);
- break;
- case Token::BIT_AND:
- answer_object = Smi::FromInt(left & right);
- break;
- case Token::BIT_XOR:
- answer_object = Smi::FromInt(left ^ right);
- break;
-
- case Token::SHL: {
- int shift_amount = right & 0x1F;
- if (Smi::IsValid(left << shift_amount)) {
- answer_object = Smi::FromInt(left << shift_amount);
- }
- break;
- }
- case Token::SHR: {
- int shift_amount = right & 0x1F;
- unsigned int unsigned_left = left;
- unsigned_left >>= shift_amount;
- if (unsigned_left <= static_cast<unsigned int>(Smi::kMaxValue)) {
- answer_object = Smi::FromInt(unsigned_left);
- }
- break;
- }
- case Token::SAR: {
- int shift_amount = right & 0x1F;
- unsigned int unsigned_left = left;
- if (left < 0) {
- // Perform arithmetic shift of a negative number by
- // complementing number, logical shifting, complementing again.
- unsigned_left = ~unsigned_left;
- unsigned_left >>= shift_amount;
- unsigned_left = ~unsigned_left;
- } else {
- unsigned_left >>= shift_amount;
- }
- ASSERT(Smi::IsValid(static_cast<int32_t>(unsigned_left)));
- answer_object = Smi::FromInt(static_cast<int32_t>(unsigned_left));
- break;
- }
- default:
- UNREACHABLE();
- break;
- }
- if (answer_object->IsUndefined()) {
- return false;
- }
- frame_->Push(Handle<Object>(answer_object));
- return true;
-}
-
-
-void CodeGenerator::JumpIfBothSmiUsingTypeInfo(Result* left,
- Result* right,
- JumpTarget* both_smi) {
- TypeInfo left_info = left->type_info();
- TypeInfo right_info = right->type_info();
- if (left_info.IsDouble() || left_info.IsString() ||
- right_info.IsDouble() || right_info.IsString()) {
- // We know that left and right are not both smi. Don't do any tests.
- return;
- }
-
- if (left->reg().is(right->reg())) {
- if (!left_info.IsSmi()) {
- __ test(left->reg(), Immediate(kSmiTagMask));
- both_smi->Branch(zero);
- } else {
- if (FLAG_debug_code) __ AbortIfNotSmi(left->reg());
- left->Unuse();
- right->Unuse();
- both_smi->Jump();
- }
- } else if (!left_info.IsSmi()) {
- if (!right_info.IsSmi()) {
- Result temp = allocator_->Allocate();
- ASSERT(temp.is_valid());
- __ mov(temp.reg(), left->reg());
- __ or_(temp.reg(), Operand(right->reg()));
- __ test(temp.reg(), Immediate(kSmiTagMask));
- temp.Unuse();
- both_smi->Branch(zero);
- } else {
- __ test(left->reg(), Immediate(kSmiTagMask));
- both_smi->Branch(zero);
- }
- } else {
- if (FLAG_debug_code) __ AbortIfNotSmi(left->reg());
- if (!right_info.IsSmi()) {
- __ test(right->reg(), Immediate(kSmiTagMask));
- both_smi->Branch(zero);
- } else {
- if (FLAG_debug_code) __ AbortIfNotSmi(right->reg());
- left->Unuse();
- right->Unuse();
- both_smi->Jump();
- }
- }
-}
-
-
-void CodeGenerator::JumpIfNotBothSmiUsingTypeInfo(Register left,
- Register right,
- Register scratch,
- TypeInfo left_info,
- TypeInfo right_info,
- DeferredCode* deferred) {
- JumpIfNotBothSmiUsingTypeInfo(left,
- right,
- scratch,
- left_info,
- right_info,
- deferred->entry_label());
-}
-
-
-void CodeGenerator::JumpIfNotBothSmiUsingTypeInfo(Register left,
- Register right,
- Register scratch,
- TypeInfo left_info,
- TypeInfo right_info,
- Label* on_not_smi) {
- if (left.is(right)) {
- if (!left_info.IsSmi()) {
- __ test(left, Immediate(kSmiTagMask));
- __ j(not_zero, on_not_smi);
- } else {
- if (FLAG_debug_code) __ AbortIfNotSmi(left);
- }
- } else if (!left_info.IsSmi()) {
- if (!right_info.IsSmi()) {
- __ mov(scratch, left);
- __ or_(scratch, Operand(right));
- __ test(scratch, Immediate(kSmiTagMask));
- __ j(not_zero, on_not_smi);
- } else {
- __ test(left, Immediate(kSmiTagMask));
- __ j(not_zero, on_not_smi);
- if (FLAG_debug_code) __ AbortIfNotSmi(right);
- }
- } else {
- if (FLAG_debug_code) __ AbortIfNotSmi(left);
- if (!right_info.IsSmi()) {
- __ test(right, Immediate(kSmiTagMask));
- __ j(not_zero, on_not_smi);
- } else {
- if (FLAG_debug_code) __ AbortIfNotSmi(right);
- }
- }
-}
-
-
-// Implements a binary operation using a deferred code object and some
-// inline code to operate on smis quickly.
-Result CodeGenerator::LikelySmiBinaryOperation(BinaryOperation* expr,
- Result* left,
- Result* right,
- OverwriteMode overwrite_mode) {
- // Copy the type info because left and right may be overwritten.
- TypeInfo left_type_info = left->type_info();
- TypeInfo right_type_info = right->type_info();
- Token::Value op = expr->op();
- Result answer;
- // Special handling of div and mod because they use fixed registers.
- if (op == Token::DIV || op == Token::MOD) {
- // We need eax as the quotient register, edx as the remainder
- // register, neither left nor right in eax or edx, and left copied
- // to eax.
- Result quotient;
- Result remainder;
- bool left_is_in_eax = false;
- // Step 1: get eax for quotient.
- if ((left->is_register() && left->reg().is(eax)) ||
- (right->is_register() && right->reg().is(eax))) {
- // One or both is in eax. Use a fresh non-edx register for
- // them.
- Result fresh = allocator_->Allocate();
- ASSERT(fresh.is_valid());
- if (fresh.reg().is(edx)) {
- remainder = fresh;
- fresh = allocator_->Allocate();
- ASSERT(fresh.is_valid());
- }
- if (left->is_register() && left->reg().is(eax)) {
- quotient = *left;
- *left = fresh;
- left_is_in_eax = true;
- }
- if (right->is_register() && right->reg().is(eax)) {
- quotient = *right;
- *right = fresh;
- }
- __ mov(fresh.reg(), eax);
- } else {
- // Neither left nor right is in eax.
- quotient = allocator_->Allocate(eax);
- }
- ASSERT(quotient.is_register() && quotient.reg().is(eax));
- ASSERT(!(left->is_register() && left->reg().is(eax)));
- ASSERT(!(right->is_register() && right->reg().is(eax)));
-
- // Step 2: get edx for remainder if necessary.
- if (!remainder.is_valid()) {
- if ((left->is_register() && left->reg().is(edx)) ||
- (right->is_register() && right->reg().is(edx))) {
- Result fresh = allocator_->Allocate();
- ASSERT(fresh.is_valid());
- if (left->is_register() && left->reg().is(edx)) {
- remainder = *left;
- *left = fresh;
- }
- if (right->is_register() && right->reg().is(edx)) {
- remainder = *right;
- *right = fresh;
- }
- __ mov(fresh.reg(), edx);
- } else {
- // Neither left nor right is in edx.
- remainder = allocator_->Allocate(edx);
- }
- }
- ASSERT(remainder.is_register() && remainder.reg().is(edx));
- ASSERT(!(left->is_register() && left->reg().is(edx)));
- ASSERT(!(right->is_register() && right->reg().is(edx)));
-
- left->ToRegister();
- right->ToRegister();
- frame_->Spill(eax);
- frame_->Spill(edx);
- // DeferredInlineBinaryOperation requires all the registers that it is
- // told about to be spilled and distinct.
- Result distinct_right = frame_->MakeDistinctAndSpilled(left, right);
-
- // Check that left and right are smi tagged.
- DeferredInlineBinaryOperation* deferred =
- new DeferredInlineBinaryOperation(op,
- (op == Token::DIV) ? eax : edx,
- left->reg(),
- distinct_right.reg(),
- left_type_info,
- right_type_info,
- overwrite_mode);
- JumpIfNotBothSmiUsingTypeInfo(left->reg(), right->reg(), edx,
- left_type_info, right_type_info, deferred);
- if (!left_is_in_eax) {
- __ mov(eax, left->reg());
- }
- // Sign extend eax into edx:eax.
- __ cdq();
- // Check for 0 divisor.
- __ test(right->reg(), Operand(right->reg()));
- deferred->Branch(zero);
- // Divide edx:eax by the right operand.
- __ idiv(right->reg());
-
- // Complete the operation.
- if (op == Token::DIV) {
- // Check for negative zero result. If result is zero, and divisor
- // is negative, return a floating point negative zero. The
- // virtual frame is unchanged in this block, so local control flow
- // can use a Label rather than a JumpTarget. If the context of this
- // expression will treat -0 like 0, do not do this test.
- if (!expr->no_negative_zero()) {
- Label non_zero_result;
- __ test(left->reg(), Operand(left->reg()));
- __ j(not_zero, &non_zero_result);
- __ test(right->reg(), Operand(right->reg()));
- deferred->Branch(negative);
- __ bind(&non_zero_result);
- }
- // Check for the corner case of dividing the most negative smi by
- // -1. We cannot use the overflow flag, since it is not set by
- // idiv instruction.
- STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
- __ cmp(eax, 0x40000000);
- deferred->Branch(equal);
- // Check that the remainder is zero.
- __ test(edx, Operand(edx));
- deferred->Branch(not_zero);
- // Tag the result and store it in the quotient register.
- __ SmiTag(eax);
- deferred->BindExit();
- left->Unuse();
- right->Unuse();
- answer = quotient;
- } else {
- ASSERT(op == Token::MOD);
- // Check for a negative zero result. If the result is zero, and
- // the dividend is negative, return a floating point negative
- // zero. The frame is unchanged in this block, so local control
- // flow can use a Label rather than a JumpTarget.
- if (!expr->no_negative_zero()) {
- Label non_zero_result;
- __ test(edx, Operand(edx));
- __ j(not_zero, &non_zero_result, taken);
- __ test(left->reg(), Operand(left->reg()));
- deferred->Branch(negative);
- __ bind(&non_zero_result);
- }
- deferred->BindExit();
- left->Unuse();
- right->Unuse();
- answer = remainder;
- }
- ASSERT(answer.is_valid());
- return answer;
- }
-
- // Special handling of shift operations because they use fixed
- // registers.
- if (op == Token::SHL || op == Token::SHR || op == Token::SAR) {
- // Move left out of ecx if necessary.
- if (left->is_register() && left->reg().is(ecx)) {
- *left = allocator_->Allocate();
- ASSERT(left->is_valid());
- __ mov(left->reg(), ecx);
- }
- right->ToRegister(ecx);
- left->ToRegister();
- ASSERT(left->is_register() && !left->reg().is(ecx));
- ASSERT(right->is_register() && right->reg().is(ecx));
- if (left_type_info.IsSmi()) {
- if (FLAG_debug_code) __ AbortIfNotSmi(left->reg());
- }
- if (right_type_info.IsSmi()) {
- if (FLAG_debug_code) __ AbortIfNotSmi(right->reg());
- }
-
- // We will modify right, it must be spilled.
- frame_->Spill(ecx);
- // DeferredInlineBinaryOperation requires all the registers that it is told
- // about to be spilled and distinct. We know that right is ecx and left is
- // not ecx.
- frame_->Spill(left->reg());
-
- // Use a fresh answer register to avoid spilling the left operand.
- answer = allocator_->Allocate();
- ASSERT(answer.is_valid());
-
- DeferredInlineBinaryOperation* deferred =
- new DeferredInlineBinaryOperation(op,
- answer.reg(),
- left->reg(),
- ecx,
- left_type_info,
- right_type_info,
- overwrite_mode);
- JumpIfNotBothSmiUsingTypeInfo(left->reg(), right->reg(), answer.reg(),
- left_type_info, right_type_info,
- deferred->NonSmiInputLabel());
-
- // Untag both operands.
- __ mov(answer.reg(), left->reg());
- __ SmiUntag(answer.reg());
- __ SmiUntag(right->reg()); // Right is ecx.
-
- // Perform the operation.
- ASSERT(right->reg().is(ecx));
- switch (op) {
- case Token::SAR: {
- __ sar_cl(answer.reg());
- if (!left_type_info.IsSmi()) {
- // Check that the *signed* result fits in a smi.
- __ cmp(answer.reg(), 0xc0000000);
- deferred->JumpToAnswerOutOfRange(negative);
- }
- break;
- }
- case Token::SHR: {
- __ shr_cl(answer.reg());
- // Check that the *unsigned* result fits in a smi. Neither of
- // the two high-order bits can be set:
- // * 0x80000000: high bit would be lost when smi tagging.
- // * 0x40000000: this number would convert to negative when smi
- // tagging.
- // These two cases can only happen with shifts by 0 or 1 when
- // handed a valid smi. If the answer cannot be represented by a
- // smi, restore the left and right arguments, and jump to slow
- // case. The low bit of the left argument may be lost, but only
- // in a case where it is dropped anyway.
- __ test(answer.reg(), Immediate(0xc0000000));
- deferred->JumpToAnswerOutOfRange(not_zero);
- break;
- }
- case Token::SHL: {
- __ shl_cl(answer.reg());
- // Check that the *signed* result fits in a smi.
- __ cmp(answer.reg(), 0xc0000000);
- deferred->JumpToAnswerOutOfRange(negative);
- break;
- }
- default:
- UNREACHABLE();
- }
- // Smi-tag the result in answer.
- __ SmiTag(answer.reg());
- deferred->BindExit();
- left->Unuse();
- right->Unuse();
- ASSERT(answer.is_valid());
- return answer;
- }
-
- // Handle the other binary operations.
- left->ToRegister();
- right->ToRegister();
- // DeferredInlineBinaryOperation requires all the registers that it is told
- // about to be spilled.
- Result distinct_right = frame_->MakeDistinctAndSpilled(left, right);
- // A newly allocated register answer is used to hold the answer. The
- // registers containing left and right are not modified so they don't
- // need to be spilled in the fast case.
- answer = allocator_->Allocate();
- ASSERT(answer.is_valid());
-
- // Perform the smi tag check.
- DeferredInlineBinaryOperation* deferred =
- new DeferredInlineBinaryOperation(op,
- answer.reg(),
- left->reg(),
- distinct_right.reg(),
- left_type_info,
- right_type_info,
- overwrite_mode);
- Label non_smi_bit_op;
- if (op != Token::BIT_OR) {
- JumpIfNotBothSmiUsingTypeInfo(left->reg(), right->reg(), answer.reg(),
- left_type_info, right_type_info,
- deferred->NonSmiInputLabel());
- }
-
- __ mov(answer.reg(), left->reg());
- switch (op) {
- case Token::ADD:
- __ add(answer.reg(), Operand(right->reg()));
- deferred->Branch(overflow);
- break;
-
- case Token::SUB:
- __ sub(answer.reg(), Operand(right->reg()));
- deferred->Branch(overflow);
- break;
-
- case Token::MUL: {
- // If the smi tag is 0 we can just leave the tag on one operand.
- STATIC_ASSERT(kSmiTag == 0); // Adjust code below if not the case.
- // Remove smi tag from the left operand (but keep sign).
- // Left-hand operand has been copied into answer.
- __ SmiUntag(answer.reg());
- // Do multiplication of smis, leaving result in answer.
- __ imul(answer.reg(), Operand(right->reg()));
- // Go slow on overflows.
- deferred->Branch(overflow);
- // Check for negative zero result. If product is zero, and one
- // argument is negative, go to slow case. The frame is unchanged
- // in this block, so local control flow can use a Label rather
- // than a JumpTarget.
- if (!expr->no_negative_zero()) {
- Label non_zero_result;
- __ test(answer.reg(), Operand(answer.reg()));
- __ j(not_zero, &non_zero_result, taken);
- __ mov(answer.reg(), left->reg());
- __ or_(answer.reg(), Operand(right->reg()));
- deferred->Branch(negative);
- __ xor_(answer.reg(), Operand(answer.reg())); // Positive 0 is correct.
- __ bind(&non_zero_result);
- }
- break;
- }
-
- case Token::BIT_OR:
- __ or_(answer.reg(), Operand(right->reg()));
- __ test(answer.reg(), Immediate(kSmiTagMask));
- __ j(not_zero, deferred->NonSmiInputLabel());
- break;
-
- case Token::BIT_AND:
- __ and_(answer.reg(), Operand(right->reg()));
- break;
-
- case Token::BIT_XOR:
- __ xor_(answer.reg(), Operand(right->reg()));
- break;
-
- default:
- UNREACHABLE();
- break;
- }
-
- deferred->BindExit();
- left->Unuse();
- right->Unuse();
- ASSERT(answer.is_valid());
- return answer;
-}
-
-
-// Call the appropriate binary operation stub to compute src op value
-// and leave the result in dst.
-class DeferredInlineSmiOperation: public DeferredCode {
- public:
- DeferredInlineSmiOperation(Token::Value op,
- Register dst,
- Register src,
- TypeInfo type_info,
- Smi* value,
- OverwriteMode overwrite_mode)
- : op_(op),
- dst_(dst),
- src_(src),
- type_info_(type_info),
- value_(value),
- overwrite_mode_(overwrite_mode) {
- if (type_info.IsSmi()) overwrite_mode_ = NO_OVERWRITE;
- set_comment("[ DeferredInlineSmiOperation");
- }
-
- virtual void Generate();
-
- private:
- Token::Value op_;
- Register dst_;
- Register src_;
- TypeInfo type_info_;
- Smi* value_;
- OverwriteMode overwrite_mode_;
-};
-
-
-void DeferredInlineSmiOperation::Generate() {
- // For mod we don't generate all the Smi code inline.
- GenericBinaryOpStub stub(
- op_,
- overwrite_mode_,
- (op_ == Token::MOD) ? NO_GENERIC_BINARY_FLAGS : NO_SMI_CODE_IN_STUB,
- TypeInfo::Combine(TypeInfo::Smi(), type_info_));
- stub.GenerateCall(masm_, src_, value_);
- if (!dst_.is(eax)) __ mov(dst_, eax);
-}
-
-
-// Call the appropriate binary operation stub to compute value op src
-// and leave the result in dst.
-class DeferredInlineSmiOperationReversed: public DeferredCode {
- public:
- DeferredInlineSmiOperationReversed(Token::Value op,
- Register dst,
- Smi* value,
- Register src,
- TypeInfo type_info,
- OverwriteMode overwrite_mode)
- : op_(op),
- dst_(dst),
- type_info_(type_info),
- value_(value),
- src_(src),
- overwrite_mode_(overwrite_mode) {
- set_comment("[ DeferredInlineSmiOperationReversed");
- }
-
- virtual void Generate();
-
- private:
- Token::Value op_;
- Register dst_;
- TypeInfo type_info_;
- Smi* value_;
- Register src_;
- OverwriteMode overwrite_mode_;
-};
-
-
-void DeferredInlineSmiOperationReversed::Generate() {
- GenericBinaryOpStub stub(
- op_,
- overwrite_mode_,
- NO_SMI_CODE_IN_STUB,
- TypeInfo::Combine(TypeInfo::Smi(), type_info_));
- stub.GenerateCall(masm_, value_, src_);
- if (!dst_.is(eax)) __ mov(dst_, eax);
-}
-
-
-// The result of src + value is in dst. It either overflowed or was not
-// smi tagged. Undo the speculative addition and call the appropriate
-// specialized stub for add. The result is left in dst.
-class DeferredInlineSmiAdd: public DeferredCode {
- public:
- DeferredInlineSmiAdd(Register dst,
- TypeInfo type_info,
- Smi* value,
- OverwriteMode overwrite_mode)
- : dst_(dst),
- type_info_(type_info),
- value_(value),
- overwrite_mode_(overwrite_mode) {
- if (type_info_.IsSmi()) overwrite_mode_ = NO_OVERWRITE;
- set_comment("[ DeferredInlineSmiAdd");
- }
-
- virtual void Generate();
-
- private:
- Register dst_;
- TypeInfo type_info_;
- Smi* value_;
- OverwriteMode overwrite_mode_;
-};
-
-
-void DeferredInlineSmiAdd::Generate() {
- // Undo the optimistic add operation and call the shared stub.
- __ sub(Operand(dst_), Immediate(value_));
- GenericBinaryOpStub igostub(
- Token::ADD,
- overwrite_mode_,
- NO_SMI_CODE_IN_STUB,
- TypeInfo::Combine(TypeInfo::Smi(), type_info_));
- igostub.GenerateCall(masm_, dst_, value_);
- if (!dst_.is(eax)) __ mov(dst_, eax);
-}
-
-
-// The result of value + src is in dst. It either overflowed or was not
-// smi tagged. Undo the speculative addition and call the appropriate
-// specialized stub for add. The result is left in dst.
-class DeferredInlineSmiAddReversed: public DeferredCode {
- public:
- DeferredInlineSmiAddReversed(Register dst,
- TypeInfo type_info,
- Smi* value,
- OverwriteMode overwrite_mode)
- : dst_(dst),
- type_info_(type_info),
- value_(value),
- overwrite_mode_(overwrite_mode) {
- set_comment("[ DeferredInlineSmiAddReversed");
- }
-
- virtual void Generate();
-
- private:
- Register dst_;
- TypeInfo type_info_;
- Smi* value_;
- OverwriteMode overwrite_mode_;
-};
-
-
-void DeferredInlineSmiAddReversed::Generate() {
- // Undo the optimistic add operation and call the shared stub.
- __ sub(Operand(dst_), Immediate(value_));
- GenericBinaryOpStub igostub(
- Token::ADD,
- overwrite_mode_,
- NO_SMI_CODE_IN_STUB,
- TypeInfo::Combine(TypeInfo::Smi(), type_info_));
- igostub.GenerateCall(masm_, value_, dst_);
- if (!dst_.is(eax)) __ mov(dst_, eax);
-}
-
-
-// The result of src - value is in dst. It either overflowed or was not
-// smi tagged. Undo the speculative subtraction and call the
-// appropriate specialized stub for subtract. The result is left in
-// dst.
-class DeferredInlineSmiSub: public DeferredCode {
- public:
- DeferredInlineSmiSub(Register dst,
- TypeInfo type_info,
- Smi* value,
- OverwriteMode overwrite_mode)
- : dst_(dst),
- type_info_(type_info),
- value_(value),
- overwrite_mode_(overwrite_mode) {
- if (type_info.IsSmi()) overwrite_mode_ = NO_OVERWRITE;
- set_comment("[ DeferredInlineSmiSub");
- }
-
- virtual void Generate();
-
- private:
- Register dst_;
- TypeInfo type_info_;
- Smi* value_;
- OverwriteMode overwrite_mode_;
-};
-
-
-void DeferredInlineSmiSub::Generate() {
- // Undo the optimistic sub operation and call the shared stub.
- __ add(Operand(dst_), Immediate(value_));
- GenericBinaryOpStub igostub(
- Token::SUB,
- overwrite_mode_,
- NO_SMI_CODE_IN_STUB,
- TypeInfo::Combine(TypeInfo::Smi(), type_info_));
- igostub.GenerateCall(masm_, dst_, value_);
- if (!dst_.is(eax)) __ mov(dst_, eax);
-}
-
-
-Result CodeGenerator::ConstantSmiBinaryOperation(BinaryOperation* expr,
- Result* operand,
- Handle<Object> value,
- bool reversed,
- OverwriteMode overwrite_mode) {
- // Generate inline code for a binary operation when one of the
- // operands is a constant smi. Consumes the argument "operand".
- if (IsUnsafeSmi(value)) {
- Result unsafe_operand(value);
- if (reversed) {
- return LikelySmiBinaryOperation(expr, &unsafe_operand, operand,
- overwrite_mode);
- } else {
- return LikelySmiBinaryOperation(expr, operand, &unsafe_operand,
- overwrite_mode);
- }
- }
-
- // Get the literal value.
- Smi* smi_value = Smi::cast(*value);
- int int_value = smi_value->value();
-
- Token::Value op = expr->op();
- Result answer;
- switch (op) {
- case Token::ADD: {
- operand->ToRegister();
- frame_->Spill(operand->reg());
-
- // Optimistically add. Call the specialized add stub if the
- // result is not a smi or overflows.
- DeferredCode* deferred = NULL;
- if (reversed) {
- deferred = new DeferredInlineSmiAddReversed(operand->reg(),
- operand->type_info(),
- smi_value,
- overwrite_mode);
- } else {
- deferred = new DeferredInlineSmiAdd(operand->reg(),
- operand->type_info(),
- smi_value,
- overwrite_mode);
- }
- __ add(Operand(operand->reg()), Immediate(value));
- deferred->Branch(overflow);
- if (!operand->type_info().IsSmi()) {
- __ test(operand->reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
- } else if (FLAG_debug_code) {
- __ AbortIfNotSmi(operand->reg());
- }
- deferred->BindExit();
- answer = *operand;
- break;
- }
-
- case Token::SUB: {
- DeferredCode* deferred = NULL;
- if (reversed) {
- // The reversed case is only hit when the right operand is not a
- // constant.
- ASSERT(operand->is_register());
- answer = allocator()->Allocate();
- ASSERT(answer.is_valid());
- __ Set(answer.reg(), Immediate(value));
- deferred =
- new DeferredInlineSmiOperationReversed(op,
- answer.reg(),
- smi_value,
- operand->reg(),
- operand->type_info(),
- overwrite_mode);
- __ sub(answer.reg(), Operand(operand->reg()));
- } else {
- operand->ToRegister();
- frame_->Spill(operand->reg());
- answer = *operand;
- deferred = new DeferredInlineSmiSub(operand->reg(),
- operand->type_info(),
- smi_value,
- overwrite_mode);
- __ sub(Operand(operand->reg()), Immediate(value));
- }
- deferred->Branch(overflow);
- if (!operand->type_info().IsSmi()) {
- __ test(answer.reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
- } else if (FLAG_debug_code) {
- __ AbortIfNotSmi(operand->reg());
- }
- deferred->BindExit();
- operand->Unuse();
- break;
- }
-
- case Token::SAR:
- if (reversed) {
- Result constant_operand(value);
- answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
- overwrite_mode);
- } else {
- // Only the least significant 5 bits of the shift value are used.
- // In the slow case, this masking is done inside the runtime call.
- int shift_value = int_value & 0x1f;
- operand->ToRegister();
- frame_->Spill(operand->reg());
- if (!operand->type_info().IsSmi()) {
- DeferredInlineSmiOperation* deferred =
- new DeferredInlineSmiOperation(op,
- operand->reg(),
- operand->reg(),
- operand->type_info(),
- smi_value,
- overwrite_mode);
- __ test(operand->reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
- if (shift_value > 0) {
- __ sar(operand->reg(), shift_value);
- __ and_(operand->reg(), ~kSmiTagMask);
- }
- deferred->BindExit();
- } else {
- if (FLAG_debug_code) {
- __ AbortIfNotSmi(operand->reg());
- }
- if (shift_value > 0) {
- __ sar(operand->reg(), shift_value);
- __ and_(operand->reg(), ~kSmiTagMask);
- }
- }
- answer = *operand;
- }
- break;
-
- case Token::SHR:
- if (reversed) {
- Result constant_operand(value);
- answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
- overwrite_mode);
- } else {
- // Only the least significant 5 bits of the shift value are used.
- // In the slow case, this masking is done inside the runtime call.
- int shift_value = int_value & 0x1f;
- operand->ToRegister();
- answer = allocator()->Allocate();
- ASSERT(answer.is_valid());
- DeferredInlineSmiOperation* deferred =
- new DeferredInlineSmiOperation(op,
- answer.reg(),
- operand->reg(),
- operand->type_info(),
- smi_value,
- overwrite_mode);
- if (!operand->type_info().IsSmi()) {
- __ test(operand->reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
- } else if (FLAG_debug_code) {
- __ AbortIfNotSmi(operand->reg());
- }
- __ mov(answer.reg(), operand->reg());
- __ SmiUntag(answer.reg());
- __ shr(answer.reg(), shift_value);
- // A negative Smi shifted right two is in the positive Smi range.
- if (shift_value < 2) {
- __ test(answer.reg(), Immediate(0xc0000000));
- deferred->Branch(not_zero);
- }
- operand->Unuse();
- __ SmiTag(answer.reg());
- deferred->BindExit();
- }
- break;
-
- case Token::SHL:
- if (reversed) {
- // Move operand into ecx and also into a second register.
- // If operand is already in a register, take advantage of that.
- // This lets us modify ecx, but still bail out to deferred code.
- Result right;
- Result right_copy_in_ecx;
- TypeInfo right_type_info = operand->type_info();
- operand->ToRegister();
- if (operand->reg().is(ecx)) {
- right = allocator()->Allocate();
- __ mov(right.reg(), ecx);
- frame_->Spill(ecx);
- right_copy_in_ecx = *operand;
- } else {
- right_copy_in_ecx = allocator()->Allocate(ecx);
- __ mov(ecx, operand->reg());
- right = *operand;
- }
- operand->Unuse();
-
- answer = allocator()->Allocate();
- DeferredInlineSmiOperationReversed* deferred =
- new DeferredInlineSmiOperationReversed(op,
- answer.reg(),
- smi_value,
- right.reg(),
- right_type_info,
- overwrite_mode);
- __ mov(answer.reg(), Immediate(int_value));
- __ sar(ecx, kSmiTagSize);
- if (!right_type_info.IsSmi()) {
- deferred->Branch(carry);
- } else if (FLAG_debug_code) {
- __ AbortIfNotSmi(right.reg());
- }
- __ shl_cl(answer.reg());
- __ cmp(answer.reg(), 0xc0000000);
- deferred->Branch(sign);
- __ SmiTag(answer.reg());
-
- deferred->BindExit();
- } else {
- // Only the least significant 5 bits of the shift value are used.
- // In the slow case, this masking is done inside the runtime call.
- int shift_value = int_value & 0x1f;
- operand->ToRegister();
- if (shift_value == 0) {
- // Spill operand so it can be overwritten in the slow case.
- frame_->Spill(operand->reg());
- DeferredInlineSmiOperation* deferred =
- new DeferredInlineSmiOperation(op,
- operand->reg(),
- operand->reg(),
- operand->type_info(),
- smi_value,
- overwrite_mode);
- __ test(operand->reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
- deferred->BindExit();
- answer = *operand;
- } else {
- // Use a fresh temporary for nonzero shift values.
- answer = allocator()->Allocate();
- ASSERT(answer.is_valid());
- DeferredInlineSmiOperation* deferred =
- new DeferredInlineSmiOperation(op,
- answer.reg(),
- operand->reg(),
- operand->type_info(),
- smi_value,
- overwrite_mode);
- if (!operand->type_info().IsSmi()) {
- __ test(operand->reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
- } else if (FLAG_debug_code) {
- __ AbortIfNotSmi(operand->reg());
- }
- __ mov(answer.reg(), operand->reg());
- STATIC_ASSERT(kSmiTag == 0); // adjust code if not the case
- // We do no shifts, only the Smi conversion, if shift_value is 1.
- if (shift_value > 1) {
- __ shl(answer.reg(), shift_value - 1);
- }
- // Convert int result to Smi, checking that it is in int range.
- STATIC_ASSERT(kSmiTagSize == 1); // adjust code if not the case
- __ add(answer.reg(), Operand(answer.reg()));
- deferred->Branch(overflow);
- deferred->BindExit();
- operand->Unuse();
- }
- }
- break;
-
- case Token::BIT_OR:
- case Token::BIT_XOR:
- case Token::BIT_AND: {
- operand->ToRegister();
- // DeferredInlineBinaryOperation requires all the registers that it is
- // told about to be spilled.
- frame_->Spill(operand->reg());
- DeferredInlineBinaryOperation* deferred = NULL;
- if (!operand->type_info().IsSmi()) {
- Result left = allocator()->Allocate();
- ASSERT(left.is_valid());
- Result right = allocator()->Allocate();
- ASSERT(right.is_valid());
- deferred = new DeferredInlineBinaryOperation(
- op,
- operand->reg(),
- left.reg(),
- right.reg(),
- operand->type_info(),
- TypeInfo::Smi(),
- overwrite_mode == NO_OVERWRITE ? NO_OVERWRITE : OVERWRITE_LEFT);
- __ test(operand->reg(), Immediate(kSmiTagMask));
- deferred->JumpToConstantRhs(not_zero, smi_value);
- } else if (FLAG_debug_code) {
- __ AbortIfNotSmi(operand->reg());
- }
- if (op == Token::BIT_AND) {
- __ and_(Operand(operand->reg()), Immediate(value));
- } else if (op == Token::BIT_XOR) {
- if (int_value != 0) {
- __ xor_(Operand(operand->reg()), Immediate(value));
- }
- } else {
- ASSERT(op == Token::BIT_OR);
- if (int_value != 0) {
- __ or_(Operand(operand->reg()), Immediate(value));
- }
- }
- if (deferred != NULL) deferred->BindExit();
- answer = *operand;
- break;
- }
-
- case Token::DIV:
- if (!reversed && int_value == 2) {
- operand->ToRegister();
- frame_->Spill(operand->reg());
-
- DeferredInlineSmiOperation* deferred =
- new DeferredInlineSmiOperation(op,
- operand->reg(),
- operand->reg(),
- operand->type_info(),
- smi_value,
- overwrite_mode);
- // Check that lowest log2(value) bits of operand are zero, and test
- // smi tag at the same time.
- STATIC_ASSERT(kSmiTag == 0);
- STATIC_ASSERT(kSmiTagSize == 1);
- __ test(operand->reg(), Immediate(3));
- deferred->Branch(not_zero); // Branch if non-smi or odd smi.
- __ sar(operand->reg(), 1);
- deferred->BindExit();
- answer = *operand;
- } else {
- // Cannot fall through MOD to default case, so we duplicate the
- // default case here.
- Result constant_operand(value);
- if (reversed) {
- answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
- overwrite_mode);
- } else {
- answer = LikelySmiBinaryOperation(expr, operand, &constant_operand,
- overwrite_mode);
- }
- }
- break;
-
- // Generate inline code for mod of powers of 2 and negative powers of 2.
- case Token::MOD:
- if (!reversed &&
- int_value != 0 &&
- (IsPowerOf2(int_value) || IsPowerOf2(-int_value))) {
- operand->ToRegister();
- frame_->Spill(operand->reg());
- DeferredCode* deferred =
- new DeferredInlineSmiOperation(op,
- operand->reg(),
- operand->reg(),
- operand->type_info(),
- smi_value,
- overwrite_mode);
- // Check for negative or non-Smi left hand side.
- __ test(operand->reg(), Immediate(kSmiTagMask | kSmiSignMask));
- deferred->Branch(not_zero);
- if (int_value < 0) int_value = -int_value;
- if (int_value == 1) {
- __ mov(operand->reg(), Immediate(Smi::FromInt(0)));
- } else {
- __ and_(operand->reg(), (int_value << kSmiTagSize) - 1);
- }
- deferred->BindExit();
- answer = *operand;
- break;
- }
- // Fall through if we did not find a power of 2 on the right hand side!
- // The next case must be the default.
-
- default: {
- Result constant_operand(value);
- if (reversed) {
- answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
- overwrite_mode);
- } else {
- answer = LikelySmiBinaryOperation(expr, operand, &constant_operand,
- overwrite_mode);
- }
- break;
- }
- }
- ASSERT(answer.is_valid());
- return answer;
-}
-
-
-static bool CouldBeNaN(const Result& result) {
- if (result.type_info().IsSmi()) return false;
- if (result.type_info().IsInteger32()) return false;
- if (!result.is_constant()) return true;
- if (!result.handle()->IsHeapNumber()) return false;
- return isnan(HeapNumber::cast(*result.handle())->value());
-}
-
-
-// Convert from signed to unsigned comparison to match the way EFLAGS are set
-// by FPU and XMM compare instructions.
-static Condition DoubleCondition(Condition cc) {
- switch (cc) {
- case less: return below;
- case equal: return equal;
- case less_equal: return below_equal;
- case greater: return above;
- case greater_equal: return above_equal;
- default: UNREACHABLE();
- }
- UNREACHABLE();
- return equal;
-}
-
-
-static CompareFlags ComputeCompareFlags(NaNInformation nan_info,
- bool inline_number_compare) {
- CompareFlags flags = NO_SMI_COMPARE_IN_STUB;
- if (nan_info == kCantBothBeNaN) {
- flags = static_cast<CompareFlags>(flags | CANT_BOTH_BE_NAN);
- }
- if (inline_number_compare) {
- flags = static_cast<CompareFlags>(flags | NO_NUMBER_COMPARE_IN_STUB);
- }
- return flags;
-}
-
-
-void CodeGenerator::Comparison(AstNode* node,
- Condition cc,
- bool strict,
- ControlDestination* dest) {
- // Strict only makes sense for equality comparisons.
- ASSERT(!strict || cc == equal);
-
- Result left_side;
- Result right_side;
- // Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order.
- if (cc == greater || cc == less_equal) {
- cc = ReverseCondition(cc);
- left_side = frame_->Pop();
- right_side = frame_->Pop();
- } else {
- right_side = frame_->Pop();
- left_side = frame_->Pop();
- }
- ASSERT(cc == less || cc == equal || cc == greater_equal);
-
- // If either side is a constant smi, optimize the comparison.
- bool left_side_constant_smi = false;
- bool left_side_constant_null = false;
- bool left_side_constant_1_char_string = false;
- if (left_side.is_constant()) {
- left_side_constant_smi = left_side.handle()->IsSmi();
- left_side_constant_null = left_side.handle()->IsNull();
- left_side_constant_1_char_string =
- (left_side.handle()->IsString() &&
- String::cast(*left_side.handle())->length() == 1 &&
- String::cast(*left_side.handle())->IsAsciiRepresentation());
- }
- bool right_side_constant_smi = false;
- bool right_side_constant_null = false;
- bool right_side_constant_1_char_string = false;
- if (right_side.is_constant()) {
- right_side_constant_smi = right_side.handle()->IsSmi();
- right_side_constant_null = right_side.handle()->IsNull();
- right_side_constant_1_char_string =
- (right_side.handle()->IsString() &&
- String::cast(*right_side.handle())->length() == 1 &&
- String::cast(*right_side.handle())->IsAsciiRepresentation());
- }
-
- if (left_side_constant_smi || right_side_constant_smi) {
- bool is_loop_condition = (node->AsExpression() != NULL) &&
- node->AsExpression()->is_loop_condition();
- ConstantSmiComparison(cc, strict, dest, &left_side, &right_side,
- left_side_constant_smi, right_side_constant_smi,
- is_loop_condition);
- } else if (left_side_constant_1_char_string ||
- right_side_constant_1_char_string) {
- if (left_side_constant_1_char_string && right_side_constant_1_char_string) {
- // Trivial case, comparing two constants.
- int left_value = String::cast(*left_side.handle())->Get(0);
- int right_value = String::cast(*right_side.handle())->Get(0);
- switch (cc) {
- case less:
- dest->Goto(left_value < right_value);
- break;
- case equal:
- dest->Goto(left_value == right_value);
- break;
- case greater_equal:
- dest->Goto(left_value >= right_value);
- break;
- default:
- UNREACHABLE();
- }
- } else {
- // Only one side is a constant 1 character string.
- // If left side is a constant 1-character string, reverse the operands.
- // Since one side is a constant string, conversion order does not matter.
- if (left_side_constant_1_char_string) {
- Result temp = left_side;
- left_side = right_side;
- right_side = temp;
- cc = ReverseCondition(cc);
- // This may reintroduce greater or less_equal as the value of cc.
- // CompareStub and the inline code both support all values of cc.
- }
- // Implement comparison against a constant string, inlining the case
- // where both sides are strings.
- left_side.ToRegister();
-
- // Here we split control flow to the stub call and inlined cases
- // before finally splitting it to the control destination. We use
- // a jump target and branching to duplicate the virtual frame at
- // the first split. We manually handle the off-frame references
- // by reconstituting them on the non-fall-through path.
- JumpTarget is_not_string, is_string;
- Register left_reg = left_side.reg();
- Handle<Object> right_val = right_side.handle();
- ASSERT(StringShape(String::cast(*right_val)).IsSymbol());
- __ test(left_side.reg(), Immediate(kSmiTagMask));
- is_not_string.Branch(zero, &left_side);
- Result temp = allocator_->Allocate();
- ASSERT(temp.is_valid());
- __ mov(temp.reg(),
- FieldOperand(left_side.reg(), HeapObject::kMapOffset));
- __ movzx_b(temp.reg(),
- FieldOperand(temp.reg(), Map::kInstanceTypeOffset));
- // If we are testing for equality then make use of the symbol shortcut.
- // Check if the right left hand side has the same type as the left hand
- // side (which is always a symbol).
- if (cc == equal) {
- Label not_a_symbol;
- STATIC_ASSERT(kSymbolTag != 0);
- // Ensure that no non-strings have the symbol bit set.
- STATIC_ASSERT(LAST_TYPE < kNotStringTag + kIsSymbolMask);
- __ test(temp.reg(), Immediate(kIsSymbolMask)); // Test the symbol bit.
- __ j(zero, ¬_a_symbol);
- // They are symbols, so do identity compare.
- __ cmp(left_side.reg(), right_side.handle());
- dest->true_target()->Branch(equal);
- dest->false_target()->Branch(not_equal);
- __ bind(¬_a_symbol);
- }
- // Call the compare stub if the left side is not a flat ascii string.
- __ and_(temp.reg(),
- kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask);
- __ cmp(temp.reg(), kStringTag | kSeqStringTag | kAsciiStringTag);
- temp.Unuse();
- is_string.Branch(equal, &left_side);
-
- // Setup and call the compare stub.
- is_not_string.Bind(&left_side);
- CompareFlags flags =
- static_cast<CompareFlags>(CANT_BOTH_BE_NAN | NO_SMI_COMPARE_IN_STUB);
- CompareStub stub(cc, strict, flags);
- Result result = frame_->CallStub(&stub, &left_side, &right_side);
- result.ToRegister();
- __ cmp(result.reg(), 0);
- result.Unuse();
- dest->true_target()->Branch(cc);
- dest->false_target()->Jump();
-
- is_string.Bind(&left_side);
- // left_side is a sequential ASCII string.
- left_side = Result(left_reg);
- right_side = Result(right_val);
- // Test string equality and comparison.
- Label comparison_done;
- if (cc == equal) {
- __ cmp(FieldOperand(left_side.reg(), String::kLengthOffset),
- Immediate(Smi::FromInt(1)));
- __ j(not_equal, &comparison_done);
- uint8_t char_value =
- static_cast<uint8_t>(String::cast(*right_val)->Get(0));
- __ cmpb(FieldOperand(left_side.reg(), SeqAsciiString::kHeaderSize),
- char_value);
- } else {
- __ cmp(FieldOperand(left_side.reg(), String::kLengthOffset),
- Immediate(Smi::FromInt(1)));
- // If the length is 0 then the jump is taken and the flags
- // correctly represent being less than the one-character string.
- __ j(below, &comparison_done);
- // Compare the first character of the string with the
- // constant 1-character string.
- uint8_t char_value =
- static_cast<uint8_t>(String::cast(*right_val)->Get(0));
- __ cmpb(FieldOperand(left_side.reg(), SeqAsciiString::kHeaderSize),
- char_value);
- __ j(not_equal, &comparison_done);
- // If the first character is the same then the long string sorts after
- // the short one.
- __ cmp(FieldOperand(left_side.reg(), String::kLengthOffset),
- Immediate(Smi::FromInt(1)));
- }
- __ bind(&comparison_done);
- left_side.Unuse();
- right_side.Unuse();
- dest->Split(cc);
- }
- } else {
- // Neither side is a constant Smi, constant 1-char string or constant null.
- // If either side is a non-smi constant, or known to be a heap number,
- // skip the smi check.
- bool known_non_smi =
- (left_side.is_constant() && !left_side.handle()->IsSmi()) ||
- (right_side.is_constant() && !right_side.handle()->IsSmi()) ||
- left_side.type_info().IsDouble() ||
- right_side.type_info().IsDouble();
-
- NaNInformation nan_info =
- (CouldBeNaN(left_side) && CouldBeNaN(right_side)) ?
- kBothCouldBeNaN :
- kCantBothBeNaN;
-
- // Inline number comparison handling any combination of smi's and heap
- // numbers if:
- // code is in a loop
- // the compare operation is different from equal
- // compare is not a for-loop comparison
- // The reason for excluding equal is that it will most likely be done
- // with smi's (not heap numbers) and the code to comparing smi's is inlined
- // separately. The same reason applies for for-loop comparison which will
- // also most likely be smi comparisons.
- bool is_loop_condition = (node->AsExpression() != NULL)
- && node->AsExpression()->is_loop_condition();
- bool inline_number_compare =
- loop_nesting() > 0 && cc != equal && !is_loop_condition;
-
- // Left and right needed in registers for the following code.
- left_side.ToRegister();
- right_side.ToRegister();
-
- if (known_non_smi) {
- // Inlined equality check:
- // If at least one of the objects is not NaN, then if the objects
- // are identical, they are equal.
- if (nan_info == kCantBothBeNaN && cc == equal) {
- __ cmp(left_side.reg(), Operand(right_side.reg()));
- dest->true_target()->Branch(equal);
- }
-
- // Inlined number comparison:
- if (inline_number_compare) {
- GenerateInlineNumberComparison(&left_side, &right_side, cc, dest);
- }
-
- // End of in-line compare, call out to the compare stub. Don't include
- // number comparison in the stub if it was inlined.
- CompareFlags flags = ComputeCompareFlags(nan_info, inline_number_compare);
- CompareStub stub(cc, strict, flags);
- Result answer = frame_->CallStub(&stub, &left_side, &right_side);
- __ test(answer.reg(), Operand(answer.reg()));
- answer.Unuse();
- dest->Split(cc);
- } else {
- // Here we split control flow to the stub call and inlined cases
- // before finally splitting it to the control destination. We use
- // a jump target and branching to duplicate the virtual frame at
- // the first split. We manually handle the off-frame references
- // by reconstituting them on the non-fall-through path.
- JumpTarget is_smi;
- Register left_reg = left_side.reg();
- Register right_reg = right_side.reg();
-
- // In-line check for comparing two smis.
- JumpIfBothSmiUsingTypeInfo(&left_side, &right_side, &is_smi);
-
- if (has_valid_frame()) {
- // Inline the equality check if both operands can't be a NaN. If both
- // objects are the same they are equal.
- if (nan_info == kCantBothBeNaN && cc == equal) {
- __ cmp(left_side.reg(), Operand(right_side.reg()));
- dest->true_target()->Branch(equal);
- }
-
- // Inlined number comparison:
- if (inline_number_compare) {
- GenerateInlineNumberComparison(&left_side, &right_side, cc, dest);
- }
-
- // End of in-line compare, call out to the compare stub. Don't include
- // number comparison in the stub if it was inlined.
- CompareFlags flags =
- ComputeCompareFlags(nan_info, inline_number_compare);
- CompareStub stub(cc, strict, flags);
- Result answer = frame_->CallStub(&stub, &left_side, &right_side);
- __ test(answer.reg(), Operand(answer.reg()));
- answer.Unuse();
- if (is_smi.is_linked()) {
- dest->true_target()->Branch(cc);
- dest->false_target()->Jump();
- } else {
- dest->Split(cc);
- }
- }
-
- if (is_smi.is_linked()) {
- is_smi.Bind();
- left_side = Result(left_reg);
- right_side = Result(right_reg);
- __ cmp(left_side.reg(), Operand(right_side.reg()));
- right_side.Unuse();
- left_side.Unuse();
- dest->Split(cc);
- }
- }
- }
-}
-
-
-void CodeGenerator::ConstantSmiComparison(Condition cc,
- bool strict,
- ControlDestination* dest,
- Result* left_side,
- Result* right_side,
- bool left_side_constant_smi,
- bool right_side_constant_smi,
- bool is_loop_condition) {
- if (left_side_constant_smi && right_side_constant_smi) {
- // Trivial case, comparing two constants.
- int left_value = Smi::cast(*left_side->handle())->value();
- int right_value = Smi::cast(*right_side->handle())->value();
- switch (cc) {
- case less:
- dest->Goto(left_value < right_value);
- break;
- case equal:
- dest->Goto(left_value == right_value);
- break;
- case greater_equal:
- dest->Goto(left_value >= right_value);
- break;
- default:
- UNREACHABLE();
- }
- } else {
- // Only one side is a constant Smi.
- // If left side is a constant Smi, reverse the operands.
- // Since one side is a constant Smi, conversion order does not matter.
- if (left_side_constant_smi) {
- Result* temp = left_side;
- left_side = right_side;
- right_side = temp;
- cc = ReverseCondition(cc);
- // This may re-introduce greater or less_equal as the value of cc.
- // CompareStub and the inline code both support all values of cc.
- }
- // Implement comparison against a constant Smi, inlining the case
- // where both sides are Smis.
- left_side->ToRegister();
- Register left_reg = left_side->reg();
- Handle<Object> right_val = right_side->handle();
-
- if (left_side->is_smi()) {
- if (FLAG_debug_code) {
- __ AbortIfNotSmi(left_reg);
- }
- // Test smi equality and comparison by signed int comparison.
- if (IsUnsafeSmi(right_side->handle())) {
- right_side->ToRegister();
- __ cmp(left_reg, Operand(right_side->reg()));
- } else {
- __ cmp(Operand(left_reg), Immediate(right_side->handle()));
- }
- left_side->Unuse();
- right_side->Unuse();
- dest->Split(cc);
- } else {
- // Only the case where the left side could possibly be a non-smi is left.
- JumpTarget is_smi;
- if (cc == equal) {
- // We can do the equality comparison before the smi check.
- __ cmp(Operand(left_reg), Immediate(right_side->handle()));
- dest->true_target()->Branch(equal);
- __ test(left_reg, Immediate(kSmiTagMask));
- dest->false_target()->Branch(zero);
- } else {
- // Do the smi check, then the comparison.
- __ test(left_reg, Immediate(kSmiTagMask));
- is_smi.Branch(zero, left_side, right_side);
- }
-
- // Jump or fall through to here if we are comparing a non-smi to a
- // constant smi. If the non-smi is a heap number and this is not
- // a loop condition, inline the floating point code.
- if (!is_loop_condition &&
- masm()->isolate()->cpu_features()->IsSupported(SSE2)) {
- // Right side is a constant smi and left side has been checked
- // not to be a smi.
- CpuFeatures::Scope use_sse2(SSE2);
- JumpTarget not_number;
- __ cmp(FieldOperand(left_reg, HeapObject::kMapOffset),
- Immediate(FACTORY->heap_number_map()));
- not_number.Branch(not_equal, left_side);
- __ movdbl(xmm1,
- FieldOperand(left_reg, HeapNumber::kValueOffset));
- int value = Smi::cast(*right_val)->value();
- if (value == 0) {
- __ xorpd(xmm0, xmm0);
- } else {
- Result temp = allocator()->Allocate();
- __ mov(temp.reg(), Immediate(value));
- __ cvtsi2sd(xmm0, Operand(temp.reg()));
- temp.Unuse();
- }
- __ ucomisd(xmm1, xmm0);
- // Jump to builtin for NaN.
- not_number.Branch(parity_even, left_side);
- left_side->Unuse();
- dest->true_target()->Branch(DoubleCondition(cc));
- dest->false_target()->Jump();
- not_number.Bind(left_side);
- }
-
- // Setup and call the compare stub.
- CompareFlags flags =
- static_cast<CompareFlags>(CANT_BOTH_BE_NAN | NO_SMI_CODE_IN_STUB);
- CompareStub stub(cc, strict, flags);
- Result result = frame_->CallStub(&stub, left_side, right_side);
- result.ToRegister();
- __ test(result.reg(), Operand(result.reg()));
- result.Unuse();
- if (cc == equal) {
- dest->Split(cc);
- } else {
- dest->true_target()->Branch(cc);
- dest->false_target()->Jump();
-
- // It is important for performance for this case to be at the end.
- is_smi.Bind(left_side, right_side);
- if (IsUnsafeSmi(right_side->handle())) {
- right_side->ToRegister();
- __ cmp(left_reg, Operand(right_side->reg()));
- } else {
- __ cmp(Operand(left_reg), Immediate(right_side->handle()));
- }
- left_side->Unuse();
- right_side->Unuse();
- dest->Split(cc);
- }
- }
- }
-}
-
-
-// Check that the comparison operand is a number. Jump to not_numbers jump
-// target passing the left and right result if the operand is not a number.
-static void CheckComparisonOperand(MacroAssembler* masm_,
- Result* operand,
- Result* left_side,
- Result* right_side,
- JumpTarget* not_numbers) {
- // Perform check if operand is not known to be a number.
- if (!operand->type_info().IsNumber()) {
- Label done;
- __ test(operand->reg(), Immediate(kSmiTagMask));
- __ j(zero, &done);
- __ cmp(FieldOperand(operand->reg(), HeapObject::kMapOffset),
- Immediate(FACTORY->heap_number_map()));
- not_numbers->Branch(not_equal, left_side, right_side, not_taken);
- __ bind(&done);
- }
-}
-
-
-// Load a comparison operand to the FPU stack. This assumes that the operand has
-// already been checked and is a number.
-static void LoadComparisonOperand(MacroAssembler* masm_,
- Result* operand) {
- Label done;
- if (operand->type_info().IsDouble()) {
- // Operand is known to be a heap number, just load it.
- __ fld_d(FieldOperand(operand->reg(), HeapNumber::kValueOffset));
- } else if (operand->type_info().IsSmi()) {
- // Operand is known to be a smi. Convert it to double and keep the original
- // smi.
- __ SmiUntag(operand->reg());
- __ push(operand->reg());
- __ fild_s(Operand(esp, 0));
- __ pop(operand->reg());
- __ SmiTag(operand->reg());
- } else {
- // Operand type not known, check for smi otherwise assume heap number.
- Label smi;
- __ test(operand->reg(), Immediate(kSmiTagMask));
- __ j(zero, &smi);
- __ fld_d(FieldOperand(operand->reg(), HeapNumber::kValueOffset));
- __ jmp(&done);
- __ bind(&smi);
- __ SmiUntag(operand->reg());
- __ push(operand->reg());
- __ fild_s(Operand(esp, 0));
- __ pop(operand->reg());
- __ SmiTag(operand->reg());
- __ jmp(&done);
- }
- __ bind(&done);
-}
-
-
-// Load a comparison operand into into a XMM register. Jump to not_numbers jump
-// target passing the left and right result if the operand is not a number.
-static void LoadComparisonOperandSSE2(MacroAssembler* masm_,
- Result* operand,
- XMMRegister xmm_reg,
- Result* left_side,
- Result* right_side,
- JumpTarget* not_numbers) {
- Label done;
- if (operand->type_info().IsDouble()) {
- // Operand is known to be a heap number, just load it.
- __ movdbl(xmm_reg, FieldOperand(operand->reg(), HeapNumber::kValueOffset));
- } else if (operand->type_info().IsSmi()) {
- // Operand is known to be a smi. Convert it to double and keep the original
- // smi.
- __ SmiUntag(operand->reg());
- __ cvtsi2sd(xmm_reg, Operand(operand->reg()));
- __ SmiTag(operand->reg());
- } else {
- // Operand type not known, check for smi or heap number.
- Label smi;
- __ test(operand->reg(), Immediate(kSmiTagMask));
- __ j(zero, &smi);
- if (!operand->type_info().IsNumber()) {
- __ cmp(FieldOperand(operand->reg(), HeapObject::kMapOffset),
- Immediate(FACTORY->heap_number_map()));
- not_numbers->Branch(not_equal, left_side, right_side, taken);
- }
- __ movdbl(xmm_reg, FieldOperand(operand->reg(), HeapNumber::kValueOffset));
- __ jmp(&done);
-
- __ bind(&smi);
- // Comvert smi to float and keep the original smi.
- __ SmiUntag(operand->reg());
- __ cvtsi2sd(xmm_reg, Operand(operand->reg()));
- __ SmiTag(operand->reg());
- __ jmp(&done);
- }
- __ bind(&done);
-}
-
-
-void CodeGenerator::GenerateInlineNumberComparison(Result* left_side,
- Result* right_side,
- Condition cc,
- ControlDestination* dest) {
- ASSERT(left_side->is_register());
- ASSERT(right_side->is_register());
-
- JumpTarget not_numbers;
- if (masm()->isolate()->cpu_features()->IsSupported(SSE2)) {
- CpuFeatures::Scope use_sse2(SSE2);
-
- // Load left and right operand into registers xmm0 and xmm1 and compare.
- LoadComparisonOperandSSE2(masm_, left_side, xmm0, left_side, right_side,
- ¬_numbers);
- LoadComparisonOperandSSE2(masm_, right_side, xmm1, left_side, right_side,
- ¬_numbers);
- __ ucomisd(xmm0, xmm1);
- } else {
- Label check_right, compare;
-
- // Make sure that both comparison operands are numbers.
- CheckComparisonOperand(masm_, left_side, left_side, right_side,
- ¬_numbers);
- CheckComparisonOperand(masm_, right_side, left_side, right_side,
- ¬_numbers);
-
- // Load right and left operand to FPU stack and compare.
- LoadComparisonOperand(masm_, right_side);
- LoadComparisonOperand(masm_, left_side);
- __ FCmp();
- }
-
- // Bail out if a NaN is involved.
- not_numbers.Branch(parity_even, left_side, right_side, not_taken);
-
- // Split to destination targets based on comparison.
- left_side->Unuse();
- right_side->Unuse();
- dest->true_target()->Branch(DoubleCondition(cc));
- dest->false_target()->Jump();
-
- not_numbers.Bind(left_side, right_side);
-}
-
-
-// Call the function just below TOS on the stack with the given
-// arguments. The receiver is the TOS.
-void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
- CallFunctionFlags flags,
- int position) {
- // Push the arguments ("left-to-right") on the stack.
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- frame_->SpillTop();
- }
-
- // Record the position for debugging purposes.
- CodeForSourcePosition(position);
-
- // Use the shared code stub to call the function.
- InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub call_function(arg_count, in_loop, flags);
- Result answer = frame_->CallStub(&call_function, arg_count + 1);
- // Restore context and replace function on the stack with the
- // result of the stub invocation.
- frame_->RestoreContextRegister();
- frame_->SetElementAt(0, &answer);
-}
-
-
-void CodeGenerator::CallApplyLazy(Expression* applicand,
- Expression* receiver,
- VariableProxy* arguments,
- int position) {
- // An optimized implementation of expressions of the form
- // x.apply(y, arguments).
- // If the arguments object of the scope has not been allocated,
- // and x.apply is Function.prototype.apply, this optimization
- // just copies y and the arguments of the current function on the
- // stack, as receiver and arguments, and calls x.
- // In the implementation comments, we call x the applicand
- // and y the receiver.
- ASSERT(ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION);
- ASSERT(arguments->IsArguments());
-
- // Load applicand.apply onto the stack. This will usually
- // give us a megamorphic load site. Not super, but it works.
- Load(applicand);
- frame()->Dup();
- Handle<String> name = FACTORY->LookupAsciiSymbol("apply");
- frame()->Push(name);
- Result answer = frame()->CallLoadIC(RelocInfo::CODE_TARGET);
- __ nop();
- frame()->Push(&answer);
-
- // Load the receiver and the existing arguments object onto the
- // expression stack. Avoid allocating the arguments object here.
- Load(receiver);
- LoadFromSlot(scope()->arguments()->AsSlot(), NOT_INSIDE_TYPEOF);
-
- // Emit the source position information after having loaded the
- // receiver and the arguments.
- CodeForSourcePosition(position);
- // Contents of frame at this point:
- // Frame[0]: arguments object of the current function or the hole.
- // Frame[1]: receiver
- // Frame[2]: applicand.apply
- // Frame[3]: applicand.
-
- // Check if the arguments object has been lazily allocated
- // already. If so, just use that instead of copying the arguments
- // from the stack. This also deals with cases where a local variable
- // named 'arguments' has been introduced.
- frame_->Dup();
- Result probe = frame_->Pop();
- { VirtualFrame::SpilledScope spilled_scope;
- Label slow, done;
- bool try_lazy = true;
- if (probe.is_constant()) {
- try_lazy = probe.handle()->IsArgumentsMarker();
- } else {
- __ cmp(Operand(probe.reg()), Immediate(FACTORY->arguments_marker()));
- probe.Unuse();
- __ j(not_equal, &slow);
- }
-
- if (try_lazy) {
- Label build_args;
- // Get rid of the arguments object probe.
- frame_->Drop(); // Can be called on a spilled frame.
- // Stack now has 3 elements on it.
- // Contents of stack at this point:
- // esp[0]: receiver
- // esp[1]: applicand.apply
- // esp[2]: applicand.
-
- // Check that the receiver really is a JavaScript object.
- __ mov(eax, Operand(esp, 0));
- __ test(eax, Immediate(kSmiTagMask));
- __ j(zero, &build_args);
- // We allow all JSObjects including JSFunctions. As long as
- // JS_FUNCTION_TYPE is the last instance type and it is right
- // after LAST_JS_OBJECT_TYPE, we do not have to check the upper
- // bound.
- STATIC_ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
- STATIC_ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
- __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx);
- __ j(below, &build_args);
-
- // Check that applicand.apply is Function.prototype.apply.
- __ mov(eax, Operand(esp, kPointerSize));
- __ test(eax, Immediate(kSmiTagMask));
- __ j(zero, &build_args);
- __ CmpObjectType(eax, JS_FUNCTION_TYPE, ecx);
- __ j(not_equal, &build_args);
- __ mov(ecx, FieldOperand(eax, JSFunction::kCodeEntryOffset));
- __ sub(Operand(ecx), Immediate(Code::kHeaderSize - kHeapObjectTag));
- Handle<Code> apply_code(masm()->isolate()->builtins()->builtin(
- Builtins::kFunctionApply));
- __ cmp(Operand(ecx), Immediate(apply_code));
- __ j(not_equal, &build_args);
-
- // Check that applicand is a function.
- __ mov(edi, Operand(esp, 2 * kPointerSize));
- __ test(edi, Immediate(kSmiTagMask));
- __ j(zero, &build_args);
- __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
- __ j(not_equal, &build_args);
-
- // Copy the arguments to this function possibly from the
- // adaptor frame below it.
- Label invoke, adapted;
- __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
- __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
- __ cmp(Operand(ecx),
- Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
- __ j(equal, &adapted);
-
- // No arguments adaptor frame. Copy fixed number of arguments.
- __ mov(eax, Immediate(scope()->num_parameters()));
- for (int i = 0; i < scope()->num_parameters(); i++) {
- __ push(frame_->ParameterAt(i));
- }
- __ jmp(&invoke);
-
- // Arguments adaptor frame present. Copy arguments from there, but
- // avoid copying too many arguments to avoid stack overflows.
- __ bind(&adapted);
- static const uint32_t kArgumentsLimit = 1 * KB;
- __ mov(eax, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
- __ SmiUntag(eax);
- __ mov(ecx, Operand(eax));
- __ cmp(eax, kArgumentsLimit);
- __ j(above, &build_args);
-
- // Loop through the arguments pushing them onto the execution
- // stack. We don't inform the virtual frame of the push, so we don't
- // have to worry about getting rid of the elements from the virtual
- // frame.
- Label loop;
- // ecx is a small non-negative integer, due to the test above.
- __ test(ecx, Operand(ecx));
- __ j(zero, &invoke);
- __ bind(&loop);
- __ push(Operand(edx, ecx, times_pointer_size, 1 * kPointerSize));
- __ dec(ecx);
- __ j(not_zero, &loop);
-
- // Invoke the function.
- __ bind(&invoke);
- ParameterCount actual(eax);
- __ InvokeFunction(edi, actual, CALL_FUNCTION);
- // Drop applicand.apply and applicand from the stack, and push
- // the result of the function call, but leave the spilled frame
- // unchanged, with 3 elements, so it is correct when we compile the
- // slow-case code.
- __ add(Operand(esp), Immediate(2 * kPointerSize));
- __ push(eax);
- // Stack now has 1 element:
- // esp[0]: result
- __ jmp(&done);
-
- // Slow-case: Allocate the arguments object since we know it isn't
- // there, and fall-through to the slow-case where we call
- // applicand.apply.
- __ bind(&build_args);
- // Stack now has 3 elements, because we have jumped from where:
- // esp[0]: receiver
- // esp[1]: applicand.apply
- // esp[2]: applicand.
-
- // StoreArgumentsObject requires a correct frame, and may modify it.
- Result arguments_object = StoreArgumentsObject(false);
- frame_->SpillAll();
- arguments_object.ToRegister();
- frame_->EmitPush(arguments_object.reg());
- arguments_object.Unuse();
- // Stack and frame now have 4 elements.
- __ bind(&slow);
- }
-
- // Generic computation of x.apply(y, args) with no special optimization.
- // Flip applicand.apply and applicand on the stack, so
- // applicand looks like the receiver of the applicand.apply call.
- // Then process it as a normal function call.
- __ mov(eax, Operand(esp, 3 * kPointerSize));
- __ mov(ebx, Operand(esp, 2 * kPointerSize));
- __ mov(Operand(esp, 2 * kPointerSize), eax);
- __ mov(Operand(esp, 3 * kPointerSize), ebx);
-
- CallFunctionStub call_function(2, NOT_IN_LOOP, NO_CALL_FUNCTION_FLAGS);
- Result res = frame_->CallStub(&call_function, 3);
- // The function and its two arguments have been dropped.
- frame_->Drop(1); // Drop the receiver as well.
- res.ToRegister();
- frame_->EmitPush(res.reg());
- // Stack now has 1 element:
- // esp[0]: result
- if (try_lazy) __ bind(&done);
- } // End of spilled scope.
- // Restore the context register after a call.
- frame_->RestoreContextRegister();
-}
-
-
-class DeferredStackCheck: public DeferredCode {
- public:
- DeferredStackCheck() {
- set_comment("[ DeferredStackCheck");
- }
-
- virtual void Generate();
-};
-
-
-void DeferredStackCheck::Generate() {
- StackCheckStub stub;
- __ CallStub(&stub);
-}
-
-
-void CodeGenerator::CheckStack() {
- DeferredStackCheck* deferred = new DeferredStackCheck;
- ExternalReference stack_limit =
- ExternalReference::address_of_stack_limit(masm()->isolate());
- __ cmp(esp, Operand::StaticVariable(stack_limit));
- deferred->Branch(below);
- deferred->BindExit();
-}
-
-
-void CodeGenerator::VisitAndSpill(Statement* statement) {
- ASSERT(in_spilled_code());
- set_in_spilled_code(false);
- Visit(statement);
- if (frame_ != NULL) {
- frame_->SpillAll();
- }
- set_in_spilled_code(true);
-}
-
-
-void CodeGenerator::VisitStatementsAndSpill(ZoneList<Statement*>* statements) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- ASSERT(in_spilled_code());
- set_in_spilled_code(false);
- VisitStatements(statements);
- if (frame_ != NULL) {
- frame_->SpillAll();
- }
- set_in_spilled_code(true);
-
- ASSERT(!has_valid_frame() || frame_->height() == original_height);
-}
-
-
-void CodeGenerator::VisitStatements(ZoneList<Statement*>* statements) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- ASSERT(!in_spilled_code());
- for (int i = 0; has_valid_frame() && i < statements->length(); i++) {
- Visit(statements->at(i));
- }
- ASSERT(!has_valid_frame() || frame_->height() == original_height);
-}
-
-
-void CodeGenerator::VisitBlock(Block* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ Block");
- CodeForStatementPosition(node);
- node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
- VisitStatements(node->statements());
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- node->break_target()->Unuse();
-}
-
-
-void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
- // Call the runtime to declare the globals. The inevitable call
- // will sync frame elements to memory anyway, so we do it eagerly to
- // allow us to push the arguments directly into place.
- frame_->SyncRange(0, frame_->element_count() - 1);
-
- frame_->EmitPush(esi); // The context is the first argument.
- frame_->EmitPush(Immediate(pairs));
- frame_->EmitPush(Immediate(Smi::FromInt(is_eval() ? 1 : 0)));
- frame_->EmitPush(Immediate(Smi::FromInt(strict_mode_flag())));
- Result ignored = frame_->CallRuntime(Runtime::kDeclareGlobals, 4);
- // Return value is ignored.
-}
-
-
-void CodeGenerator::VisitDeclaration(Declaration* node) {
- Comment cmnt(masm_, "[ Declaration");
- Variable* var = node->proxy()->var();
- ASSERT(var != NULL); // must have been resolved
- Slot* slot = var->AsSlot();
-
- // If it was not possible to allocate the variable at compile time,
- // we need to "declare" it at runtime to make sure it actually
- // exists in the local context.
- if (slot != NULL && slot->type() == Slot::LOOKUP) {
- // Variables with a "LOOKUP" slot were introduced as non-locals
- // during variable resolution and must have mode DYNAMIC.
- ASSERT(var->is_dynamic());
- // For now, just do a runtime call. Sync the virtual frame eagerly
- // so we can simply push the arguments into place.
- frame_->SyncRange(0, frame_->element_count() - 1);
- frame_->EmitPush(esi);
- frame_->EmitPush(Immediate(var->name()));
- // Declaration nodes are always introduced in one of two modes.
- ASSERT(node->mode() == Variable::VAR || node->mode() == Variable::CONST);
- PropertyAttributes attr = node->mode() == Variable::VAR ? NONE : READ_ONLY;
- frame_->EmitPush(Immediate(Smi::FromInt(attr)));
- // Push initial value, if any.
- // Note: For variables we must not push an initial value (such as
- // 'undefined') because we may have a (legal) redeclaration and we
- // must not destroy the current value.
- if (node->mode() == Variable::CONST) {
- frame_->EmitPush(Immediate(FACTORY->the_hole_value()));
- } else if (node->fun() != NULL) {
- Load(node->fun());
- } else {
- frame_->EmitPush(Immediate(Smi::FromInt(0))); // no initial value!
- }
- Result ignored = frame_->CallRuntime(Runtime::kDeclareContextSlot, 4);
- // Ignore the return value (declarations are statements).
- return;
- }
-
- ASSERT(!var->is_global());
-
- // If we have a function or a constant, we need to initialize the variable.
- Expression* val = NULL;
- if (node->mode() == Variable::CONST) {
- val = new Literal(FACTORY->the_hole_value());
- } else {
- val = node->fun(); // NULL if we don't have a function
- }
-
- if (val != NULL) {
- {
- // Set the initial value.
- Reference target(this, node->proxy());
- Load(val);
- target.SetValue(NOT_CONST_INIT);
- // The reference is removed from the stack (preserving TOS) when
- // it goes out of scope.
- }
- // Get rid of the assigned value (declarations are statements).
- frame_->Drop();
- }
-}
-
-
-void CodeGenerator::VisitExpressionStatement(ExpressionStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ ExpressionStatement");
- CodeForStatementPosition(node);
- Expression* expression = node->expression();
- expression->MarkAsStatement();
- Load(expression);
- // Remove the lingering expression result from the top of stack.
- frame_->Drop();
-}
-
-
-void CodeGenerator::VisitEmptyStatement(EmptyStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "// EmptyStatement");
- CodeForStatementPosition(node);
- // nothing to do
-}
-
-
-void CodeGenerator::VisitIfStatement(IfStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ IfStatement");
- // Generate different code depending on which parts of the if statement
- // are present or not.
- bool has_then_stm = node->HasThenStatement();
- bool has_else_stm = node->HasElseStatement();
-
- CodeForStatementPosition(node);
- JumpTarget exit;
- if (has_then_stm && has_else_stm) {
- JumpTarget then;
- JumpTarget else_;
- ControlDestination dest(&then, &else_, true);
- LoadCondition(node->condition(), &dest, true);
-
- if (dest.false_was_fall_through()) {
- // The else target was bound, so we compile the else part first.
- Visit(node->else_statement());
-
- // We may have dangling jumps to the then part.
- if (then.is_linked()) {
- if (has_valid_frame()) exit.Jump();
- then.Bind();
- Visit(node->then_statement());
- }
- } else {
- // The then target was bound, so we compile the then part first.
- Visit(node->then_statement());
-
- if (else_.is_linked()) {
- if (has_valid_frame()) exit.Jump();
- else_.Bind();
- Visit(node->else_statement());
- }
- }
-
- } else if (has_then_stm) {
- ASSERT(!has_else_stm);
- JumpTarget then;
- ControlDestination dest(&then, &exit, true);
- LoadCondition(node->condition(), &dest, true);
-
- if (dest.false_was_fall_through()) {
- // The exit label was bound. We may have dangling jumps to the
- // then part.
- if (then.is_linked()) {
- exit.Unuse();
- exit.Jump();
- then.Bind();
- Visit(node->then_statement());
- }
- } else {
- // The then label was bound.
- Visit(node->then_statement());
- }
-
- } else if (has_else_stm) {
- ASSERT(!has_then_stm);
- JumpTarget else_;
- ControlDestination dest(&exit, &else_, false);
- LoadCondition(node->condition(), &dest, true);
-
- if (dest.true_was_fall_through()) {
- // The exit label was bound. We may have dangling jumps to the
- // else part.
- if (else_.is_linked()) {
- exit.Unuse();
- exit.Jump();
- else_.Bind();
- Visit(node->else_statement());
- }
- } else {
- // The else label was bound.
- Visit(node->else_statement());
- }
-
- } else {
- ASSERT(!has_then_stm && !has_else_stm);
- // We only care about the condition's side effects (not its value
- // or control flow effect). LoadCondition is called without
- // forcing control flow.
- ControlDestination dest(&exit, &exit, true);
- LoadCondition(node->condition(), &dest, false);
- if (!dest.is_used()) {
- // We got a value on the frame rather than (or in addition to)
- // control flow.
- frame_->Drop();
- }
- }
-
- if (exit.is_linked()) {
- exit.Bind();
- }
-}
-
-
-void CodeGenerator::VisitContinueStatement(ContinueStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ ContinueStatement");
- CodeForStatementPosition(node);
- node->target()->continue_target()->Jump();
-}
-
-
-void CodeGenerator::VisitBreakStatement(BreakStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ BreakStatement");
- CodeForStatementPosition(node);
- node->target()->break_target()->Jump();
-}
-
-
-void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ ReturnStatement");
-
- CodeForStatementPosition(node);
- Load(node->expression());
- Result return_value = frame_->Pop();
- masm()->positions_recorder()->WriteRecordedPositions();
- if (function_return_is_shadowed_) {
- function_return_.Jump(&return_value);
- } else {
- frame_->PrepareForReturn();
- if (function_return_.is_bound()) {
- // If the function return label is already bound we reuse the
- // code by jumping to the return site.
- function_return_.Jump(&return_value);
- } else {
- function_return_.Bind(&return_value);
- GenerateReturnSequence(&return_value);
- }
- }
-}
-
-
-void CodeGenerator::GenerateReturnSequence(Result* return_value) {
- // The return value is a live (but not currently reference counted)
- // reference to eax. This is safe because the current frame does not
- // contain a reference to eax (it is prepared for the return by spilling
- // all registers).
- if (FLAG_trace) {
- frame_->Push(return_value);
- *return_value = frame_->CallRuntime(Runtime::kTraceExit, 1);
- }
- return_value->ToRegister(eax);
-
- // Add a label for checking the size of the code used for returning.
-#ifdef DEBUG
- Label check_exit_codesize;
- masm_->bind(&check_exit_codesize);
-#endif
-
- // Leave the frame and return popping the arguments and the
- // receiver.
- frame_->Exit();
- int arguments_bytes = (scope()->num_parameters() + 1) * kPointerSize;
- __ Ret(arguments_bytes, ecx);
- DeleteFrame();
-
-#ifdef ENABLE_DEBUGGER_SUPPORT
- // Check that the size of the code used for returning is large enough
- // for the debugger's requirements.
- ASSERT(Assembler::kJSReturnSequenceLength <=
- masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
-#endif
-}
-
-
-void CodeGenerator::VisitWithEnterStatement(WithEnterStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ WithEnterStatement");
- CodeForStatementPosition(node);
- Load(node->expression());
- Result context;
- if (node->is_catch_block()) {
- context = frame_->CallRuntime(Runtime::kPushCatchContext, 1);
- } else {
- context = frame_->CallRuntime(Runtime::kPushContext, 1);
- }
-
- // Update context local.
- frame_->SaveContextRegister();
-
- // Verify that the runtime call result and esi agree.
- if (FLAG_debug_code) {
- __ cmp(context.reg(), Operand(esi));
- __ Assert(equal, "Runtime::NewContext should end up in esi");
- }
-}
-
-
-void CodeGenerator::VisitWithExitStatement(WithExitStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ WithExitStatement");
- CodeForStatementPosition(node);
- // Pop context.
- __ mov(esi, ContextOperand(esi, Context::PREVIOUS_INDEX));
- // Update context local.
- frame_->SaveContextRegister();
-}
-
-
-void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ SwitchStatement");
- CodeForStatementPosition(node);
- node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
-
- // Compile the switch value.
- Load(node->tag());
-
- ZoneList<CaseClause*>* cases = node->cases();
- int length = cases->length();
- CaseClause* default_clause = NULL;
-
- JumpTarget next_test;
- // Compile the case label expressions and comparisons. Exit early
- // if a comparison is unconditionally true. The target next_test is
- // bound before the loop in order to indicate control flow to the
- // first comparison.
- next_test.Bind();
- for (int i = 0; i < length && !next_test.is_unused(); i++) {
- CaseClause* clause = cases->at(i);
- // The default is not a test, but remember it for later.
- if (clause->is_default()) {
- default_clause = clause;
- continue;
- }
-
- Comment cmnt(masm_, "[ Case comparison");
- // We recycle the same target next_test for each test. Bind it if
- // the previous test has not done so and then unuse it for the
- // loop.
- if (next_test.is_linked()) {
- next_test.Bind();
- }
- next_test.Unuse();
-
- // Duplicate the switch value.
- frame_->Dup();
-
- // Compile the label expression.
- Load(clause->label());
-
- // Compare and branch to the body if true or the next test if
- // false. Prefer the next test as a fall through.
- ControlDestination dest(clause->body_target(), &next_test, false);
- Comparison(node, equal, true, &dest);
-
- // If the comparison fell through to the true target, jump to the
- // actual body.
- if (dest.true_was_fall_through()) {
- clause->body_target()->Unuse();
- clause->body_target()->Jump();
- }
- }
-
- // If there was control flow to a next test from the last one
- // compiled, compile a jump to the default or break target.
- if (!next_test.is_unused()) {
- if (next_test.is_linked()) {
- next_test.Bind();
- }
- // Drop the switch value.
- frame_->Drop();
- if (default_clause != NULL) {
- default_clause->body_target()->Jump();
- } else {
- node->break_target()->Jump();
- }
- }
-
- // The last instruction emitted was a jump, either to the default
- // clause or the break target, or else to a case body from the loop
- // that compiles the tests.
- ASSERT(!has_valid_frame());
- // Compile case bodies as needed.
- for (int i = 0; i < length; i++) {
- CaseClause* clause = cases->at(i);
-
- // There are two ways to reach the body: from the corresponding
- // test or as the fall through of the previous body.
- if (clause->body_target()->is_linked() || has_valid_frame()) {
- if (clause->body_target()->is_linked()) {
- if (has_valid_frame()) {
- // If we have both a jump to the test and a fall through, put
- // a jump on the fall through path to avoid the dropping of
- // the switch value on the test path. The exception is the
- // default which has already had the switch value dropped.
- if (clause->is_default()) {
- clause->body_target()->Bind();
- } else {
- JumpTarget body;
- body.Jump();
- clause->body_target()->Bind();
- frame_->Drop();
- body.Bind();
- }
- } else {
- // No fall through to worry about.
- clause->body_target()->Bind();
- if (!clause->is_default()) {
- frame_->Drop();
- }
- }
- } else {
- // Otherwise, we have only fall through.
- ASSERT(has_valid_frame());
- }
-
- // We are now prepared to compile the body.
- Comment cmnt(masm_, "[ Case body");
- VisitStatements(clause->statements());
- }
- clause->body_target()->Unuse();
- }
-
- // We may not have a valid frame here so bind the break target only
- // if needed.
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- node->break_target()->Unuse();
-}
-
-
-void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ DoWhileStatement");
- CodeForStatementPosition(node);
- node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
- JumpTarget body(JumpTarget::BIDIRECTIONAL);
- IncrementLoopNesting();
-
- ConditionAnalysis info = AnalyzeCondition(node->cond());
- // Label the top of the loop for the backward jump if necessary.
- switch (info) {
- case ALWAYS_TRUE:
- // Use the continue target.
- node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
- node->continue_target()->Bind();
- break;
- case ALWAYS_FALSE:
- // No need to label it.
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
- break;
- case DONT_KNOW:
- // Continue is the test, so use the backward body target.
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
- body.Bind();
- break;
- }
-
- CheckStack(); // TODO(1222600): ignore if body contains calls.
- Visit(node->body());
-
- // Compile the test.
- switch (info) {
- case ALWAYS_TRUE:
- // If control flow can fall off the end of the body, jump back
- // to the top and bind the break target at the exit.
- if (has_valid_frame()) {
- node->continue_target()->Jump();
- }
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- break;
- case ALWAYS_FALSE:
- // We may have had continues or breaks in the body.
- if (node->continue_target()->is_linked()) {
- node->continue_target()->Bind();
- }
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- break;
- case DONT_KNOW:
- // We have to compile the test expression if it can be reached by
- // control flow falling out of the body or via continue.
- if (node->continue_target()->is_linked()) {
- node->continue_target()->Bind();
- }
- if (has_valid_frame()) {
- Comment cmnt(masm_, "[ DoWhileCondition");
- CodeForDoWhileConditionPosition(node);
- ControlDestination dest(&body, node->break_target(), false);
- LoadCondition(node->cond(), &dest, true);
- }
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- break;
- }
-
- DecrementLoopNesting();
- node->continue_target()->Unuse();
- node->break_target()->Unuse();
-}
-
-
-void CodeGenerator::VisitWhileStatement(WhileStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ WhileStatement");
- CodeForStatementPosition(node);
-
- // If the condition is always false and has no side effects, we do not
- // need to compile anything.
- ConditionAnalysis info = AnalyzeCondition(node->cond());
- if (info == ALWAYS_FALSE) return;
-
- // Do not duplicate conditions that may have function literal
- // subexpressions. This can cause us to compile the function literal
- // twice.
- bool test_at_bottom = !node->may_have_function_literal();
- node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
- IncrementLoopNesting();
- JumpTarget body;
- if (test_at_bottom) {
- body.set_direction(JumpTarget::BIDIRECTIONAL);
- }
-
- // Based on the condition analysis, compile the test as necessary.
- switch (info) {
- case ALWAYS_TRUE:
- // We will not compile the test expression. Label the top of the
- // loop with the continue target.
- node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
- node->continue_target()->Bind();
- break;
- case DONT_KNOW: {
- if (test_at_bottom) {
- // Continue is the test at the bottom, no need to label the test
- // at the top. The body is a backward target.
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
- } else {
- // Label the test at the top as the continue target. The body
- // is a forward-only target.
- node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
- node->continue_target()->Bind();
- }
- // Compile the test with the body as the true target and preferred
- // fall-through and with the break target as the false target.
- ControlDestination dest(&body, node->break_target(), true);
- LoadCondition(node->cond(), &dest, true);
-
- if (dest.false_was_fall_through()) {
- // If we got the break target as fall-through, the test may have
- // been unconditionally false (if there are no jumps to the
- // body).
- if (!body.is_linked()) {
- DecrementLoopNesting();
- return;
- }
-
- // Otherwise, jump around the body on the fall through and then
- // bind the body target.
- node->break_target()->Unuse();
- node->break_target()->Jump();
- body.Bind();
- }
- break;
- }
- case ALWAYS_FALSE:
- UNREACHABLE();
- break;
- }
-
- CheckStack(); // TODO(1222600): ignore if body contains calls.
- Visit(node->body());
-
- // Based on the condition analysis, compile the backward jump as
- // necessary.
- switch (info) {
- case ALWAYS_TRUE:
- // The loop body has been labeled with the continue target.
- if (has_valid_frame()) {
- node->continue_target()->Jump();
- }
- break;
- case DONT_KNOW:
- if (test_at_bottom) {
- // If we have chosen to recompile the test at the bottom,
- // then it is the continue target.
- if (node->continue_target()->is_linked()) {
- node->continue_target()->Bind();
- }
- if (has_valid_frame()) {
- // The break target is the fall-through (body is a backward
- // jump from here and thus an invalid fall-through).
- ControlDestination dest(&body, node->break_target(), false);
- LoadCondition(node->cond(), &dest, true);
- }
- } else {
- // If we have chosen not to recompile the test at the bottom,
- // jump back to the one at the top.
- if (has_valid_frame()) {
- node->continue_target()->Jump();
- }
- }
- break;
- case ALWAYS_FALSE:
- UNREACHABLE();
- break;
- }
-
- // The break target may be already bound (by the condition), or there
- // may not be a valid frame. Bind it only if needed.
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- DecrementLoopNesting();
-}
-
-
-void CodeGenerator::SetTypeForStackSlot(Slot* slot, TypeInfo info) {
- ASSERT(slot->type() == Slot::LOCAL || slot->type() == Slot::PARAMETER);
- if (slot->type() == Slot::LOCAL) {
- frame_->SetTypeForLocalAt(slot->index(), info);
- } else {
- frame_->SetTypeForParamAt(slot->index(), info);
- }
- if (FLAG_debug_code && info.IsSmi()) {
- if (slot->type() == Slot::LOCAL) {
- frame_->PushLocalAt(slot->index());
- } else {
- frame_->PushParameterAt(slot->index());
- }
- Result var = frame_->Pop();
- var.ToRegister();
- __ AbortIfNotSmi(var.reg());
- }
-}
-
-
-void CodeGenerator::VisitForStatement(ForStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ ForStatement");
- CodeForStatementPosition(node);
-
- // Compile the init expression if present.
- if (node->init() != NULL) {
- Visit(node->init());
- }
-
- // If the condition is always false and has no side effects, we do not
- // need to compile anything else.
- ConditionAnalysis info = AnalyzeCondition(node->cond());
- if (info == ALWAYS_FALSE) return;
-
- // Do not duplicate conditions that may have function literal
- // subexpressions. This can cause us to compile the function literal
- // twice.
- bool test_at_bottom = !node->may_have_function_literal();
- node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
- IncrementLoopNesting();
-
- // Target for backward edge if no test at the bottom, otherwise
- // unused.
- JumpTarget loop(JumpTarget::BIDIRECTIONAL);
-
- // Target for backward edge if there is a test at the bottom,
- // otherwise used as target for test at the top.
- JumpTarget body;
- if (test_at_bottom) {
- body.set_direction(JumpTarget::BIDIRECTIONAL);
- }
-
- // Based on the condition analysis, compile the test as necessary.
- switch (info) {
- case ALWAYS_TRUE:
- // We will not compile the test expression. Label the top of the
- // loop.
- if (node->next() == NULL) {
- // Use the continue target if there is no update expression.
- node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
- node->continue_target()->Bind();
- } else {
- // Otherwise use the backward loop target.
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
- loop.Bind();
- }
- break;
- case DONT_KNOW: {
- if (test_at_bottom) {
- // Continue is either the update expression or the test at the
- // bottom, no need to label the test at the top.
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
- } else if (node->next() == NULL) {
- // We are not recompiling the test at the bottom and there is no
- // update expression.
- node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
- node->continue_target()->Bind();
- } else {
- // We are not recompiling the test at the bottom and there is an
- // update expression.
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
- loop.Bind();
- }
-
- // Compile the test with the body as the true target and preferred
- // fall-through and with the break target as the false target.
- ControlDestination dest(&body, node->break_target(), true);
- LoadCondition(node->cond(), &dest, true);
-
- if (dest.false_was_fall_through()) {
- // If we got the break target as fall-through, the test may have
- // been unconditionally false (if there are no jumps to the
- // body).
- if (!body.is_linked()) {
- DecrementLoopNesting();
- return;
- }
-
- // Otherwise, jump around the body on the fall through and then
- // bind the body target.
- node->break_target()->Unuse();
- node->break_target()->Jump();
- body.Bind();
- }
- break;
- }
- case ALWAYS_FALSE:
- UNREACHABLE();
- break;
- }
-
- CheckStack(); // TODO(1222600): ignore if body contains calls.
-
- // We know that the loop index is a smi if it is not modified in the
- // loop body and it is checked against a constant limit in the loop
- // condition. In this case, we reset the static type information of the
- // loop index to smi before compiling the body, the update expression, and
- // the bottom check of the loop condition.
- if (node->is_fast_smi_loop()) {
- // Set number type of the loop variable to smi.
- SetTypeForStackSlot(node->loop_variable()->AsSlot(), TypeInfo::Smi());
- }
-
- Visit(node->body());
-
- // If there is an update expression, compile it if necessary.
- if (node->next() != NULL) {
- if (node->continue_target()->is_linked()) {
- node->continue_target()->Bind();
- }
-
- // Control can reach the update by falling out of the body or by a
- // continue.
- if (has_valid_frame()) {
- // Record the source position of the statement as this code which
- // is after the code for the body actually belongs to the loop
- // statement and not the body.
- CodeForStatementPosition(node);
- Visit(node->next());
- }
- }
-
- // Set the type of the loop variable to smi before compiling the test
- // expression if we are in a fast smi loop condition.
- if (node->is_fast_smi_loop() && has_valid_frame()) {
- // Set number type of the loop variable to smi.
- SetTypeForStackSlot(node->loop_variable()->AsSlot(), TypeInfo::Smi());
- }
-
- // Based on the condition analysis, compile the backward jump as
- // necessary.
- switch (info) {
- case ALWAYS_TRUE:
- if (has_valid_frame()) {
- if (node->next() == NULL) {
- node->continue_target()->Jump();
- } else {
- loop.Jump();
- }
- }
- break;
- case DONT_KNOW:
- if (test_at_bottom) {
- if (node->continue_target()->is_linked()) {
- // We can have dangling jumps to the continue target if there
- // was no update expression.
- node->continue_target()->Bind();
- }
- // Control can reach the test at the bottom by falling out of
- // the body, by a continue in the body, or from the update
- // expression.
- if (has_valid_frame()) {
- // The break target is the fall-through (body is a backward
- // jump from here).
- ControlDestination dest(&body, node->break_target(), false);
- LoadCondition(node->cond(), &dest, true);
- }
- } else {
- // Otherwise, jump back to the test at the top.
- if (has_valid_frame()) {
- if (node->next() == NULL) {
- node->continue_target()->Jump();
- } else {
- loop.Jump();
- }
- }
- }
- break;
- case ALWAYS_FALSE:
- UNREACHABLE();
- break;
- }
-
- // The break target may be already bound (by the condition), or there
- // may not be a valid frame. Bind it only if needed.
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- DecrementLoopNesting();
-}
-
-
-void CodeGenerator::VisitForInStatement(ForInStatement* node) {
- ASSERT(!in_spilled_code());
- VirtualFrame::SpilledScope spilled_scope;
- Comment cmnt(masm_, "[ ForInStatement");
- CodeForStatementPosition(node);
-
- JumpTarget primitive;
- JumpTarget jsobject;
- JumpTarget fixed_array;
- JumpTarget entry(JumpTarget::BIDIRECTIONAL);
- JumpTarget end_del_check;
- JumpTarget exit;
-
- // Get the object to enumerate over (converted to JSObject).
- LoadAndSpill(node->enumerable());
-
- // Both SpiderMonkey and kjs ignore null and undefined in contrast
- // to the specification. 12.6.4 mandates a call to ToObject.
- frame_->EmitPop(eax);
-
- // eax: value to be iterated over
- __ cmp(eax, FACTORY->undefined_value());
- exit.Branch(equal);
- __ cmp(eax, FACTORY->null_value());
- exit.Branch(equal);
-
- // Stack layout in body:
- // [iteration counter (smi)] <- slot 0
- // [length of array] <- slot 1
- // [FixedArray] <- slot 2
- // [Map or 0] <- slot 3
- // [Object] <- slot 4
-
- // Check if enumerable is already a JSObject
- // eax: value to be iterated over
- __ test(eax, Immediate(kSmiTagMask));
- primitive.Branch(zero);
- __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx);
- jsobject.Branch(above_equal);
-
- primitive.Bind();
- frame_->EmitPush(eax);
- frame_->InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION, 1);
- // function call returns the value in eax, which is where we want it below
-
- jsobject.Bind();
- // Get the set of properties (as a FixedArray or Map).
- // eax: value to be iterated over
- frame_->EmitPush(eax); // Push the object being iterated over.
-
- // Check cache validity in generated code. This is a fast case for
- // the JSObject::IsSimpleEnum cache validity checks. If we cannot
- // guarantee cache validity, call the runtime system to check cache
- // validity or get the property names in a fixed array.
- JumpTarget call_runtime;
- JumpTarget loop(JumpTarget::BIDIRECTIONAL);
- JumpTarget check_prototype;
- JumpTarget use_cache;
- __ mov(ecx, eax);
- loop.Bind();
- // Check that there are no elements.
- __ mov(edx, FieldOperand(ecx, JSObject::kElementsOffset));
- __ cmp(Operand(edx), Immediate(FACTORY->empty_fixed_array()));
- call_runtime.Branch(not_equal);
- // Check that instance descriptors are not empty so that we can
- // check for an enum cache. Leave the map in ebx for the subsequent
- // prototype load.
- __ mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset));
- __ mov(edx, FieldOperand(ebx, Map::kInstanceDescriptorsOffset));
- __ cmp(Operand(edx), Immediate(FACTORY->empty_descriptor_array()));
- call_runtime.Branch(equal);
- // Check that there in an enum cache in the non-empty instance
- // descriptors. This is the case if the next enumeration index
- // field does not contain a smi.
- __ mov(edx, FieldOperand(edx, DescriptorArray::kEnumerationIndexOffset));
- __ test(edx, Immediate(kSmiTagMask));
- call_runtime.Branch(zero);
- // For all objects but the receiver, check that the cache is empty.
- __ cmp(ecx, Operand(eax));
- check_prototype.Branch(equal);
- __ mov(edx, FieldOperand(edx, DescriptorArray::kEnumCacheBridgeCacheOffset));
- __ cmp(Operand(edx), Immediate(FACTORY->empty_fixed_array()));
- call_runtime.Branch(not_equal);
- check_prototype.Bind();
- // Load the prototype from the map and loop if non-null.
- __ mov(ecx, FieldOperand(ebx, Map::kPrototypeOffset));
- __ cmp(Operand(ecx), Immediate(FACTORY->null_value()));
- loop.Branch(not_equal);
- // The enum cache is valid. Load the map of the object being
- // iterated over and use the cache for the iteration.
- __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset));
- use_cache.Jump();
-
- call_runtime.Bind();
- // Call the runtime to get the property names for the object.
- frame_->EmitPush(eax); // push the Object (slot 4) for the runtime call
- frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1);
-
- // If we got a map from the runtime call, we can do a fast
- // modification check. Otherwise, we got a fixed array, and we have
- // to do a slow check.
- // eax: map or fixed array (result from call to
- // Runtime::kGetPropertyNamesFast)
- __ mov(edx, Operand(eax));
- __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
- __ cmp(ecx, FACTORY->meta_map());
- fixed_array.Branch(not_equal);
-
- use_cache.Bind();
- // Get enum cache
- // eax: map (either the result from a call to
- // Runtime::kGetPropertyNamesFast or has been fetched directly from
- // the object)
- __ mov(ecx, Operand(eax));
-
- __ mov(ecx, FieldOperand(ecx, Map::kInstanceDescriptorsOffset));
- // Get the bridge array held in the enumeration index field.
- __ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumerationIndexOffset));
- // Get the cache from the bridge array.
- __ mov(edx, FieldOperand(ecx, DescriptorArray::kEnumCacheBridgeCacheOffset));
-
- frame_->EmitPush(eax); // <- slot 3
- frame_->EmitPush(edx); // <- slot 2
- __ mov(eax, FieldOperand(edx, FixedArray::kLengthOffset));
- frame_->EmitPush(eax); // <- slot 1
- frame_->EmitPush(Immediate(Smi::FromInt(0))); // <- slot 0
- entry.Jump();
-
- fixed_array.Bind();
- // eax: fixed array (result from call to Runtime::kGetPropertyNamesFast)
- frame_->EmitPush(Immediate(Smi::FromInt(0))); // <- slot 3
- frame_->EmitPush(eax); // <- slot 2
-
- // Push the length of the array and the initial index onto the stack.
- __ mov(eax, FieldOperand(eax, FixedArray::kLengthOffset));
- frame_->EmitPush(eax); // <- slot 1
- frame_->EmitPush(Immediate(Smi::FromInt(0))); // <- slot 0
-
- // Condition.
- entry.Bind();
- // Grab the current frame's height for the break and continue
- // targets only after all the state is pushed on the frame.
- node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
-
- __ mov(eax, frame_->ElementAt(0)); // load the current count
- __ cmp(eax, frame_->ElementAt(1)); // compare to the array length
- node->break_target()->Branch(above_equal);
-
- // Get the i'th entry of the array.
- __ mov(edx, frame_->ElementAt(2));
- __ mov(ebx, FixedArrayElementOperand(edx, eax));
-
- // Get the expected map from the stack or a zero map in the
- // permanent slow case eax: current iteration count ebx: i'th entry
- // of the enum cache
- __ mov(edx, frame_->ElementAt(3));
- // Check if the expected map still matches that of the enumerable.
- // If not, we have to filter the key.
- // eax: current iteration count
- // ebx: i'th entry of the enum cache
- // edx: expected map value
- __ mov(ecx, frame_->ElementAt(4));
- __ mov(ecx, FieldOperand(ecx, HeapObject::kMapOffset));
- __ cmp(ecx, Operand(edx));
- end_del_check.Branch(equal);
-
- // Convert the entry to a string (or null if it isn't a property anymore).
- frame_->EmitPush(frame_->ElementAt(4)); // push enumerable
- frame_->EmitPush(ebx); // push entry
- frame_->InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION, 2);
- __ mov(ebx, Operand(eax));
-
- // If the property has been removed while iterating, we just skip it.
- __ test(ebx, Operand(ebx));
- node->continue_target()->Branch(equal);
-
- end_del_check.Bind();
- // Store the entry in the 'each' expression and take another spin in the
- // loop. edx: i'th entry of the enum cache (or string there of)
- frame_->EmitPush(ebx);
- { Reference each(this, node->each());
- if (!each.is_illegal()) {
- if (each.size() > 0) {
- // Loading a reference may leave the frame in an unspilled state.
- frame_->SpillAll();
- // Get the value (under the reference on the stack) from memory.
- frame_->EmitPush(frame_->ElementAt(each.size()));
- each.SetValue(NOT_CONST_INIT);
- frame_->Drop(2);
- } else {
- // If the reference was to a slot we rely on the convenient property
- // that it doesn't matter whether a value (eg, ebx pushed above) is
- // right on top of or right underneath a zero-sized reference.
- each.SetValue(NOT_CONST_INIT);
- frame_->Drop();
- }
- }
- }
- // Unloading a reference may leave the frame in an unspilled state.
- frame_->SpillAll();
-
- // Body.
- CheckStack(); // TODO(1222600): ignore if body contains calls.
- VisitAndSpill(node->body());
-
- // Next. Reestablish a spilled frame in case we are coming here via
- // a continue in the body.
- node->continue_target()->Bind();
- frame_->SpillAll();
- frame_->EmitPop(eax);
- __ add(Operand(eax), Immediate(Smi::FromInt(1)));
- frame_->EmitPush(eax);
- entry.Jump();
-
- // Cleanup. No need to spill because VirtualFrame::Drop is safe for
- // any frame.
- node->break_target()->Bind();
- frame_->Drop(5);
-
- // Exit.
- exit.Bind();
-
- node->continue_target()->Unuse();
- node->break_target()->Unuse();
-}
-
-
-void CodeGenerator::VisitTryCatchStatement(TryCatchStatement* node) {
- ASSERT(!in_spilled_code());
- VirtualFrame::SpilledScope spilled_scope;
- Comment cmnt(masm_, "[ TryCatchStatement");
- CodeForStatementPosition(node);
-
- JumpTarget try_block;
- JumpTarget exit;
-
- try_block.Call();
- // --- Catch block ---
- frame_->EmitPush(eax);
-
- // Store the caught exception in the catch variable.
- Variable* catch_var = node->catch_var()->var();
- ASSERT(catch_var != NULL && catch_var->AsSlot() != NULL);
- StoreToSlot(catch_var->AsSlot(), NOT_CONST_INIT);
-
- // Remove the exception from the stack.
- frame_->Drop();
-
- VisitStatementsAndSpill(node->catch_block()->statements());
- if (has_valid_frame()) {
- exit.Jump();
- }
-
-
- // --- Try block ---
- try_block.Bind();
-
- frame_->PushTryHandler(TRY_CATCH_HANDLER);
- int handler_height = frame_->height();
-
- // Shadow the jump targets for all escapes from the try block, including
- // returns. During shadowing, the original target is hidden as the
- // ShadowTarget and operations on the original actually affect the
- // shadowing target.
- //
- // We should probably try to unify the escaping targets and the return
- // target.
- int nof_escapes = node->escaping_targets()->length();
- List<ShadowTarget*> shadows(1 + nof_escapes);
-
- // Add the shadow target for the function return.
- static const int kReturnShadowIndex = 0;
- shadows.Add(new ShadowTarget(&function_return_));
- bool function_return_was_shadowed = function_return_is_shadowed_;
- function_return_is_shadowed_ = true;
- ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
-
- // Add the remaining shadow targets.
- for (int i = 0; i < nof_escapes; i++) {
- shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
- }
-
- // Generate code for the statements in the try block.
- VisitStatementsAndSpill(node->try_block()->statements());
-
- // Stop the introduced shadowing and count the number of required unlinks.
- // After shadowing stops, the original targets are unshadowed and the
- // ShadowTargets represent the formerly shadowing targets.
- bool has_unlinks = false;
- for (int i = 0; i < shadows.length(); i++) {
- shadows[i]->StopShadowing();
- has_unlinks = has_unlinks || shadows[i]->is_linked();
- }
- function_return_is_shadowed_ = function_return_was_shadowed;
-
- // Get an external reference to the handler address.
- ExternalReference handler_address(Isolate::k_handler_address,
- masm()->isolate());
-
- // Make sure that there's nothing left on the stack above the
- // handler structure.
- if (FLAG_debug_code) {
- __ mov(eax, Operand::StaticVariable(handler_address));
- __ cmp(esp, Operand(eax));
- __ Assert(equal, "stack pointer should point to top handler");
- }
-
- // If we can fall off the end of the try block, unlink from try chain.
- if (has_valid_frame()) {
- // The next handler address is on top of the frame. Unlink from
- // the handler list and drop the rest of this handler from the
- // frame.
- STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
- frame_->EmitPop(Operand::StaticVariable(handler_address));
- frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
- if (has_unlinks) {
- exit.Jump();
- }
- }
-
- // Generate unlink code for the (formerly) shadowing targets that
- // have been jumped to. Deallocate each shadow target.
- Result return_value;
- for (int i = 0; i < shadows.length(); i++) {
- if (shadows[i]->is_linked()) {
- // Unlink from try chain; be careful not to destroy the TOS if
- // there is one.
- if (i == kReturnShadowIndex) {
- shadows[i]->Bind(&return_value);
- return_value.ToRegister(eax);
- } else {
- shadows[i]->Bind();
- }
- // Because we can be jumping here (to spilled code) from
- // unspilled code, we need to reestablish a spilled frame at
- // this block.
- frame_->SpillAll();
-
- // Reload sp from the top handler, because some statements that we
- // break from (eg, for...in) may have left stuff on the stack.
- __ mov(esp, Operand::StaticVariable(handler_address));
- frame_->Forget(frame_->height() - handler_height);
-
- STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
- frame_->EmitPop(Operand::StaticVariable(handler_address));
- frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
-
- if (i == kReturnShadowIndex) {
- if (!function_return_is_shadowed_) frame_->PrepareForReturn();
- shadows[i]->other_target()->Jump(&return_value);
- } else {
- shadows[i]->other_target()->Jump();
- }
- }
- }
-
- exit.Bind();
-}
-
-
-void CodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* node) {
- ASSERT(!in_spilled_code());
- VirtualFrame::SpilledScope spilled_scope;
- Comment cmnt(masm_, "[ TryFinallyStatement");
- CodeForStatementPosition(node);
-
- // State: Used to keep track of reason for entering the finally
- // block. Should probably be extended to hold information for
- // break/continue from within the try block.
- enum { FALLING, THROWING, JUMPING };
-
- JumpTarget try_block;
- JumpTarget finally_block;
-
- try_block.Call();
-
- frame_->EmitPush(eax);
- // In case of thrown exceptions, this is where we continue.
- __ Set(ecx, Immediate(Smi::FromInt(THROWING)));
- finally_block.Jump();
-
- // --- Try block ---
- try_block.Bind();
-
- frame_->PushTryHandler(TRY_FINALLY_HANDLER);
- int handler_height = frame_->height();
-
- // Shadow the jump targets for all escapes from the try block, including
- // returns. During shadowing, the original target is hidden as the
- // ShadowTarget and operations on the original actually affect the
- // shadowing target.
- //
- // We should probably try to unify the escaping targets and the return
- // target.
- int nof_escapes = node->escaping_targets()->length();
- List<ShadowTarget*> shadows(1 + nof_escapes);
-
- // Add the shadow target for the function return.
- static const int kReturnShadowIndex = 0;
- shadows.Add(new ShadowTarget(&function_return_));
- bool function_return_was_shadowed = function_return_is_shadowed_;
- function_return_is_shadowed_ = true;
- ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
-
- // Add the remaining shadow targets.
- for (int i = 0; i < nof_escapes; i++) {
- shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
- }
-
- // Generate code for the statements in the try block.
- VisitStatementsAndSpill(node->try_block()->statements());
-
- // Stop the introduced shadowing and count the number of required unlinks.
- // After shadowing stops, the original targets are unshadowed and the
- // ShadowTargets represent the formerly shadowing targets.
- int nof_unlinks = 0;
- for (int i = 0; i < shadows.length(); i++) {
- shadows[i]->StopShadowing();
- if (shadows[i]->is_linked()) nof_unlinks++;
- }
- function_return_is_shadowed_ = function_return_was_shadowed;
-
- // Get an external reference to the handler address.
- ExternalReference handler_address(Isolate::k_handler_address,
- masm()->isolate());
-
- // If we can fall off the end of the try block, unlink from the try
- // chain and set the state on the frame to FALLING.
- if (has_valid_frame()) {
- // The next handler address is on top of the frame.
- STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
- frame_->EmitPop(Operand::StaticVariable(handler_address));
- frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
-
- // Fake a top of stack value (unneeded when FALLING) and set the
- // state in ecx, then jump around the unlink blocks if any.
- frame_->EmitPush(Immediate(FACTORY->undefined_value()));
- __ Set(ecx, Immediate(Smi::FromInt(FALLING)));
- if (nof_unlinks > 0) {
- finally_block.Jump();
- }
- }
-
- // Generate code to unlink and set the state for the (formerly)
- // shadowing targets that have been jumped to.
- for (int i = 0; i < shadows.length(); i++) {
- if (shadows[i]->is_linked()) {
- // If we have come from the shadowed return, the return value is
- // on the virtual frame. We must preserve it until it is
- // pushed.
- if (i == kReturnShadowIndex) {
- Result return_value;
- shadows[i]->Bind(&return_value);
- return_value.ToRegister(eax);
- } else {
- shadows[i]->Bind();
- }
- // Because we can be jumping here (to spilled code) from
- // unspilled code, we need to reestablish a spilled frame at
- // this block.
- frame_->SpillAll();
-
- // Reload sp from the top handler, because some statements that
- // we break from (eg, for...in) may have left stuff on the
- // stack.
- __ mov(esp, Operand::StaticVariable(handler_address));
- frame_->Forget(frame_->height() - handler_height);
-
- // Unlink this handler and drop it from the frame.
- STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
- frame_->EmitPop(Operand::StaticVariable(handler_address));
- frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
-
- if (i == kReturnShadowIndex) {
- // If this target shadowed the function return, materialize
- // the return value on the stack.
- frame_->EmitPush(eax);
- } else {
- // Fake TOS for targets that shadowed breaks and continues.
- frame_->EmitPush(Immediate(FACTORY->undefined_value()));
- }
- __ Set(ecx, Immediate(Smi::FromInt(JUMPING + i)));
- if (--nof_unlinks > 0) {
- // If this is not the last unlink block, jump around the next.
- finally_block.Jump();
- }
- }
- }
-
- // --- Finally block ---
- finally_block.Bind();
-
- // Push the state on the stack.
- frame_->EmitPush(ecx);
-
- // We keep two elements on the stack - the (possibly faked) result
- // and the state - while evaluating the finally block.
- //
- // Generate code for the statements in the finally block.
- VisitStatementsAndSpill(node->finally_block()->statements());
-
- if (has_valid_frame()) {
- // Restore state and return value or faked TOS.
- frame_->EmitPop(ecx);
- frame_->EmitPop(eax);
- }
-
- // Generate code to jump to the right destination for all used
- // formerly shadowing targets. Deallocate each shadow target.
- for (int i = 0; i < shadows.length(); i++) {
- if (has_valid_frame() && shadows[i]->is_bound()) {
- BreakTarget* original = shadows[i]->other_target();
- __ cmp(Operand(ecx), Immediate(Smi::FromInt(JUMPING + i)));
- if (i == kReturnShadowIndex) {
- // The return value is (already) in eax.
- Result return_value = allocator_->Allocate(eax);
- ASSERT(return_value.is_valid());
- if (function_return_is_shadowed_) {
- original->Branch(equal, &return_value);
- } else {
- // Branch around the preparation for return which may emit
- // code.
- JumpTarget skip;
- skip.Branch(not_equal);
- frame_->PrepareForReturn();
- original->Jump(&return_value);
- skip.Bind();
- }
- } else {
- original->Branch(equal);
- }
- }
- }
-
- if (has_valid_frame()) {
- // Check if we need to rethrow the exception.
- JumpTarget exit;
- __ cmp(Operand(ecx), Immediate(Smi::FromInt(THROWING)));
- exit.Branch(not_equal);
-
- // Rethrow exception.
- frame_->EmitPush(eax); // undo pop from above
- frame_->CallRuntime(Runtime::kReThrow, 1);
-
- // Done.
- exit.Bind();
- }
-}
-
-
-void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ DebuggerStatement");
- CodeForStatementPosition(node);
-#ifdef ENABLE_DEBUGGER_SUPPORT
- // Spill everything, even constants, to the frame.
- frame_->SpillAll();
-
- frame_->DebugBreak();
- // Ignore the return value.
-#endif
-}
-
-
-Result CodeGenerator::InstantiateFunction(
- Handle<SharedFunctionInfo> function_info,
- bool pretenure) {
- // The inevitable call will sync frame elements to memory anyway, so
- // we do it eagerly to allow us to push the arguments directly into
- // place.
- frame()->SyncRange(0, frame()->element_count() - 1);
-
- // Use the fast case closure allocation code that allocates in new
- // space for nested functions that don't need literals cloning.
- if (!pretenure &&
- scope()->is_function_scope() &&
- function_info->num_literals() == 0) {
- FastNewClosureStub stub(
- function_info->strict_mode() ? kStrictMode : kNonStrictMode);
- frame()->EmitPush(Immediate(function_info));
- return frame()->CallStub(&stub, 1);
- } else {
- // Call the runtime to instantiate the function based on the
- // shared function info.
- frame()->EmitPush(esi);
- frame()->EmitPush(Immediate(function_info));
- frame()->EmitPush(Immediate(pretenure
- ? FACTORY->true_value()
- : FACTORY->false_value()));
- return frame()->CallRuntime(Runtime::kNewClosure, 3);
- }
-}
-
-
-void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
- Comment cmnt(masm_, "[ FunctionLiteral");
- ASSERT(!in_safe_int32_mode());
- // Build the function info and instantiate it.
- Handle<SharedFunctionInfo> function_info =
- Compiler::BuildFunctionInfo(node, script());
- // Check for stack-overflow exception.
- if (function_info.is_null()) {
- SetStackOverflow();
- return;
- }
- Result result = InstantiateFunction(function_info, node->pretenure());
- frame()->Push(&result);
-}
-
-
-void CodeGenerator::VisitSharedFunctionInfoLiteral(
- SharedFunctionInfoLiteral* node) {
- ASSERT(!in_safe_int32_mode());
- Comment cmnt(masm_, "[ SharedFunctionInfoLiteral");
- Result result = InstantiateFunction(node->shared_function_info(), false);
- frame()->Push(&result);
-}
-
-
-void CodeGenerator::VisitConditional(Conditional* node) {
- Comment cmnt(masm_, "[ Conditional");
- ASSERT(!in_safe_int32_mode());
- JumpTarget then;
- JumpTarget else_;
- JumpTarget exit;
- ControlDestination dest(&then, &else_, true);
- LoadCondition(node->condition(), &dest, true);
-
- if (dest.false_was_fall_through()) {
- // The else target was bound, so we compile the else part first.
- Load(node->else_expression());
-
- if (then.is_linked()) {
- exit.Jump();
- then.Bind();
- Load(node->then_expression());
- }
- } else {
- // The then target was bound, so we compile the then part first.
- Load(node->then_expression());
-
- if (else_.is_linked()) {
- exit.Jump();
- else_.Bind();
- Load(node->else_expression());
- }
- }
-
- exit.Bind();
-}
-
-
-void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
- if (slot->type() == Slot::LOOKUP) {
- ASSERT(slot->var()->is_dynamic());
- JumpTarget slow;
- JumpTarget done;
- Result value;
-
- // Generate fast case for loading from slots that correspond to
- // local/global variables or arguments unless they are shadowed by
- // eval-introduced bindings.
- EmitDynamicLoadFromSlotFastCase(slot,
- typeof_state,
- &value,
- &slow,
- &done);
-
- slow.Bind();
- // A runtime call is inevitable. We eagerly sync frame elements
- // to memory so that we can push the arguments directly into place
- // on top of the frame.
- frame()->SyncRange(0, frame()->element_count() - 1);
- frame()->EmitPush(esi);
- frame()->EmitPush(Immediate(slot->var()->name()));
- if (typeof_state == INSIDE_TYPEOF) {
- value =
- frame()->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
- } else {
- value = frame()->CallRuntime(Runtime::kLoadContextSlot, 2);
- }
-
- done.Bind(&value);
- frame_->Push(&value);
-
- } else if (slot->var()->mode() == Variable::CONST) {
- // Const slots may contain 'the hole' value (the constant hasn't been
- // initialized yet) which needs to be converted into the 'undefined'
- // value.
- //
- // We currently spill the virtual frame because constants use the
- // potentially unsafe direct-frame access of SlotOperand.
- VirtualFrame::SpilledScope spilled_scope;
- Comment cmnt(masm_, "[ Load const");
- Label exit;
- __ mov(ecx, SlotOperand(slot, ecx));
- __ cmp(ecx, FACTORY->the_hole_value());
- __ j(not_equal, &exit);
- __ mov(ecx, FACTORY->undefined_value());
- __ bind(&exit);
- frame()->EmitPush(ecx);
-
- } else if (slot->type() == Slot::PARAMETER) {
- frame()->PushParameterAt(slot->index());
-
- } else if (slot->type() == Slot::LOCAL) {
- frame()->PushLocalAt(slot->index());
-
- } else {
- // The other remaining slot types (LOOKUP and GLOBAL) cannot reach
- // here.
- //
- // The use of SlotOperand below is safe for an unspilled frame
- // because it will always be a context slot.
- ASSERT(slot->type() == Slot::CONTEXT);
- Result temp = allocator()->Allocate();
- ASSERT(temp.is_valid());
- __ mov(temp.reg(), SlotOperand(slot, temp.reg()));
- frame()->Push(&temp);
- }
-}
-
-
-void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
- TypeofState state) {
- LoadFromSlot(slot, state);
-
- // Bail out quickly if we're not using lazy arguments allocation.
- if (ArgumentsMode() != LAZY_ARGUMENTS_ALLOCATION) return;
-
- // ... or if the slot isn't a non-parameter arguments slot.
- if (slot->type() == Slot::PARAMETER || !slot->is_arguments()) return;
-
- // If the loaded value is a constant, we know if the arguments
- // object has been lazily loaded yet.
- Result result = frame()->Pop();
- if (result.is_constant()) {
- if (result.handle()->IsArgumentsMarker()) {
- result = StoreArgumentsObject(false);
- }
- frame()->Push(&result);
- return;
- }
- ASSERT(result.is_register());
- // The loaded value is in a register. If it is the sentinel that
- // indicates that we haven't loaded the arguments object yet, we
- // need to do it now.
- JumpTarget exit;
- __ cmp(Operand(result.reg()), Immediate(FACTORY->arguments_marker()));
- frame()->Push(&result);
- exit.Branch(not_equal);
-
- result = StoreArgumentsObject(false);
- frame()->SetElementAt(0, &result);
- result.Unuse();
- exit.Bind();
- return;
-}
-
-
-Result CodeGenerator::LoadFromGlobalSlotCheckExtensions(
- Slot* slot,
- TypeofState typeof_state,
- JumpTarget* slow) {
- ASSERT(!in_safe_int32_mode());
- // Check that no extension objects have been created by calls to
- // eval from the current scope to the global scope.
- Register context = esi;
- Result tmp = allocator_->Allocate();
- ASSERT(tmp.is_valid()); // All non-reserved registers were available.
-
- Scope* s = scope();
- while (s != NULL) {
- if (s->num_heap_slots() > 0) {
- if (s->calls_eval()) {
- // Check that extension is NULL.
- __ cmp(ContextOperand(context, Context::EXTENSION_INDEX),
- Immediate(0));
- slow->Branch(not_equal, not_taken);
- }
- // Load next context in chain.
- __ mov(tmp.reg(), ContextOperand(context, Context::CLOSURE_INDEX));
- __ mov(tmp.reg(), FieldOperand(tmp.reg(), JSFunction::kContextOffset));
- context = tmp.reg();
- }
- // If no outer scope calls eval, we do not need to check more
- // context extensions. If we have reached an eval scope, we check
- // all extensions from this point.
- if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break;
- s = s->outer_scope();
- }
-
- if (s != NULL && s->is_eval_scope()) {
- // Loop up the context chain. There is no frame effect so it is
- // safe to use raw labels here.
- Label next, fast;
- if (!context.is(tmp.reg())) {
- __ mov(tmp.reg(), context);
- }
- __ bind(&next);
- // Terminate at global context.
- __ cmp(FieldOperand(tmp.reg(), HeapObject::kMapOffset),
- Immediate(FACTORY->global_context_map()));
- __ j(equal, &fast);
- // Check that extension is NULL.
- __ cmp(ContextOperand(tmp.reg(), Context::EXTENSION_INDEX), Immediate(0));
- slow->Branch(not_equal, not_taken);
- // Load next context in chain.
- __ mov(tmp.reg(), ContextOperand(tmp.reg(), Context::CLOSURE_INDEX));
- __ mov(tmp.reg(), FieldOperand(tmp.reg(), JSFunction::kContextOffset));
- __ jmp(&next);
- __ bind(&fast);
- }
- tmp.Unuse();
-
- // All extension objects were empty and it is safe to use a global
- // load IC call.
- // The register allocator prefers eax if it is free, so the code generator
- // will load the global object directly into eax, which is where the LoadIC
- // expects it.
- frame_->Spill(eax);
- LoadGlobal();
- frame_->Push(slot->var()->name());
- RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
- ? RelocInfo::CODE_TARGET
- : RelocInfo::CODE_TARGET_CONTEXT;
- Result answer = frame_->CallLoadIC(mode);
- // A test eax instruction following the call signals that the inobject
- // property case was inlined. Ensure that there is not a test eax
- // instruction here.
- __ nop();
- return answer;
-}
-
-
-void CodeGenerator::EmitDynamicLoadFromSlotFastCase(Slot* slot,
- TypeofState typeof_state,
- Result* result,
- JumpTarget* slow,
- JumpTarget* done) {
- // Generate fast-case code for variables that might be shadowed by
- // eval-introduced variables. Eval is used a lot without
- // introducing variables. In those cases, we do not want to
- // perform a runtime call for all variables in the scope
- // containing the eval.
- if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
- *result = LoadFromGlobalSlotCheckExtensions(slot, typeof_state, slow);
- done->Jump(result);
-
- } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
- Slot* potential_slot = slot->var()->local_if_not_shadowed()->AsSlot();
- Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite();
- if (potential_slot != NULL) {
- // Generate fast case for locals that rewrite to slots.
- // Allocate a fresh register to use as a temp in
- // ContextSlotOperandCheckExtensions and to hold the result
- // value.
- *result = allocator()->Allocate();
- ASSERT(result->is_valid());
- __ mov(result->reg(),
- ContextSlotOperandCheckExtensions(potential_slot, *result, slow));
- if (potential_slot->var()->mode() == Variable::CONST) {
- __ cmp(result->reg(), FACTORY->the_hole_value());
- done->Branch(not_equal, result);
- __ mov(result->reg(), FACTORY->undefined_value());
- }
- done->Jump(result);
- } else if (rewrite != NULL) {
- // Generate fast case for calls of an argument function.
- Property* property = rewrite->AsProperty();
- if (property != NULL) {
- VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
- Literal* key_literal = property->key()->AsLiteral();
- if (obj_proxy != NULL &&
- key_literal != NULL &&
- obj_proxy->IsArguments() &&
- key_literal->handle()->IsSmi()) {
- // Load arguments object if there are no eval-introduced
- // variables. Then load the argument from the arguments
- // object using keyed load.
- Result arguments = allocator()->Allocate();
- ASSERT(arguments.is_valid());
- __ mov(arguments.reg(),
- ContextSlotOperandCheckExtensions(obj_proxy->var()->AsSlot(),
- arguments,
- slow));
- frame_->Push(&arguments);
- frame_->Push(key_literal->handle());
- *result = EmitKeyedLoad();
- done->Jump(result);
- }
- }
- }
- }
-}
-
-
-void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) {
- if (slot->type() == Slot::LOOKUP) {
- ASSERT(slot->var()->is_dynamic());
-
- // For now, just do a runtime call. Since the call is inevitable,
- // we eagerly sync the virtual frame so we can directly push the
- // arguments into place.
- frame_->SyncRange(0, frame_->element_count() - 1);
-
- frame_->EmitPush(esi);
- frame_->EmitPush(Immediate(slot->var()->name()));
-
- Result value;
- if (init_state == CONST_INIT) {
- // Same as the case for a normal store, but ignores attribute
- // (e.g. READ_ONLY) of context slot so that we can initialize const
- // properties (introduced via eval("const foo = (some expr);")). Also,
- // uses the current function context instead of the top context.
- //
- // Note that we must declare the foo upon entry of eval(), via a
- // context slot declaration, but we cannot initialize it at the same
- // time, because the const declaration may be at the end of the eval
- // code (sigh...) and the const variable may have been used before
- // (where its value is 'undefined'). Thus, we can only do the
- // initialization when we actually encounter the expression and when
- // the expression operands are defined and valid, and thus we need the
- // split into 2 operations: declaration of the context slot followed
- // by initialization.
- value = frame_->CallRuntime(Runtime::kInitializeConstContextSlot, 3);
- } else {
- frame_->Push(Smi::FromInt(strict_mode_flag()));
- value = frame_->CallRuntime(Runtime::kStoreContextSlot, 4);
- }
- // Storing a variable must keep the (new) value on the expression
- // stack. This is necessary for compiling chained assignment
- // expressions.
- frame_->Push(&value);
-
- } else {
- ASSERT(!slot->var()->is_dynamic());
-
- JumpTarget exit;
- if (init_state == CONST_INIT) {
- ASSERT(slot->var()->mode() == Variable::CONST);
- // Only the first const initialization must be executed (the slot
- // still contains 'the hole' value). When the assignment is executed,
- // the code is identical to a normal store (see below).
- //
- // We spill the frame in the code below because the direct-frame
- // access of SlotOperand is potentially unsafe with an unspilled
- // frame.
- VirtualFrame::SpilledScope spilled_scope;
- Comment cmnt(masm_, "[ Init const");
- __ mov(ecx, SlotOperand(slot, ecx));
- __ cmp(ecx, FACTORY->the_hole_value());
- exit.Branch(not_equal);
- }
-
- // We must execute the store. Storing a variable must keep the (new)
- // value on the stack. This is necessary for compiling assignment
- // expressions.
- //
- // Note: We will reach here even with slot->var()->mode() ==
- // Variable::CONST because of const declarations which will initialize
- // consts to 'the hole' value and by doing so, end up calling this code.
- if (slot->type() == Slot::PARAMETER) {
- frame_->StoreToParameterAt(slot->index());
- } else if (slot->type() == Slot::LOCAL) {
- frame_->StoreToLocalAt(slot->index());
- } else {
- // The other slot types (LOOKUP and GLOBAL) cannot reach here.
- //
- // The use of SlotOperand below is safe for an unspilled frame
- // because the slot is a context slot.
- ASSERT(slot->type() == Slot::CONTEXT);
- frame_->Dup();
- Result value = frame_->Pop();
- value.ToRegister();
- Result start = allocator_->Allocate();
- ASSERT(start.is_valid());
- __ mov(SlotOperand(slot, start.reg()), value.reg());
- // RecordWrite may destroy the value registers.
- //
- // TODO(204): Avoid actually spilling when the value is not
- // needed (probably the common case).
- frame_->Spill(value.reg());
- int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
- Result temp = allocator_->Allocate();
- ASSERT(temp.is_valid());
- __ RecordWrite(start.reg(), offset, value.reg(), temp.reg());
- // The results start, value, and temp are unused by going out of
- // scope.
- }
-
- exit.Bind();
- }
-}
-
-
-void CodeGenerator::VisitSlot(Slot* slot) {
- Comment cmnt(masm_, "[ Slot");
- if (in_safe_int32_mode()) {
- if ((slot->type() == Slot::LOCAL && !slot->is_arguments())) {
- frame()->UntaggedPushLocalAt(slot->index());
- } else if (slot->type() == Slot::PARAMETER) {
- frame()->UntaggedPushParameterAt(slot->index());
- } else {
- UNREACHABLE();
- }
- } else {
- LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
- }
-}
-
-
-void CodeGenerator::VisitVariableProxy(VariableProxy* node) {
- Comment cmnt(masm_, "[ VariableProxy");
- Variable* var = node->var();
- Expression* expr = var->rewrite();
- if (expr != NULL) {
- Visit(expr);
- } else {
- ASSERT(var->is_global());
- ASSERT(!in_safe_int32_mode());
- Reference ref(this, node);
- ref.GetValue();
- }
-}
-
-
-void CodeGenerator::VisitLiteral(Literal* node) {
- Comment cmnt(masm_, "[ Literal");
- if (frame_->ConstantPoolOverflowed()) {
- Result temp = allocator_->Allocate();
- ASSERT(temp.is_valid());
- if (in_safe_int32_mode()) {
- temp.set_untagged_int32(true);
- }
- __ Set(temp.reg(), Immediate(node->handle()));
- frame_->Push(&temp);
- } else {
- if (in_safe_int32_mode()) {
- frame_->PushUntaggedElement(node->handle());
- } else {
- frame_->Push(node->handle());
- }
- }
-}
-
-
-void CodeGenerator::PushUnsafeSmi(Handle<Object> value) {
- ASSERT(value->IsSmi());
- int bits = reinterpret_cast<int>(*value);
- __ push(Immediate(bits ^ jit_cookie_));
- __ xor_(Operand(esp, 0), Immediate(jit_cookie_));
-}
-
-
-void CodeGenerator::StoreUnsafeSmiToLocal(int offset, Handle<Object> value) {
- ASSERT(value->IsSmi());
- int bits = reinterpret_cast<int>(*value);
- __ mov(Operand(ebp, offset), Immediate(bits ^ jit_cookie_));
- __ xor_(Operand(ebp, offset), Immediate(jit_cookie_));
-}
-
-
-void CodeGenerator::MoveUnsafeSmi(Register target, Handle<Object> value) {
- ASSERT(target.is_valid());
- ASSERT(value->IsSmi());
- int bits = reinterpret_cast<int>(*value);
- __ Set(target, Immediate(bits ^ jit_cookie_));
- __ xor_(target, jit_cookie_);
-}
-
-
-bool CodeGenerator::IsUnsafeSmi(Handle<Object> value) {
- if (!value->IsSmi()) return false;
- int int_value = Smi::cast(*value)->value();
- return !is_intn(int_value, kMaxSmiInlinedBits);
-}
-
-
-// Materialize the regexp literal 'node' in the literals array
-// 'literals' of the function. Leave the regexp boilerplate in
-// 'boilerplate'.
-class DeferredRegExpLiteral: public DeferredCode {
- public:
- DeferredRegExpLiteral(Register boilerplate,
- Register literals,
- RegExpLiteral* node)
- : boilerplate_(boilerplate), literals_(literals), node_(node) {
- set_comment("[ DeferredRegExpLiteral");
- }
-
- void Generate();
-
- private:
- Register boilerplate_;
- Register literals_;
- RegExpLiteral* node_;
-};
-
-
-void DeferredRegExpLiteral::Generate() {
- // Since the entry is undefined we call the runtime system to
- // compute the literal.
- // Literal array (0).
- __ push(literals_);
- // Literal index (1).
- __ push(Immediate(Smi::FromInt(node_->literal_index())));
- // RegExp pattern (2).
- __ push(Immediate(node_->pattern()));
- // RegExp flags (3).
- __ push(Immediate(node_->flags()));
- __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
- if (!boilerplate_.is(eax)) __ mov(boilerplate_, eax);
-}
-
-
-class DeferredAllocateInNewSpace: public DeferredCode {
- public:
- DeferredAllocateInNewSpace(int size,
- Register target,
- int registers_to_save = 0)
- : size_(size), target_(target), registers_to_save_(registers_to_save) {
- ASSERT(size >= kPointerSize && size <= HEAP->MaxObjectSizeInNewSpace());
- ASSERT_EQ(0, registers_to_save & target.bit());
- set_comment("[ DeferredAllocateInNewSpace");
- }
- void Generate();
-
- private:
- int size_;
- Register target_;
- int registers_to_save_;
-};
-
-
-void DeferredAllocateInNewSpace::Generate() {
- for (int i = 0; i < kNumRegs; i++) {
- if (registers_to_save_ & (1 << i)) {
- Register save_register = { i };
- __ push(save_register);
- }
- }
- __ push(Immediate(Smi::FromInt(size_)));
- __ CallRuntime(Runtime::kAllocateInNewSpace, 1);
- if (!target_.is(eax)) {
- __ mov(target_, eax);
- }
- for (int i = kNumRegs - 1; i >= 0; i--) {
- if (registers_to_save_ & (1 << i)) {
- Register save_register = { i };
- __ pop(save_register);
- }
- }
-}
-
-
-void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
- ASSERT(!in_safe_int32_mode());
- Comment cmnt(masm_, "[ RegExp Literal");
-
- // Retrieve the literals array and check the allocated entry. Begin
- // with a writable copy of the function of this activation in a
- // register.
- frame_->PushFunction();
- Result literals = frame_->Pop();
- literals.ToRegister();
- frame_->Spill(literals.reg());
-
- // Load the literals array of the function.
- __ mov(literals.reg(),
- FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
-
- // Load the literal at the ast saved index.
- Result boilerplate = allocator_->Allocate();
- ASSERT(boilerplate.is_valid());
- int literal_offset =
- FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
- __ mov(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset));
-
- // Check whether we need to materialize the RegExp object. If so,
- // jump to the deferred code passing the literals array.
- DeferredRegExpLiteral* deferred =
- new DeferredRegExpLiteral(boilerplate.reg(), literals.reg(), node);
- __ cmp(boilerplate.reg(), FACTORY->undefined_value());
- deferred->Branch(equal);
- deferred->BindExit();
-
- // Register of boilerplate contains RegExp object.
-
- Result tmp = allocator()->Allocate();
- ASSERT(tmp.is_valid());
-
- int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize;
-
- DeferredAllocateInNewSpace* allocate_fallback =
- new DeferredAllocateInNewSpace(size, literals.reg());
- frame_->Push(&boilerplate);
- frame_->SpillTop();
- __ AllocateInNewSpace(size,
- literals.reg(),
- tmp.reg(),
- no_reg,
- allocate_fallback->entry_label(),
- TAG_OBJECT);
- allocate_fallback->BindExit();
- boilerplate = frame_->Pop();
- // Copy from boilerplate to clone and return clone.
-
- for (int i = 0; i < size; i += kPointerSize) {
- __ mov(tmp.reg(), FieldOperand(boilerplate.reg(), i));
- __ mov(FieldOperand(literals.reg(), i), tmp.reg());
- }
- frame_->Push(&literals);
-}
-
-
-void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
- ASSERT(!in_safe_int32_mode());
- Comment cmnt(masm_, "[ ObjectLiteral");
-
- // Load a writable copy of the function of this activation in a
- // register.
- frame_->PushFunction();
- Result literals = frame_->Pop();
- literals.ToRegister();
- frame_->Spill(literals.reg());
-
- // Load the literals array of the function.
- __ mov(literals.reg(),
- FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
- // Literal array.
- frame_->Push(&literals);
- // Literal index.
- frame_->Push(Smi::FromInt(node->literal_index()));
- // Constant properties.
- frame_->Push(node->constant_properties());
- // Should the object literal have fast elements?
- frame_->Push(Smi::FromInt(node->fast_elements() ? 1 : 0));
- Result clone;
- if (node->depth() > 1) {
- clone = frame_->CallRuntime(Runtime::kCreateObjectLiteral, 4);
- } else {
- clone = frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
- }
- frame_->Push(&clone);
-
- // Mark all computed expressions that are bound to a key that
- // is shadowed by a later occurrence of the same key. For the
- // marked expressions, no store code is emitted.
- node->CalculateEmitStore();
-
- for (int i = 0; i < node->properties()->length(); i++) {
- ObjectLiteral::Property* property = node->properties()->at(i);
- switch (property->kind()) {
- case ObjectLiteral::Property::CONSTANT:
- break;
- case ObjectLiteral::Property::MATERIALIZED_LITERAL:
- if (CompileTimeValue::IsCompileTimeValue(property->value())) break;
- // else fall through.
- case ObjectLiteral::Property::COMPUTED: {
- Handle<Object> key(property->key()->handle());
- if (key->IsSymbol()) {
- // Duplicate the object as the IC receiver.
- frame_->Dup();
- Load(property->value());
- if (property->emit_store()) {
- Result ignored =
- frame_->CallStoreIC(Handle<String>::cast(key), false,
- strict_mode_flag());
- // A test eax instruction following the store IC call would
- // indicate the presence of an inlined version of the
- // store. Add a nop to indicate that there is no such
- // inlined version.
- __ nop();
- } else {
- frame_->Drop(2);
- }
- break;
- }
- // Fall through
- }
- case ObjectLiteral::Property::PROTOTYPE: {
- // Duplicate the object as an argument to the runtime call.
- frame_->Dup();
- Load(property->key());
- Load(property->value());
- if (property->emit_store()) {
- frame_->Push(Smi::FromInt(NONE)); // PropertyAttributes
- // Ignore the result.
- Result ignored = frame_->CallRuntime(Runtime::kSetProperty, 4);
- } else {
- frame_->Drop(3);
- }
- break;
- }
- case ObjectLiteral::Property::SETTER: {
- // Duplicate the object as an argument to the runtime call.
- frame_->Dup();
- Load(property->key());
- frame_->Push(Smi::FromInt(1));
- Load(property->value());
- Result ignored = frame_->CallRuntime(Runtime::kDefineAccessor, 4);
- // Ignore the result.
- break;
- }
- case ObjectLiteral::Property::GETTER: {
- // Duplicate the object as an argument to the runtime call.
- frame_->Dup();
- Load(property->key());
- frame_->Push(Smi::FromInt(0));
- Load(property->value());
- Result ignored = frame_->CallRuntime(Runtime::kDefineAccessor, 4);
- // Ignore the result.
- break;
- }
- default: UNREACHABLE();
- }
- }
-}
-
-
-void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
- ASSERT(!in_safe_int32_mode());
- Comment cmnt(masm_, "[ ArrayLiteral");
-
- // Load a writable copy of the function of this activation in a
- // register.
- frame_->PushFunction();
- Result literals = frame_->Pop();
- literals.ToRegister();
- frame_->Spill(literals.reg());
-
- // Load the literals array of the function.
- __ mov(literals.reg(),
- FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
-
- frame_->Push(&literals);
- frame_->Push(Smi::FromInt(node->literal_index()));
- frame_->Push(node->constant_elements());
- int length = node->values()->length();
- Result clone;
- if (node->constant_elements()->map() == HEAP->fixed_cow_array_map()) {
- FastCloneShallowArrayStub stub(
- FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, length);
- clone = frame_->CallStub(&stub, 3);
- Counters* counters = masm()->isolate()->counters();
- __ IncrementCounter(counters->cow_arrays_created_stub(), 1);
- } else if (node->depth() > 1) {
- clone = frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3);
- } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) {
- clone = frame_->CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
- } else {
- FastCloneShallowArrayStub stub(
- FastCloneShallowArrayStub::CLONE_ELEMENTS, length);
- clone = frame_->CallStub(&stub, 3);
- }
- frame_->Push(&clone);
-
- // Generate code to set the elements in the array that are not
- // literals.
- for (int i = 0; i < length; i++) {
- Expression* value = node->values()->at(i);
-
- if (!CompileTimeValue::ArrayLiteralElementNeedsInitialization(value)) {
- continue;
- }
-
- // The property must be set by generated code.
- Load(value);
-
- // Get the property value off the stack.
- Result prop_value = frame_->Pop();
- prop_value.ToRegister();
-
- // Fetch the array literal while leaving a copy on the stack and
- // use it to get the elements array.
- frame_->Dup();
- Result elements = frame_->Pop();
- elements.ToRegister();
- frame_->Spill(elements.reg());
- // Get the elements array.
- __ mov(elements.reg(),
- FieldOperand(elements.reg(), JSObject::kElementsOffset));
-
- // Write to the indexed properties array.
- int offset = i * kPointerSize + FixedArray::kHeaderSize;
- __ mov(FieldOperand(elements.reg(), offset), prop_value.reg());
-
- // Update the write barrier for the array address.
- frame_->Spill(prop_value.reg()); // Overwritten by the write barrier.
- Result scratch = allocator_->Allocate();
- ASSERT(scratch.is_valid());
- __ RecordWrite(elements.reg(), offset, prop_value.reg(), scratch.reg());
- }
-}
-
-
-void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) {
- ASSERT(!in_safe_int32_mode());
- ASSERT(!in_spilled_code());
- // Call runtime routine to allocate the catch extension object and
- // assign the exception value to the catch variable.
- Comment cmnt(masm_, "[ CatchExtensionObject");
- Load(node->key());
- Load(node->value());
- Result result =
- frame_->CallRuntime(Runtime::kCreateCatchExtensionObject, 2);
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::EmitSlotAssignment(Assignment* node) {
-#ifdef DEBUG
- int original_height = frame()->height();
-#endif
- Comment cmnt(masm(), "[ Variable Assignment");
- Variable* var = node->target()->AsVariableProxy()->AsVariable();
- ASSERT(var != NULL);
- Slot* slot = var->AsSlot();
- ASSERT(slot != NULL);
-
- // Evaluate the right-hand side.
- if (node->is_compound()) {
- // For a compound assignment the right-hand side is a binary operation
- // between the current property value and the actual right-hand side.
- LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
- Load(node->value());
-
- // Perform the binary operation.
- bool overwrite_value = node->value()->ResultOverwriteAllowed();
- // Construct the implicit binary operation.
- BinaryOperation expr(node);
- GenericBinaryOperation(&expr,
- overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
- } else {
- // For non-compound assignment just load the right-hand side.
- Load(node->value());
- }
-
- // Perform the assignment.
- if (var->mode() != Variable::CONST || node->op() == Token::INIT_CONST) {
- CodeForSourcePosition(node->position());
- StoreToSlot(slot,
- node->op() == Token::INIT_CONST ? CONST_INIT : NOT_CONST_INIT);
- }
- ASSERT(frame()->height() == original_height + 1);
-}
-
-
-void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) {
-#ifdef DEBUG
- int original_height = frame()->height();
-#endif
- Comment cmnt(masm(), "[ Named Property Assignment");
- Variable* var = node->target()->AsVariableProxy()->AsVariable();
- Property* prop = node->target()->AsProperty();
- ASSERT(var == NULL || (prop == NULL && var->is_global()));
-
- // Initialize name and evaluate the receiver sub-expression if necessary. If
- // the receiver is trivial it is not placed on the stack at this point, but
- // loaded whenever actually needed.
- Handle<String> name;
- bool is_trivial_receiver = false;
- if (var != NULL) {
- name = var->name();
- } else {
- Literal* lit = prop->key()->AsLiteral();
- ASSERT_NOT_NULL(lit);
- name = Handle<String>::cast(lit->handle());
- // Do not materialize the receiver on the frame if it is trivial.
- is_trivial_receiver = prop->obj()->IsTrivial();
- if (!is_trivial_receiver) Load(prop->obj());
- }
-
- // Change to slow case in the beginning of an initialization block to
- // avoid the quadratic behavior of repeatedly adding fast properties.
- if (node->starts_initialization_block()) {
- // Initialization block consists of assignments of the form expr.x = ..., so
- // this will never be an assignment to a variable, so there must be a
- // receiver object.
- ASSERT_EQ(NULL, var);
- if (is_trivial_receiver) {
- frame()->Push(prop->obj());
- } else {
- frame()->Dup();
- }
- Result ignored = frame()->CallRuntime(Runtime::kToSlowProperties, 1);
- }
-
- // Change to fast case at the end of an initialization block. To prepare for
- // that add an extra copy of the receiver to the frame, so that it can be
- // converted back to fast case after the assignment.
- if (node->ends_initialization_block() && !is_trivial_receiver) {
- frame()->Dup();
- }
-
- // Stack layout:
- // [tos] : receiver (only materialized if non-trivial)
- // [tos+1] : receiver if at the end of an initialization block
-
- // Evaluate the right-hand side.
- if (node->is_compound()) {
- // For a compound assignment the right-hand side is a binary operation
- // between the current property value and the actual right-hand side.
- if (is_trivial_receiver) {
- frame()->Push(prop->obj());
- } else if (var != NULL) {
- // The LoadIC stub expects the object in eax.
- // Freeing eax causes the code generator to load the global into it.
- frame_->Spill(eax);
- LoadGlobal();
- } else {
- frame()->Dup();
- }
- Result value = EmitNamedLoad(name, var != NULL);
- frame()->Push(&value);
- Load(node->value());
-
- bool overwrite_value = node->value()->ResultOverwriteAllowed();
- // Construct the implicit binary operation.
- BinaryOperation expr(node);
- GenericBinaryOperation(&expr,
- overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
- } else {
- // For non-compound assignment just load the right-hand side.
- Load(node->value());
- }
-
- // Stack layout:
- // [tos] : value
- // [tos+1] : receiver (only materialized if non-trivial)
- // [tos+2] : receiver if at the end of an initialization block
-
- // Perform the assignment. It is safe to ignore constants here.
- ASSERT(var == NULL || var->mode() != Variable::CONST);
- ASSERT_NE(Token::INIT_CONST, node->op());
- if (is_trivial_receiver) {
- Result value = frame()->Pop();
- frame()->Push(prop->obj());
- frame()->Push(&value);
- }
- CodeForSourcePosition(node->position());
- bool is_contextual = (var != NULL);
- Result answer = EmitNamedStore(name, is_contextual);
- frame()->Push(&answer);
-
- // Stack layout:
- // [tos] : result
- // [tos+1] : receiver if at the end of an initialization block
-
- if (node->ends_initialization_block()) {
- ASSERT_EQ(NULL, var);
- // The argument to the runtime call is the receiver.
- if (is_trivial_receiver) {
- frame()->Push(prop->obj());
- } else {
- // A copy of the receiver is below the value of the assignment. Swap
- // the receiver and the value of the assignment expression.
- Result result = frame()->Pop();
- Result receiver = frame()->Pop();
- frame()->Push(&result);
- frame()->Push(&receiver);
- }
- Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1);
- }
-
- // Stack layout:
- // [tos] : result
-
- ASSERT_EQ(frame()->height(), original_height + 1);
-}
-
-
-void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) {
-#ifdef DEBUG
- int original_height = frame()->height();
-#endif
- Comment cmnt(masm_, "[ Keyed Property Assignment");
- Property* prop = node->target()->AsProperty();
- ASSERT_NOT_NULL(prop);
-
- // Evaluate the receiver subexpression.
- Load(prop->obj());
-
- // Change to slow case in the beginning of an initialization block to
- // avoid the quadratic behavior of repeatedly adding fast properties.
- if (node->starts_initialization_block()) {
- frame_->Dup();
- Result ignored = frame_->CallRuntime(Runtime::kToSlowProperties, 1);
- }
-
- // Change to fast case at the end of an initialization block. To prepare for
- // that add an extra copy of the receiver to the frame, so that it can be
- // converted back to fast case after the assignment.
- if (node->ends_initialization_block()) {
- frame_->Dup();
- }
-
- // Evaluate the key subexpression.
- Load(prop->key());
-
- // Stack layout:
- // [tos] : key
- // [tos+1] : receiver
- // [tos+2] : receiver if at the end of an initialization block
-
- // Evaluate the right-hand side.
- if (node->is_compound()) {
- // For a compound assignment the right-hand side is a binary operation
- // between the current property value and the actual right-hand side.
- // Duplicate receiver and key for loading the current property value.
- frame()->PushElementAt(1);
- frame()->PushElementAt(1);
- Result value = EmitKeyedLoad();
- frame()->Push(&value);
- Load(node->value());
-
- // Perform the binary operation.
- bool overwrite_value = node->value()->ResultOverwriteAllowed();
- BinaryOperation expr(node);
- GenericBinaryOperation(&expr,
- overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
- } else {
- // For non-compound assignment just load the right-hand side.
- Load(node->value());
- }
-
- // Stack layout:
- // [tos] : value
- // [tos+1] : key
- // [tos+2] : receiver
- // [tos+3] : receiver if at the end of an initialization block
-
- // Perform the assignment. It is safe to ignore constants here.
- ASSERT(node->op() != Token::INIT_CONST);
- CodeForSourcePosition(node->position());
- Result answer = EmitKeyedStore(prop->key()->type());
- frame()->Push(&answer);
-
- // Stack layout:
- // [tos] : result
- // [tos+1] : receiver if at the end of an initialization block
-
- // Change to fast case at the end of an initialization block.
- if (node->ends_initialization_block()) {
- // The argument to the runtime call is the extra copy of the receiver,
- // which is below the value of the assignment. Swap the receiver and
- // the value of the assignment expression.
- Result result = frame()->Pop();
- Result receiver = frame()->Pop();
- frame()->Push(&result);
- frame()->Push(&receiver);
- Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1);
- }
-
- // Stack layout:
- // [tos] : result
-
- ASSERT(frame()->height() == original_height + 1);
-}
-
-
-void CodeGenerator::VisitAssignment(Assignment* node) {
- ASSERT(!in_safe_int32_mode());
-#ifdef DEBUG
- int original_height = frame()->height();
-#endif
- Variable* var = node->target()->AsVariableProxy()->AsVariable();
- Property* prop = node->target()->AsProperty();
-
- if (var != NULL && !var->is_global()) {
- EmitSlotAssignment(node);
-
- } else if ((prop != NULL && prop->key()->IsPropertyName()) ||
- (var != NULL && var->is_global())) {
- // Properties whose keys are property names and global variables are
- // treated as named property references. We do not need to consider
- // global 'this' because it is not a valid left-hand side.
- EmitNamedPropertyAssignment(node);
-
- } else if (prop != NULL) {
- // Other properties (including rewritten parameters for a function that
- // uses arguments) are keyed property assignments.
- EmitKeyedPropertyAssignment(node);
-
- } else {
- // Invalid left-hand side.
- Load(node->target());
- Result result = frame()->CallRuntime(Runtime::kThrowReferenceError, 1);
- // The runtime call doesn't actually return but the code generator will
- // still generate code and expects a certain frame height.
- frame()->Push(&result);
- }
-
- ASSERT(frame()->height() == original_height + 1);
-}
-
-
-void CodeGenerator::VisitThrow(Throw* node) {
- ASSERT(!in_safe_int32_mode());
- Comment cmnt(masm_, "[ Throw");
- Load(node->exception());
- Result result = frame_->CallRuntime(Runtime::kThrow, 1);
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::VisitProperty(Property* node) {
- ASSERT(!in_safe_int32_mode());
- Comment cmnt(masm_, "[ Property");
- Reference property(this, node);
- property.GetValue();
-}
-
-
-void CodeGenerator::VisitCall(Call* node) {
- ASSERT(!in_safe_int32_mode());
- Comment cmnt(masm_, "[ Call");
-
- Expression* function = node->expression();
- ZoneList<Expression*>* args = node->arguments();
-
- // Check if the function is a variable or a property.
- Variable* var = function->AsVariableProxy()->AsVariable();
- Property* property = function->AsProperty();
-
- // ------------------------------------------------------------------------
- // Fast-case: Use inline caching.
- // ---
- // According to ECMA-262, section 11.2.3, page 44, the function to call
- // must be resolved after the arguments have been evaluated. The IC code
- // automatically handles this by loading the arguments before the function
- // is resolved in cache misses (this also holds for megamorphic calls).
- // ------------------------------------------------------------------------
-
- if (var != NULL && var->is_possibly_eval()) {
- // ----------------------------------
- // JavaScript example: 'eval(arg)' // eval is not known to be shadowed
- // ----------------------------------
-
- // In a call to eval, we first call %ResolvePossiblyDirectEval to
- // resolve the function we need to call and the receiver of the
- // call. Then we call the resolved function using the given
- // arguments.
-
- // Prepare the stack for the call to the resolved function.
- Load(function);
-
- // Allocate a frame slot for the receiver.
- frame_->Push(FACTORY->undefined_value());
-
- // Load the arguments.
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- frame_->SpillTop();
- }
-
- // Result to hold the result of the function resolution and the
- // final result of the eval call.
- Result result;
-
- // If we know that eval can only be shadowed by eval-introduced
- // variables we attempt to load the global eval function directly
- // in generated code. If we succeed, there is no need to perform a
- // context lookup in the runtime system.
- JumpTarget done;
- if (var->AsSlot() != NULL && var->mode() == Variable::DYNAMIC_GLOBAL) {
- ASSERT(var->AsSlot()->type() == Slot::LOOKUP);
- JumpTarget slow;
- // Prepare the stack for the call to
- // ResolvePossiblyDirectEvalNoLookup by pushing the loaded
- // function, the first argument to the eval call and the
- // receiver.
- Result fun = LoadFromGlobalSlotCheckExtensions(var->AsSlot(),
- NOT_INSIDE_TYPEOF,
- &slow);
- frame_->Push(&fun);
- if (arg_count > 0) {
- frame_->PushElementAt(arg_count);
- } else {
- frame_->Push(FACTORY->undefined_value());
- }
- frame_->PushParameterAt(-1);
-
- // Push the strict mode flag.
- frame_->Push(Smi::FromInt(strict_mode_flag()));
-
- // Resolve the call.
- result =
- frame_->CallRuntime(Runtime::kResolvePossiblyDirectEvalNoLookup, 4);
-
- done.Jump(&result);
- slow.Bind();
- }
-
- // Prepare the stack for the call to ResolvePossiblyDirectEval by
- // pushing the loaded function, the first argument to the eval
- // call and the receiver.
- frame_->PushElementAt(arg_count + 1);
- if (arg_count > 0) {
- frame_->PushElementAt(arg_count);
- } else {
- frame_->Push(FACTORY->undefined_value());
- }
- frame_->PushParameterAt(-1);
-
- // Push the strict mode flag.
- frame_->Push(Smi::FromInt(strict_mode_flag()));
-
- // Resolve the call.
- result = frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 4);
-
- // If we generated fast-case code bind the jump-target where fast
- // and slow case merge.
- if (done.is_linked()) done.Bind(&result);
-
- // The runtime call returns a pair of values in eax (function) and
- // edx (receiver). Touch up the stack with the right values.
- Result receiver = allocator_->Allocate(edx);
- frame_->SetElementAt(arg_count + 1, &result);
- frame_->SetElementAt(arg_count, &receiver);
- receiver.Unuse();
-
- // Call the function.
- CodeForSourcePosition(node->position());
- InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub call_function(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
- result = frame_->CallStub(&call_function, arg_count + 1);
-
- // Restore the context and overwrite the function on the stack with
- // the result.
- frame_->RestoreContextRegister();
- frame_->SetElementAt(0, &result);
-
- } else if (var != NULL && !var->is_this() && var->is_global()) {
- // ----------------------------------
- // JavaScript example: 'foo(1, 2, 3)' // foo is global
- // ----------------------------------
-
- // Pass the global object as the receiver and let the IC stub
- // patch the stack to use the global proxy as 'this' in the
- // invoked function.
- LoadGlobal();
-
- // Load the arguments.
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- frame_->SpillTop();
- }
-
- // Push the name of the function onto the frame.
- frame_->Push(var->name());
-
- // Call the IC initialization code.
- CodeForSourcePosition(node->position());
- Result result = frame_->CallCallIC(RelocInfo::CODE_TARGET_CONTEXT,
- arg_count,
- loop_nesting());
- frame_->RestoreContextRegister();
- frame_->Push(&result);
-
- } else if (var != NULL && var->AsSlot() != NULL &&
- var->AsSlot()->type() == Slot::LOOKUP) {
- // ----------------------------------
- // JavaScript examples:
- //
- // with (obj) foo(1, 2, 3) // foo may be in obj.
- //
- // function f() {};
- // function g() {
- // eval(...);
- // f(); // f could be in extension object.
- // }
- // ----------------------------------
-
- JumpTarget slow, done;
- Result function;
-
- // Generate fast case for loading functions from slots that
- // correspond to local/global variables or arguments unless they
- // are shadowed by eval-introduced bindings.
- EmitDynamicLoadFromSlotFastCase(var->AsSlot(),
- NOT_INSIDE_TYPEOF,
- &function,
- &slow,
- &done);
-
- slow.Bind();
- // Enter the runtime system to load the function from the context.
- // Sync the frame so we can push the arguments directly into
- // place.
- frame_->SyncRange(0, frame_->element_count() - 1);
- frame_->EmitPush(esi);
- frame_->EmitPush(Immediate(var->name()));
- frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
- // The runtime call returns a pair of values in eax and edx. The
- // looked-up function is in eax and the receiver is in edx. These
- // register references are not ref counted here. We spill them
- // eagerly since they are arguments to an inevitable call (and are
- // not sharable by the arguments).
- ASSERT(!allocator()->is_used(eax));
- frame_->EmitPush(eax);
-
- // Load the receiver.
- ASSERT(!allocator()->is_used(edx));
- frame_->EmitPush(edx);
-
- // If fast case code has been generated, emit code to push the
- // function and receiver and have the slow path jump around this
- // code.
- if (done.is_linked()) {
- JumpTarget call;
- call.Jump();
- done.Bind(&function);
- frame_->Push(&function);
- LoadGlobalReceiver();
- call.Bind();
- }
-
- // Call the function.
- CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
-
- } else if (property != NULL) {
- // Check if the key is a literal string.
- Literal* literal = property->key()->AsLiteral();
-
- if (literal != NULL && literal->handle()->IsSymbol()) {
- // ------------------------------------------------------------------
- // JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
- // ------------------------------------------------------------------
-
- Handle<String> name = Handle<String>::cast(literal->handle());
-
- if (ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION &&
- name->IsEqualTo(CStrVector("apply")) &&
- args->length() == 2 &&
- args->at(1)->AsVariableProxy() != NULL &&
- args->at(1)->AsVariableProxy()->IsArguments()) {
- // Use the optimized Function.prototype.apply that avoids
- // allocating lazily allocated arguments objects.
- CallApplyLazy(property->obj(),
- args->at(0),
- args->at(1)->AsVariableProxy(),
- node->position());
-
- } else {
- // Push the receiver onto the frame.
- Load(property->obj());
-
- // Load the arguments.
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- frame_->SpillTop();
- }
-
- // Push the name of the function onto the frame.
- frame_->Push(name);
-
- // Call the IC initialization code.
- CodeForSourcePosition(node->position());
- Result result =
- frame_->CallCallIC(RelocInfo::CODE_TARGET, arg_count,
- loop_nesting());
- frame_->RestoreContextRegister();
- frame_->Push(&result);
- }
-
- } else {
- // -------------------------------------------
- // JavaScript example: 'array[index](1, 2, 3)'
- // -------------------------------------------
-
- // Load the function to call from the property through a reference.
-
- // Pass receiver to called function.
- if (property->is_synthetic()) {
- Reference ref(this, property);
- ref.GetValue();
- // Use global object as receiver.
- LoadGlobalReceiver();
- // Call the function.
- CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
- } else {
- // Push the receiver onto the frame.
- Load(property->obj());
-
- // Load the name of the function.
- Load(property->key());
-
- // Swap the name of the function and the receiver on the stack to follow
- // the calling convention for call ICs.
- Result key = frame_->Pop();
- Result receiver = frame_->Pop();
- frame_->Push(&key);
- frame_->Push(&receiver);
- key.Unuse();
- receiver.Unuse();
-
- // Load the arguments.
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- frame_->SpillTop();
- }
-
- // Place the key on top of stack and call the IC initialization code.
- frame_->PushElementAt(arg_count + 1);
- CodeForSourcePosition(node->position());
- Result result =
- frame_->CallKeyedCallIC(RelocInfo::CODE_TARGET,
- arg_count,
- loop_nesting());
- frame_->Drop(); // Drop the key still on the stack.
- frame_->RestoreContextRegister();
- frame_->Push(&result);
- }
- }
-
- } else {
- // ----------------------------------
- // JavaScript example: 'foo(1, 2, 3)' // foo is not global
- // ----------------------------------
-
- // Load the function.
- Load(function);
-
- // Pass the global proxy as the receiver.
- LoadGlobalReceiver();
-
- // Call the function.
- CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
- }
-}
-
-
-void CodeGenerator::VisitCallNew(CallNew* node) {
- ASSERT(!in_safe_int32_mode());
- Comment cmnt(masm_, "[ CallNew");
-
- // According to ECMA-262, section 11.2.2, page 44, the function
- // expression in new calls must be evaluated before the
- // arguments. This is different from ordinary calls, where the
- // actual function to call is resolved after the arguments have been
- // evaluated.
-
- // Push constructor on the stack. If it's not a function it's used as
- // receiver for CALL_NON_FUNCTION, otherwise the value on the stack is
- // ignored.
- Load(node->expression());
-
- // Push the arguments ("left-to-right") on the stack.
- ZoneList<Expression*>* args = node->arguments();
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- }
-
- // Call the construct call builtin that handles allocation and
- // constructor invocation.
- CodeForSourcePosition(node->position());
- Result result = frame_->CallConstructor(arg_count);
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result value = frame_->Pop();
- value.ToRegister();
- ASSERT(value.is_valid());
- __ test(value.reg(), Immediate(kSmiTagMask));
- value.Unuse();
- destination()->Split(zero);
-}
-
-
-void CodeGenerator::GenerateLog(ZoneList<Expression*>* args) {
- // Conditionally generate a log call.
- // Args:
- // 0 (literal string): The type of logging (corresponds to the flags).
- // This is used to determine whether or not to generate the log call.
- // 1 (string): Format string. Access the string at argument index 2
- // with '%2s' (see Logger::LogRuntime for all the formats).
- // 2 (array): Arguments to the format string.
- ASSERT_EQ(args->length(), 3);
-#ifdef ENABLE_LOGGING_AND_PROFILING
- if (ShouldGenerateLog(args->at(0))) {
- Load(args->at(1));
- Load(args->at(2));
- frame_->CallRuntime(Runtime::kLog, 2);
- }
-#endif
- // Finally, we're expected to leave a value on the top of the stack.
- frame_->Push(FACTORY->undefined_value());
-}
-
-
-void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result value = frame_->Pop();
- value.ToRegister();
- ASSERT(value.is_valid());
- __ test(value.reg(), Immediate(kSmiTagMask | kSmiSignMask));
- value.Unuse();
- destination()->Split(zero);
-}
-
-
-class DeferredStringCharCodeAt : public DeferredCode {
- public:
- DeferredStringCharCodeAt(Register object,
- Register index,
- Register scratch,
- Register result)
- : result_(result),
- char_code_at_generator_(object,
- index,
- scratch,
- result,
- &need_conversion_,
- &need_conversion_,
- &index_out_of_range_,
- STRING_INDEX_IS_NUMBER) {}
-
- StringCharCodeAtGenerator* fast_case_generator() {
- return &char_code_at_generator_;
- }
-
- virtual void Generate() {
- VirtualFrameRuntimeCallHelper call_helper(frame_state());
- char_code_at_generator_.GenerateSlow(masm(), call_helper);
-
- __ bind(&need_conversion_);
- // Move the undefined value into the result register, which will
- // trigger conversion.
- __ Set(result_, Immediate(FACTORY->undefined_value()));
- __ jmp(exit_label());
-
- __ bind(&index_out_of_range_);
- // When the index is out of range, the spec requires us to return
- // NaN.
- __ Set(result_, Immediate(FACTORY->nan_value()));
- __ jmp(exit_label());
- }
-
- private:
- Register result_;
-
- Label need_conversion_;
- Label index_out_of_range_;
-
- StringCharCodeAtGenerator char_code_at_generator_;
-};
-
-
-// This generates code that performs a String.prototype.charCodeAt() call
-// or returns a smi in order to trigger conversion.
-void CodeGenerator::GenerateStringCharCodeAt(ZoneList<Expression*>* args) {
- Comment(masm_, "[ GenerateStringCharCodeAt");
- ASSERT(args->length() == 2);
-
- Load(args->at(0));
- Load(args->at(1));
- Result index = frame_->Pop();
- Result object = frame_->Pop();
- object.ToRegister();
- index.ToRegister();
- // We might mutate the object register.
- frame_->Spill(object.reg());
-
- // We need two extra registers.
- Result result = allocator()->Allocate();
- ASSERT(result.is_valid());
- Result scratch = allocator()->Allocate();
- ASSERT(scratch.is_valid());
-
- DeferredStringCharCodeAt* deferred =
- new DeferredStringCharCodeAt(object.reg(),
- index.reg(),
- scratch.reg(),
- result.reg());
- deferred->fast_case_generator()->GenerateFast(masm_);
- deferred->BindExit();
- frame_->Push(&result);
-}
-
-
-class DeferredStringCharFromCode : public DeferredCode {
- public:
- DeferredStringCharFromCode(Register code,
- Register result)
- : char_from_code_generator_(code, result) {}
-
- StringCharFromCodeGenerator* fast_case_generator() {
- return &char_from_code_generator_;
- }
-
- virtual void Generate() {
- VirtualFrameRuntimeCallHelper call_helper(frame_state());
- char_from_code_generator_.GenerateSlow(masm(), call_helper);
- }
-
- private:
- StringCharFromCodeGenerator char_from_code_generator_;
-};
-
-
-// Generates code for creating a one-char string from a char code.
-void CodeGenerator::GenerateStringCharFromCode(ZoneList<Expression*>* args) {
- Comment(masm_, "[ GenerateStringCharFromCode");
- ASSERT(args->length() == 1);
-
- Load(args->at(0));
-
- Result code = frame_->Pop();
- code.ToRegister();
- ASSERT(code.is_valid());
-
- Result result = allocator()->Allocate();
- ASSERT(result.is_valid());
-
- DeferredStringCharFromCode* deferred = new DeferredStringCharFromCode(
- code.reg(), result.reg());
- deferred->fast_case_generator()->GenerateFast(masm_);
- deferred->BindExit();
- frame_->Push(&result);
-}
-
-
-class DeferredStringCharAt : public DeferredCode {
- public:
- DeferredStringCharAt(Register object,
- Register index,
- Register scratch1,
- Register scratch2,
- Register result)
- : result_(result),
- char_at_generator_(object,
- index,
- scratch1,
- scratch2,
- result,
- &need_conversion_,
- &need_conversion_,
- &index_out_of_range_,
- STRING_INDEX_IS_NUMBER) {}
-
- StringCharAtGenerator* fast_case_generator() {
- return &char_at_generator_;
- }
-
- virtual void Generate() {
- VirtualFrameRuntimeCallHelper call_helper(frame_state());
- char_at_generator_.GenerateSlow(masm(), call_helper);
-
- __ bind(&need_conversion_);
- // Move smi zero into the result register, which will trigger
- // conversion.
- __ Set(result_, Immediate(Smi::FromInt(0)));
- __ jmp(exit_label());
-
- __ bind(&index_out_of_range_);
- // When the index is out of range, the spec requires us to return
- // the empty string.
- __ Set(result_, Immediate(FACTORY->empty_string()));
- __ jmp(exit_label());
- }
-
- private:
- Register result_;
-
- Label need_conversion_;
- Label index_out_of_range_;
-
- StringCharAtGenerator char_at_generator_;
-};
-
-
-// This generates code that performs a String.prototype.charAt() call
-// or returns a smi in order to trigger conversion.
-void CodeGenerator::GenerateStringCharAt(ZoneList<Expression*>* args) {
- Comment(masm_, "[ GenerateStringCharAt");
- ASSERT(args->length() == 2);
-
- Load(args->at(0));
- Load(args->at(1));
- Result index = frame_->Pop();
- Result object = frame_->Pop();
- object.ToRegister();
- index.ToRegister();
- // We might mutate the object register.
- frame_->Spill(object.reg());
-
- // We need three extra registers.
- Result result = allocator()->Allocate();
- ASSERT(result.is_valid());
- Result scratch1 = allocator()->Allocate();
- ASSERT(scratch1.is_valid());
- Result scratch2 = allocator()->Allocate();
- ASSERT(scratch2.is_valid());
-
- DeferredStringCharAt* deferred =
- new DeferredStringCharAt(object.reg(),
- index.reg(),
- scratch1.reg(),
- scratch2.reg(),
- result.reg());
- deferred->fast_case_generator()->GenerateFast(masm_);
- deferred->BindExit();
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result value = frame_->Pop();
- value.ToRegister();
- ASSERT(value.is_valid());
- __ test(value.reg(), Immediate(kSmiTagMask));
- destination()->false_target()->Branch(equal);
- // It is a heap object - get map.
- Result temp = allocator()->Allocate();
- ASSERT(temp.is_valid());
- // Check if the object is a JS array or not.
- __ CmpObjectType(value.reg(), JS_ARRAY_TYPE, temp.reg());
- value.Unuse();
- temp.Unuse();
- destination()->Split(equal);
-}
-
-
-void CodeGenerator::GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args) {
- Label bailout, done, one_char_separator, long_separator,
- non_trivial_array, not_size_one_array, loop, loop_condition,
- loop_1, loop_1_condition, loop_2, loop_2_entry, loop_3, loop_3_entry;
-
- ASSERT(args->length() == 2);
- // We will leave the separator on the stack until the end of the function.
- Load(args->at(1));
- // Load this to eax (= array)
- Load(args->at(0));
- Result array_result = frame_->Pop();
- array_result.ToRegister(eax);
- frame_->SpillAll();
-
- // All aliases of the same register have disjoint lifetimes.
- Register array = eax;
- Register elements = no_reg; // Will be eax.
-
- Register index = edx;
-
- Register string_length = ecx;
-
- Register string = esi;
-
- Register scratch = ebx;
-
- Register array_length = edi;
- Register result_pos = no_reg; // Will be edi.
-
- // Separator operand is already pushed.
- Operand separator_operand = Operand(esp, 2 * kPointerSize);
- Operand result_operand = Operand(esp, 1 * kPointerSize);
- Operand array_length_operand = Operand(esp, 0);
- __ sub(Operand(esp), Immediate(2 * kPointerSize));
- __ cld();
- // Check that the array is a JSArray
- __ test(array, Immediate(kSmiTagMask));
- __ j(zero, &bailout);
- __ CmpObjectType(array, JS_ARRAY_TYPE, scratch);
- __ j(not_equal, &bailout);
-
- // Check that the array has fast elements.
- __ test_b(FieldOperand(scratch, Map::kBitField2Offset),
- 1 << Map::kHasFastElements);
- __ j(zero, &bailout);
-
- // If the array has length zero, return the empty string.
- __ mov(array_length, FieldOperand(array, JSArray::kLengthOffset));
- __ sar(array_length, 1);
- __ j(not_zero, &non_trivial_array);
- __ mov(result_operand, FACTORY->empty_string());
- __ jmp(&done);
-
- // Save the array length.
- __ bind(&non_trivial_array);
- __ mov(array_length_operand, array_length);
-
- // Save the FixedArray containing array's elements.
- // End of array's live range.
- elements = array;
- __ mov(elements, FieldOperand(array, JSArray::kElementsOffset));
- array = no_reg;
-
-
- // Check that all array elements are sequential ASCII strings, and
- // accumulate the sum of their lengths, as a smi-encoded value.
- __ Set(index, Immediate(0));
- __ Set(string_length, Immediate(0));
- // Loop condition: while (index < length).
- // Live loop registers: index, array_length, string,
- // scratch, string_length, elements.
- __ jmp(&loop_condition);
- __ bind(&loop);
- __ cmp(index, Operand(array_length));
- __ j(greater_equal, &done);
-
- __ mov(string, FieldOperand(elements, index,
- times_pointer_size,
- FixedArray::kHeaderSize));
- __ test(string, Immediate(kSmiTagMask));
- __ j(zero, &bailout);
- __ mov(scratch, FieldOperand(string, HeapObject::kMapOffset));
- __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
- __ and_(scratch, Immediate(
- kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask));
- __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag);
- __ j(not_equal, &bailout);
- __ add(string_length,
- FieldOperand(string, SeqAsciiString::kLengthOffset));
- __ j(overflow, &bailout);
- __ add(Operand(index), Immediate(1));
- __ bind(&loop_condition);
- __ cmp(index, Operand(array_length));
- __ j(less, &loop);
-
- // If array_length is 1, return elements[0], a string.
- __ cmp(array_length, 1);
- __ j(not_equal, ¬_size_one_array);
- __ mov(scratch, FieldOperand(elements, FixedArray::kHeaderSize));
- __ mov(result_operand, scratch);
- __ jmp(&done);
-
- __ bind(¬_size_one_array);
-
- // End of array_length live range.
- result_pos = array_length;
- array_length = no_reg;
-
- // Live registers:
- // string_length: Sum of string lengths, as a smi.
- // elements: FixedArray of strings.
-
- // Check that the separator is a flat ASCII string.
- __ mov(string, separator_operand);
- __ test(string, Immediate(kSmiTagMask));
- __ j(zero, &bailout);
- __ mov(scratch, FieldOperand(string, HeapObject::kMapOffset));
- __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
- __ and_(scratch, Immediate(
- kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask));
- __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag);
- __ j(not_equal, &bailout);
-
- // Add (separator length times array_length) - separator length
- // to string_length.
- __ mov(scratch, separator_operand);
- __ mov(scratch, FieldOperand(scratch, SeqAsciiString::kLengthOffset));
- __ sub(string_length, Operand(scratch)); // May be negative, temporarily.
- __ imul(scratch, array_length_operand);
- __ j(overflow, &bailout);
- __ add(string_length, Operand(scratch));
- __ j(overflow, &bailout);
-
- __ shr(string_length, 1);
- // Live registers and stack values:
- // string_length
- // elements
- __ AllocateAsciiString(result_pos, string_length, scratch,
- index, string, &bailout);
- __ mov(result_operand, result_pos);
- __ lea(result_pos, FieldOperand(result_pos, SeqAsciiString::kHeaderSize));
-
-
- __ mov(string, separator_operand);
- __ cmp(FieldOperand(string, SeqAsciiString::kLengthOffset),
- Immediate(Smi::FromInt(1)));
- __ j(equal, &one_char_separator);
- __ j(greater, &long_separator);
-
-
- // Empty separator case
- __ mov(index, Immediate(0));
- __ jmp(&loop_1_condition);
- // Loop condition: while (index < length).
- __ bind(&loop_1);
- // Each iteration of the loop concatenates one string to the result.
- // Live values in registers:
- // index: which element of the elements array we are adding to the result.
- // result_pos: the position to which we are currently copying characters.
- // elements: the FixedArray of strings we are joining.
-
- // Get string = array[index].
- __ mov(string, FieldOperand(elements, index,
- times_pointer_size,
- FixedArray::kHeaderSize));
- __ mov(string_length,
- FieldOperand(string, String::kLengthOffset));
- __ shr(string_length, 1);
- __ lea(string,
- FieldOperand(string, SeqAsciiString::kHeaderSize));
- __ CopyBytes(string, result_pos, string_length, scratch);
- __ add(Operand(index), Immediate(1));
- __ bind(&loop_1_condition);
- __ cmp(index, array_length_operand);
- __ j(less, &loop_1); // End while (index < length).
- __ jmp(&done);
-
-
-
- // One-character separator case
- __ bind(&one_char_separator);
- // Replace separator with its ascii character value.
- __ mov_b(scratch, FieldOperand(string, SeqAsciiString::kHeaderSize));
- __ mov_b(separator_operand, scratch);
-
- __ Set(index, Immediate(0));
- // Jump into the loop after the code that copies the separator, so the first
- // element is not preceded by a separator
- __ jmp(&loop_2_entry);
- // Loop condition: while (index < length).
- __ bind(&loop_2);
- // Each iteration of the loop concatenates one string to the result.
- // Live values in registers:
- // index: which element of the elements array we are adding to the result.
- // result_pos: the position to which we are currently copying characters.
-
- // Copy the separator character to the result.
- __ mov_b(scratch, separator_operand);
- __ mov_b(Operand(result_pos, 0), scratch);
- __ inc(result_pos);
-
- __ bind(&loop_2_entry);
- // Get string = array[index].
- __ mov(string, FieldOperand(elements, index,
- times_pointer_size,
- FixedArray::kHeaderSize));
- __ mov(string_length,
- FieldOperand(string, String::kLengthOffset));
- __ shr(string_length, 1);
- __ lea(string,
- FieldOperand(string, SeqAsciiString::kHeaderSize));
- __ CopyBytes(string, result_pos, string_length, scratch);
- __ add(Operand(index), Immediate(1));
-
- __ cmp(index, array_length_operand);
- __ j(less, &loop_2); // End while (index < length).
- __ jmp(&done);
-
-
- // Long separator case (separator is more than one character).
- __ bind(&long_separator);
-
- __ Set(index, Immediate(0));
- // Jump into the loop after the code that copies the separator, so the first
- // element is not preceded by a separator
- __ jmp(&loop_3_entry);
- // Loop condition: while (index < length).
- __ bind(&loop_3);
- // Each iteration of the loop concatenates one string to the result.
- // Live values in registers:
- // index: which element of the elements array we are adding to the result.
- // result_pos: the position to which we are currently copying characters.
-
- // Copy the separator to the result.
- __ mov(string, separator_operand);
- __ mov(string_length,
- FieldOperand(string, String::kLengthOffset));
- __ shr(string_length, 1);
- __ lea(string,
- FieldOperand(string, SeqAsciiString::kHeaderSize));
- __ CopyBytes(string, result_pos, string_length, scratch);
-
- __ bind(&loop_3_entry);
- // Get string = array[index].
- __ mov(string, FieldOperand(elements, index,
- times_pointer_size,
- FixedArray::kHeaderSize));
- __ mov(string_length,
- FieldOperand(string, String::kLengthOffset));
- __ shr(string_length, 1);
- __ lea(string,
- FieldOperand(string, SeqAsciiString::kHeaderSize));
- __ CopyBytes(string, result_pos, string_length, scratch);
- __ add(Operand(index), Immediate(1));
-
- __ cmp(index, array_length_operand);
- __ j(less, &loop_3); // End while (index < length).
- __ jmp(&done);
-
-
- __ bind(&bailout);
- __ mov(result_operand, FACTORY->undefined_value());
- __ bind(&done);
- __ mov(eax, result_operand);
- // Drop temp values from the stack, and restore context register.
- __ add(Operand(esp), Immediate(2 * kPointerSize));
-
- __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
- frame_->Drop(1);
- frame_->Push(&array_result);
-}
-
-
-void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result value = frame_->Pop();
- value.ToRegister();
- ASSERT(value.is_valid());
- __ test(value.reg(), Immediate(kSmiTagMask));
- destination()->false_target()->Branch(equal);
- // It is a heap object - get map.
- Result temp = allocator()->Allocate();
- ASSERT(temp.is_valid());
- // Check if the object is a regexp.
- __ CmpObjectType(value.reg(), JS_REGEXP_TYPE, temp.reg());
- value.Unuse();
- temp.Unuse();
- destination()->Split(equal);
-}
-
-
-void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
- // This generates a fast version of:
- // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result obj = frame_->Pop();
- obj.ToRegister();
-
- __ test(obj.reg(), Immediate(kSmiTagMask));
- destination()->false_target()->Branch(zero);
- __ cmp(obj.reg(), FACTORY->null_value());
- destination()->true_target()->Branch(equal);
-
- Result map = allocator()->Allocate();
- ASSERT(map.is_valid());
- __ mov(map.reg(), FieldOperand(obj.reg(), HeapObject::kMapOffset));
- // Undetectable objects behave like undefined when tested with typeof.
- __ test_b(FieldOperand(map.reg(), Map::kBitFieldOffset),
- 1 << Map::kIsUndetectable);
- destination()->false_target()->Branch(not_zero);
- // Do a range test for JSObject type. We can't use
- // MacroAssembler::IsInstanceJSObjectType, because we are using a
- // ControlDestination, so we copy its implementation here.
- __ movzx_b(map.reg(), FieldOperand(map.reg(), Map::kInstanceTypeOffset));
- __ sub(Operand(map.reg()), Immediate(FIRST_JS_OBJECT_TYPE));
- __ cmp(map.reg(), LAST_JS_OBJECT_TYPE - FIRST_JS_OBJECT_TYPE);
- obj.Unuse();
- map.Unuse();
- destination()->Split(below_equal);
-}
-
-
-void CodeGenerator::GenerateIsSpecObject(ZoneList<Expression*>* args) {
- // This generates a fast version of:
- // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp' ||
- // typeof(arg) == function).
- // It includes undetectable objects (as opposed to IsObject).
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result value = frame_->Pop();
- value.ToRegister();
- ASSERT(value.is_valid());
- __ test(value.reg(), Immediate(kSmiTagMask));
- destination()->false_target()->Branch(equal);
-
- // Check that this is an object.
- frame_->Spill(value.reg());
- __ CmpObjectType(value.reg(), FIRST_JS_OBJECT_TYPE, value.reg());
- value.Unuse();
- destination()->Split(above_equal);
-}
-
-
-// Deferred code to check whether the String JavaScript object is safe for using
-// default value of. This code is called after the bit caching this information
-// in the map has been checked with the map for the object in the map_result_
-// register. On return the register map_result_ contains 1 for true and 0 for
-// false.
-class DeferredIsStringWrapperSafeForDefaultValueOf : public DeferredCode {
- public:
- DeferredIsStringWrapperSafeForDefaultValueOf(Register object,
- Register map_result,
- Register scratch1,
- Register scratch2)
- : object_(object),
- map_result_(map_result),
- scratch1_(scratch1),
- scratch2_(scratch2) { }
-
- virtual void Generate() {
- Label false_result;
-
- // Check that map is loaded as expected.
- if (FLAG_debug_code) {
- __ cmp(map_result_, FieldOperand(object_, HeapObject::kMapOffset));
- __ Assert(equal, "Map not in expected register");
- }
-
- // Check for fast case object. Generate false result for slow case object.
- __ mov(scratch1_, FieldOperand(object_, JSObject::kPropertiesOffset));
- __ mov(scratch1_, FieldOperand(scratch1_, HeapObject::kMapOffset));
- __ cmp(scratch1_, FACTORY->hash_table_map());
- __ j(equal, &false_result);
-
- // Look for valueOf symbol in the descriptor array, and indicate false if
- // found. The type is not checked, so if it is a transition it is a false
- // negative.
- __ mov(map_result_,
- FieldOperand(map_result_, Map::kInstanceDescriptorsOffset));
- __ mov(scratch1_, FieldOperand(map_result_, FixedArray::kLengthOffset));
- // map_result_: descriptor array
- // scratch1_: length of descriptor array
- // Calculate the end of the descriptor array.
- STATIC_ASSERT(kSmiTag == 0);
- STATIC_ASSERT(kSmiTagSize == 1);
- STATIC_ASSERT(kPointerSize == 4);
- __ lea(scratch1_,
- Operand(map_result_, scratch1_, times_2, FixedArray::kHeaderSize));
- // Calculate location of the first key name.
- __ add(Operand(map_result_),
- Immediate(FixedArray::kHeaderSize +
- DescriptorArray::kFirstIndex * kPointerSize));
- // Loop through all the keys in the descriptor array. If one of these is the
- // symbol valueOf the result is false.
- Label entry, loop;
- __ jmp(&entry);
- __ bind(&loop);
- __ mov(scratch2_, FieldOperand(map_result_, 0));
- __ cmp(scratch2_, FACTORY->value_of_symbol());
- __ j(equal, &false_result);
- __ add(Operand(map_result_), Immediate(kPointerSize));
- __ bind(&entry);
- __ cmp(map_result_, Operand(scratch1_));
- __ j(not_equal, &loop);
-
- // Reload map as register map_result_ was used as temporary above.
- __ mov(map_result_, FieldOperand(object_, HeapObject::kMapOffset));
-
- // If a valueOf property is not found on the object check that it's
- // prototype is the un-modified String prototype. If not result is false.
- __ mov(scratch1_, FieldOperand(map_result_, Map::kPrototypeOffset));
- __ test(scratch1_, Immediate(kSmiTagMask));
- __ j(zero, &false_result);
- __ mov(scratch1_, FieldOperand(scratch1_, HeapObject::kMapOffset));
- __ mov(scratch2_, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
- __ mov(scratch2_,
- FieldOperand(scratch2_, GlobalObject::kGlobalContextOffset));
- __ cmp(scratch1_,
- ContextOperand(scratch2_,
- Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX));
- __ j(not_equal, &false_result);
- // Set the bit in the map to indicate that it has been checked safe for
- // default valueOf and set true result.
- __ or_(FieldOperand(map_result_, Map::kBitField2Offset),
- Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf));
- __ Set(map_result_, Immediate(1));
- __ jmp(exit_label());
- __ bind(&false_result);
- // Set false result.
- __ Set(map_result_, Immediate(0));
- }
-
- private:
- Register object_;
- Register map_result_;
- Register scratch1_;
- Register scratch2_;
-};
-
-
-void CodeGenerator::GenerateIsStringWrapperSafeForDefaultValueOf(
- ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result obj = frame_->Pop(); // Pop the string wrapper.
- obj.ToRegister();
- ASSERT(obj.is_valid());
- if (FLAG_debug_code) {
- __ AbortIfSmi(obj.reg());
- }
-
- // Check whether this map has already been checked to be safe for default
- // valueOf.
- Result map_result = allocator()->Allocate();
- ASSERT(map_result.is_valid());
- __ mov(map_result.reg(), FieldOperand(obj.reg(), HeapObject::kMapOffset));
- __ test_b(FieldOperand(map_result.reg(), Map::kBitField2Offset),
- 1 << Map::kStringWrapperSafeForDefaultValueOf);
- destination()->true_target()->Branch(not_zero);
-
- // We need an additional two scratch registers for the deferred code.
- Result temp1 = allocator()->Allocate();
- ASSERT(temp1.is_valid());
- Result temp2 = allocator()->Allocate();
- ASSERT(temp2.is_valid());
-
- DeferredIsStringWrapperSafeForDefaultValueOf* deferred =
- new DeferredIsStringWrapperSafeForDefaultValueOf(
- obj.reg(), map_result.reg(), temp1.reg(), temp2.reg());
- deferred->Branch(zero);
- deferred->BindExit();
- __ test(map_result.reg(), Operand(map_result.reg()));
- obj.Unuse();
- map_result.Unuse();
- temp1.Unuse();
- temp2.Unuse();
- destination()->Split(not_equal);
-}
-
-
-void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
- // This generates a fast version of:
- // (%_ClassOf(arg) === 'Function')
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result obj = frame_->Pop();
- obj.ToRegister();
- __ test(obj.reg(), Immediate(kSmiTagMask));
- destination()->false_target()->Branch(zero);
- Result temp = allocator()->Allocate();
- ASSERT(temp.is_valid());
- __ CmpObjectType(obj.reg(), JS_FUNCTION_TYPE, temp.reg());
- obj.Unuse();
- temp.Unuse();
- destination()->Split(equal);
-}
-
-
-void CodeGenerator::GenerateIsUndetectableObject(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result obj = frame_->Pop();
- obj.ToRegister();
- __ test(obj.reg(), Immediate(kSmiTagMask));
- destination()->false_target()->Branch(zero);
- Result temp = allocator()->Allocate();
- ASSERT(temp.is_valid());
- __ mov(temp.reg(),
- FieldOperand(obj.reg(), HeapObject::kMapOffset));
- __ test_b(FieldOperand(temp.reg(), Map::kBitFieldOffset),
- 1 << Map::kIsUndetectable);
- obj.Unuse();
- temp.Unuse();
- destination()->Split(not_zero);
-}
-
-
-void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 0);
-
- // Get the frame pointer for the calling frame.
- Result fp = allocator()->Allocate();
- __ mov(fp.reg(), Operand(ebp, StandardFrameConstants::kCallerFPOffset));
-
- // Skip the arguments adaptor frame if it exists.
- Label check_frame_marker;
- __ cmp(Operand(fp.reg(), StandardFrameConstants::kContextOffset),
- Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
- __ j(not_equal, &check_frame_marker);
- __ mov(fp.reg(), Operand(fp.reg(), StandardFrameConstants::kCallerFPOffset));
-
- // Check the marker in the calling frame.
- __ bind(&check_frame_marker);
- __ cmp(Operand(fp.reg(), StandardFrameConstants::kMarkerOffset),
- Immediate(Smi::FromInt(StackFrame::CONSTRUCT)));
- fp.Unuse();
- destination()->Split(equal);
-}
-
-
-void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 0);
-
- Result fp = allocator_->Allocate();
- Result result = allocator_->Allocate();
- ASSERT(fp.is_valid() && result.is_valid());
-
- Label exit;
-
- // Get the number of formal parameters.
- __ Set(result.reg(), Immediate(Smi::FromInt(scope()->num_parameters())));
-
- // Check if the calling frame is an arguments adaptor frame.
- __ mov(fp.reg(), Operand(ebp, StandardFrameConstants::kCallerFPOffset));
- __ cmp(Operand(fp.reg(), StandardFrameConstants::kContextOffset),
- Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
- __ j(not_equal, &exit);
-
- // Arguments adaptor case: Read the arguments length from the
- // adaptor frame.
- __ mov(result.reg(),
- Operand(fp.reg(), ArgumentsAdaptorFrameConstants::kLengthOffset));
-
- __ bind(&exit);
- result.set_type_info(TypeInfo::Smi());
- if (FLAG_debug_code) __ AbortIfNotSmi(result.reg());
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::GenerateClassOf(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- JumpTarget leave, null, function, non_function_constructor;
- Load(args->at(0)); // Load the object.
- Result obj = frame_->Pop();
- obj.ToRegister();
- frame_->Spill(obj.reg());
-
- // If the object is a smi, we return null.
- __ test(obj.reg(), Immediate(kSmiTagMask));
- null.Branch(zero);
-
- // Check that the object is a JS object but take special care of JS
- // functions to make sure they have 'Function' as their class.
- __ CmpObjectType(obj.reg(), FIRST_JS_OBJECT_TYPE, obj.reg());
- null.Branch(below);
-
- // As long as JS_FUNCTION_TYPE is the last instance type and it is
- // right after LAST_JS_OBJECT_TYPE, we can avoid checking for
- // LAST_JS_OBJECT_TYPE.
- STATIC_ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
- STATIC_ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
- __ CmpInstanceType(obj.reg(), JS_FUNCTION_TYPE);
- function.Branch(equal);
-
- // Check if the constructor in the map is a function.
- { Result tmp = allocator()->Allocate();
- __ mov(obj.reg(), FieldOperand(obj.reg(), Map::kConstructorOffset));
- __ CmpObjectType(obj.reg(), JS_FUNCTION_TYPE, tmp.reg());
- non_function_constructor.Branch(not_equal);
- }
-
- // The map register now contains the constructor function. Grab the
- // instance class name from there.
- __ mov(obj.reg(),
- FieldOperand(obj.reg(), JSFunction::kSharedFunctionInfoOffset));
- __ mov(obj.reg(),
- FieldOperand(obj.reg(), SharedFunctionInfo::kInstanceClassNameOffset));
- frame_->Push(&obj);
- leave.Jump();
-
- // Functions have class 'Function'.
- function.Bind();
- frame_->Push(FACTORY->function_class_symbol());
- leave.Jump();
-
- // Objects with a non-function constructor have class 'Object'.
- non_function_constructor.Bind();
- frame_->Push(FACTORY->Object_symbol());
- leave.Jump();
-
- // Non-JS objects have class null.
- null.Bind();
- frame_->Push(FACTORY->null_value());
-
- // All done.
- leave.Bind();
-}
-
-
-void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- JumpTarget leave;
- Load(args->at(0)); // Load the object.
- frame_->Dup();
- Result object = frame_->Pop();
- object.ToRegister();
- ASSERT(object.is_valid());
- // if (object->IsSmi()) return object.
- __ test(object.reg(), Immediate(kSmiTagMask));
- leave.Branch(zero, taken);
- // It is a heap object - get map.
- Result temp = allocator()->Allocate();
- ASSERT(temp.is_valid());
- // if (!object->IsJSValue()) return object.
- __ CmpObjectType(object.reg(), JS_VALUE_TYPE, temp.reg());
- leave.Branch(not_equal, not_taken);
- __ mov(temp.reg(), FieldOperand(object.reg(), JSValue::kValueOffset));
- object.Unuse();
- frame_->SetElementAt(0, &temp);
- leave.Bind();
-}
-
-
-void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 2);
- JumpTarget leave;
- Load(args->at(0)); // Load the object.
- Load(args->at(1)); // Load the value.
- Result value = frame_->Pop();
- Result object = frame_->Pop();
- value.ToRegister();
- object.ToRegister();
-
- // if (object->IsSmi()) return value.
- __ test(object.reg(), Immediate(kSmiTagMask));
- leave.Branch(zero, &value, taken);
-
- // It is a heap object - get its map.
- Result scratch = allocator_->Allocate();
- ASSERT(scratch.is_valid());
- // if (!object->IsJSValue()) return value.
- __ CmpObjectType(object.reg(), JS_VALUE_TYPE, scratch.reg());
- leave.Branch(not_equal, &value, not_taken);
-
- // Store the value.
- __ mov(FieldOperand(object.reg(), JSValue::kValueOffset), value.reg());
- // Update the write barrier. Save the value as it will be
- // overwritten by the write barrier code and is needed afterward.
- Result duplicate_value = allocator_->Allocate();
- ASSERT(duplicate_value.is_valid());
- __ mov(duplicate_value.reg(), value.reg());
- // The object register is also overwritten by the write barrier and
- // possibly aliased in the frame.
- frame_->Spill(object.reg());
- __ RecordWrite(object.reg(), JSValue::kValueOffset, duplicate_value.reg(),
- scratch.reg());
- object.Unuse();
- scratch.Unuse();
- duplicate_value.Unuse();
-
- // Leave.
- leave.Bind(&value);
- frame_->Push(&value);
-}
-
-
-void CodeGenerator::GenerateArguments(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
-
- // ArgumentsAccessStub expects the key in edx and the formal
- // parameter count in eax.
- Load(args->at(0));
- Result key = frame_->Pop();
- // Explicitly create a constant result.
- Result count(Handle<Smi>(Smi::FromInt(scope()->num_parameters())));
- // Call the shared stub to get to arguments[key].
- ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
- Result result = frame_->CallStub(&stub, &key, &count);
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 2);
-
- // Load the two objects into registers and perform the comparison.
- Load(args->at(0));
- Load(args->at(1));
- Result right = frame_->Pop();
- Result left = frame_->Pop();
- right.ToRegister();
- left.ToRegister();
- __ cmp(right.reg(), Operand(left.reg()));
- right.Unuse();
- left.Unuse();
- destination()->Split(equal);
-}
-
-
-void CodeGenerator::GenerateGetFramePointer(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 0);
- STATIC_ASSERT(kSmiTag == 0); // EBP value is aligned, so it looks like a Smi.
- Result ebp_as_smi = allocator_->Allocate();
- ASSERT(ebp_as_smi.is_valid());
- __ mov(ebp_as_smi.reg(), Operand(ebp));
- frame_->Push(&ebp_as_smi);
-}
-
-
-void CodeGenerator::GenerateRandomHeapNumber(
- ZoneList<Expression*>* args) {
- ASSERT(args->length() == 0);
- frame_->SpillAll();
-
- Label slow_allocate_heapnumber;
- Label heapnumber_allocated;
-
- __ AllocateHeapNumber(edi, ebx, ecx, &slow_allocate_heapnumber);
- __ jmp(&heapnumber_allocated);
-
- __ bind(&slow_allocate_heapnumber);
- // Allocate a heap number.
- __ CallRuntime(Runtime::kNumberAlloc, 0);
- __ mov(edi, eax);
-
- __ bind(&heapnumber_allocated);
-
- __ PrepareCallCFunction(0, ebx);
- __ CallCFunction(ExternalReference::random_uint32_function(masm()->isolate()),
- 0);
-
- // Convert 32 random bits in eax to 0.(32 random bits) in a double
- // by computing:
- // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
- // This is implemented on both SSE2 and FPU.
- if (masm()->isolate()->cpu_features()->IsSupported(SSE2)) {
- CpuFeatures::Scope fscope(SSE2);
- __ mov(ebx, Immediate(0x49800000)); // 1.0 x 2^20 as single.
- __ movd(xmm1, Operand(ebx));
- __ movd(xmm0, Operand(eax));
- __ cvtss2sd(xmm1, xmm1);
- __ pxor(xmm0, xmm1);
- __ subsd(xmm0, xmm1);
- __ movdbl(FieldOperand(edi, HeapNumber::kValueOffset), xmm0);
- } else {
- // 0x4130000000000000 is 1.0 x 2^20 as a double.
- __ mov(FieldOperand(edi, HeapNumber::kExponentOffset),
- Immediate(0x41300000));
- __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), eax);
- __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset));
- __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), Immediate(0));
- __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset));
- __ fsubp(1);
- __ fstp_d(FieldOperand(edi, HeapNumber::kValueOffset));
- }
- __ mov(eax, edi);
-
- Result result = allocator_->Allocate(eax);
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
- ASSERT_EQ(2, args->length());
-
- Load(args->at(0));
- Load(args->at(1));
-
- StringAddStub stub(NO_STRING_ADD_FLAGS);
- Result answer = frame_->CallStub(&stub, 2);
- frame_->Push(&answer);
-}
-
-
-void CodeGenerator::GenerateSubString(ZoneList<Expression*>* args) {
- ASSERT_EQ(3, args->length());
-
- Load(args->at(0));
- Load(args->at(1));
- Load(args->at(2));
-
- SubStringStub stub;
- Result answer = frame_->CallStub(&stub, 3);
- frame_->Push(&answer);
-}
-
-
-void CodeGenerator::GenerateStringCompare(ZoneList<Expression*>* args) {
- ASSERT_EQ(2, args->length());
-
- Load(args->at(0));
- Load(args->at(1));
-
- StringCompareStub stub;
- Result answer = frame_->CallStub(&stub, 2);
- frame_->Push(&answer);
-}
-
-
-void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
- ASSERT_EQ(4, args->length());
-
- // Load the arguments on the stack and call the stub.
- Load(args->at(0));
- Load(args->at(1));
- Load(args->at(2));
- Load(args->at(3));
-
- RegExpExecStub stub;
- Result result = frame_->CallStub(&stub, 4);
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
- ASSERT_EQ(3, args->length());
-
- Load(args->at(0)); // Size of array, smi.
- Load(args->at(1)); // "index" property value.
- Load(args->at(2)); // "input" property value.
-
- RegExpConstructResultStub stub;
- Result result = frame_->CallStub(&stub, 3);
- frame_->Push(&result);
-}
-
-
-class DeferredSearchCache: public DeferredCode {
- public:
- DeferredSearchCache(Register dst, Register cache, Register key)
- : dst_(dst), cache_(cache), key_(key) {
- set_comment("[ DeferredSearchCache");
- }
-
- virtual void Generate();
-
- private:
- Register dst_; // on invocation Smi index of finger, on exit
- // holds value being looked up.
- Register cache_; // instance of JSFunctionResultCache.
- Register key_; // key being looked up.
-};
-
-
-void DeferredSearchCache::Generate() {
- Label first_loop, search_further, second_loop, cache_miss;
-
- // Smi-tagging is equivalent to multiplying by 2.
- STATIC_ASSERT(kSmiTag == 0);
- STATIC_ASSERT(kSmiTagSize == 1);
-
- Smi* kEntrySizeSmi = Smi::FromInt(JSFunctionResultCache::kEntrySize);
- Smi* kEntriesIndexSmi = Smi::FromInt(JSFunctionResultCache::kEntriesIndex);
-
- // Check the cache from finger to start of the cache.
- __ bind(&first_loop);
- __ sub(Operand(dst_), Immediate(kEntrySizeSmi));
- __ cmp(Operand(dst_), Immediate(kEntriesIndexSmi));
- __ j(less, &search_further);
-
- __ cmp(key_, CodeGenerator::FixedArrayElementOperand(cache_, dst_));
- __ j(not_equal, &first_loop);
-
- __ mov(FieldOperand(cache_, JSFunctionResultCache::kFingerOffset), dst_);
- __ mov(dst_, CodeGenerator::FixedArrayElementOperand(cache_, dst_, 1));
- __ jmp(exit_label());
-
- __ bind(&search_further);
-
- // Check the cache from end of cache up to finger.
- __ mov(dst_, FieldOperand(cache_, JSFunctionResultCache::kCacheSizeOffset));
-
- __ bind(&second_loop);
- __ sub(Operand(dst_), Immediate(kEntrySizeSmi));
- // Consider prefetching into some reg.
- __ cmp(dst_, FieldOperand(cache_, JSFunctionResultCache::kFingerOffset));
- __ j(less_equal, &cache_miss);
-
- __ cmp(key_, CodeGenerator::FixedArrayElementOperand(cache_, dst_));
- __ j(not_equal, &second_loop);
-
- __ mov(FieldOperand(cache_, JSFunctionResultCache::kFingerOffset), dst_);
- __ mov(dst_, CodeGenerator::FixedArrayElementOperand(cache_, dst_, 1));
- __ jmp(exit_label());
-
- __ bind(&cache_miss);
- __ push(cache_); // store a reference to cache
- __ push(key_); // store a key
- __ push(Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
- __ push(key_);
- // On ia32 function must be in edi.
- __ mov(edi, FieldOperand(cache_, JSFunctionResultCache::kFactoryOffset));
- ParameterCount expected(1);
- __ InvokeFunction(edi, expected, CALL_FUNCTION);
-
- // Find a place to put new cached value into.
- Label add_new_entry, update_cache;
- __ mov(ecx, Operand(esp, kPointerSize)); // restore the cache
- // Possible optimization: cache size is constant for the given cache
- // so technically we could use a constant here. However, if we have
- // cache miss this optimization would hardly matter much.
-
- // Check if we could add new entry to cache.
- __ mov(ebx, FieldOperand(ecx, FixedArray::kLengthOffset));
- __ cmp(ebx, FieldOperand(ecx, JSFunctionResultCache::kCacheSizeOffset));
- __ j(greater, &add_new_entry);
-
- // Check if we could evict entry after finger.
- __ mov(edx, FieldOperand(ecx, JSFunctionResultCache::kFingerOffset));
- __ add(Operand(edx), Immediate(kEntrySizeSmi));
- __ cmp(ebx, Operand(edx));
- __ j(greater, &update_cache);
-
- // Need to wrap over the cache.
- __ mov(edx, Immediate(kEntriesIndexSmi));
- __ jmp(&update_cache);
-
- __ bind(&add_new_entry);
- __ mov(edx, FieldOperand(ecx, JSFunctionResultCache::kCacheSizeOffset));
- __ lea(ebx, Operand(edx, JSFunctionResultCache::kEntrySize << 1));
- __ mov(FieldOperand(ecx, JSFunctionResultCache::kCacheSizeOffset), ebx);
-
- // Update the cache itself.
- // edx holds the index.
- __ bind(&update_cache);
- __ pop(ebx); // restore the key
- __ mov(FieldOperand(ecx, JSFunctionResultCache::kFingerOffset), edx);
- // Store key.
- __ mov(CodeGenerator::FixedArrayElementOperand(ecx, edx), ebx);
- __ RecordWrite(ecx, 0, ebx, edx);
-
- // Store value.
- __ pop(ecx); // restore the cache.
- __ mov(edx, FieldOperand(ecx, JSFunctionResultCache::kFingerOffset));
- __ add(Operand(edx), Immediate(Smi::FromInt(1)));
- __ mov(ebx, eax);
- __ mov(CodeGenerator::FixedArrayElementOperand(ecx, edx), ebx);
- __ RecordWrite(ecx, 0, ebx, edx);
-
- if (!dst_.is(eax)) {
- __ mov(dst_, eax);
- }
-}
-
-
-void CodeGenerator::GenerateGetFromCache(ZoneList<Expression*>* args) {
- ASSERT_EQ(2, args->length());
-
- ASSERT_NE(NULL, args->at(0)->AsLiteral());
- int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
-
- Handle<FixedArray> jsfunction_result_caches(
- masm()->isolate()->global_context()->jsfunction_result_caches());
- if (jsfunction_result_caches->length() <= cache_id) {
- __ Abort("Attempt to use undefined cache.");
- frame_->Push(FACTORY->undefined_value());
- return;
- }
-
- Load(args->at(1));
- Result key = frame_->Pop();
- key.ToRegister();
-
- Result cache = allocator()->Allocate();
- ASSERT(cache.is_valid());
- __ mov(cache.reg(), ContextOperand(esi, Context::GLOBAL_INDEX));
- __ mov(cache.reg(),
- FieldOperand(cache.reg(), GlobalObject::kGlobalContextOffset));
- __ mov(cache.reg(),
- ContextOperand(cache.reg(), Context::JSFUNCTION_RESULT_CACHES_INDEX));
- __ mov(cache.reg(),
- FieldOperand(cache.reg(), FixedArray::OffsetOfElementAt(cache_id)));
-
- Result tmp = allocator()->Allocate();
- ASSERT(tmp.is_valid());
-
- DeferredSearchCache* deferred = new DeferredSearchCache(tmp.reg(),
- cache.reg(),
- key.reg());
-
- // tmp.reg() now holds finger offset as a smi.
- STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
- __ mov(tmp.reg(), FieldOperand(cache.reg(),
- JSFunctionResultCache::kFingerOffset));
- __ cmp(key.reg(), FixedArrayElementOperand(cache.reg(), tmp.reg()));
- deferred->Branch(not_equal);
-
- __ mov(tmp.reg(), FixedArrayElementOperand(cache.reg(), tmp.reg(), 1));
-
- deferred->BindExit();
- frame_->Push(&tmp);
-}
-
-
-void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
- ASSERT_EQ(args->length(), 1);
-
- // Load the argument on the stack and call the stub.
- Load(args->at(0));
- NumberToStringStub stub;
- Result result = frame_->CallStub(&stub, 1);
- frame_->Push(&result);
-}
-
-
-class DeferredSwapElements: public DeferredCode {
- public:
- DeferredSwapElements(Register object, Register index1, Register index2)
- : object_(object), index1_(index1), index2_(index2) {
- set_comment("[ DeferredSwapElements");
- }
-
- virtual void Generate();
-
- private:
- Register object_, index1_, index2_;
-};
-
-
-void DeferredSwapElements::Generate() {
- __ push(object_);
- __ push(index1_);
- __ push(index2_);
- __ CallRuntime(Runtime::kSwapElements, 3);
-}
-
-
-void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) {
- // Note: this code assumes that indices are passed are within
- // elements' bounds and refer to valid (not holes) values.
- Comment cmnt(masm_, "[ GenerateSwapElements");
-
- ASSERT_EQ(3, args->length());
-
- Load(args->at(0));
- Load(args->at(1));
- Load(args->at(2));
-
- Result index2 = frame_->Pop();
- index2.ToRegister();
-
- Result index1 = frame_->Pop();
- index1.ToRegister();
-
- Result object = frame_->Pop();
- object.ToRegister();
-
- Result tmp1 = allocator()->Allocate();
- tmp1.ToRegister();
- Result tmp2 = allocator()->Allocate();
- tmp2.ToRegister();
-
- frame_->Spill(object.reg());
- frame_->Spill(index1.reg());
- frame_->Spill(index2.reg());
-
- DeferredSwapElements* deferred = new DeferredSwapElements(object.reg(),
- index1.reg(),
- index2.reg());
-
- // Fetch the map and check if array is in fast case.
- // Check that object doesn't require security checks and
- // has no indexed interceptor.
- __ CmpObjectType(object.reg(), FIRST_JS_OBJECT_TYPE, tmp1.reg());
- deferred->Branch(below);
- __ test_b(FieldOperand(tmp1.reg(), Map::kBitFieldOffset),
- KeyedLoadIC::kSlowCaseBitFieldMask);
- deferred->Branch(not_zero);
-
- // Check the object's elements are in fast case and writable.
- __ mov(tmp1.reg(), FieldOperand(object.reg(), JSObject::kElementsOffset));
- __ cmp(FieldOperand(tmp1.reg(), HeapObject::kMapOffset),
- Immediate(FACTORY->fixed_array_map()));
- deferred->Branch(not_equal);
-
- // Smi-tagging is equivalent to multiplying by 2.
- STATIC_ASSERT(kSmiTag == 0);
- STATIC_ASSERT(kSmiTagSize == 1);
-
- // Check that both indices are smis.
- __ mov(tmp2.reg(), index1.reg());
- __ or_(tmp2.reg(), Operand(index2.reg()));
- __ test(tmp2.reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
-
- // Check that both indices are valid.
- __ mov(tmp2.reg(), FieldOperand(object.reg(), JSArray::kLengthOffset));
- __ cmp(tmp2.reg(), Operand(index1.reg()));
- deferred->Branch(below_equal);
- __ cmp(tmp2.reg(), Operand(index2.reg()));
- deferred->Branch(below_equal);
-
- // Bring addresses into index1 and index2.
- __ lea(index1.reg(), FixedArrayElementOperand(tmp1.reg(), index1.reg()));
- __ lea(index2.reg(), FixedArrayElementOperand(tmp1.reg(), index2.reg()));
-
- // Swap elements.
- __ mov(object.reg(), Operand(index1.reg(), 0));
- __ mov(tmp2.reg(), Operand(index2.reg(), 0));
- __ mov(Operand(index2.reg(), 0), object.reg());
- __ mov(Operand(index1.reg(), 0), tmp2.reg());
-
- Label done;
- __ InNewSpace(tmp1.reg(), tmp2.reg(), equal, &done);
- // Possible optimization: do a check that both values are Smis
- // (or them and test against Smi mask.)
-
- __ mov(tmp2.reg(), tmp1.reg());
- __ RecordWriteHelper(tmp2.reg(), index1.reg(), object.reg());
- __ RecordWriteHelper(tmp1.reg(), index2.reg(), object.reg());
- __ bind(&done);
-
- deferred->BindExit();
- frame_->Push(FACTORY->undefined_value());
-}
-
-
-void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) {
- Comment cmnt(masm_, "[ GenerateCallFunction");
-
- ASSERT(args->length() >= 2);
-
- int n_args = args->length() - 2; // for receiver and function.
- Load(args->at(0)); // receiver
- for (int i = 0; i < n_args; i++) {
- Load(args->at(i + 1));
- }
- Load(args->at(n_args + 1)); // function
- Result result = frame_->CallJSFunction(n_args);
- frame_->Push(&result);
-}
-
-
-// Generates the Math.pow method. Only handles special cases and
-// branches to the runtime system for everything else. Please note
-// that this function assumes that the callsite has executed ToNumber
-// on both arguments.
-void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 2);
- Load(args->at(0));
- Load(args->at(1));
- if (!masm()->isolate()->cpu_features()->IsSupported(SSE2)) {
- Result res = frame_->CallRuntime(Runtime::kMath_pow, 2);
- frame_->Push(&res);
- } else {
- CpuFeatures::Scope use_sse2(SSE2);
- Label allocate_return;
- // Load the two operands while leaving the values on the frame.
- frame()->Dup();
- Result exponent = frame()->Pop();
- exponent.ToRegister();
- frame()->Spill(exponent.reg());
- frame()->PushElementAt(1);
- Result base = frame()->Pop();
- base.ToRegister();
- frame()->Spill(base.reg());
-
- Result answer = allocator()->Allocate();
- ASSERT(answer.is_valid());
- ASSERT(!exponent.reg().is(base.reg()));
- JumpTarget call_runtime;
-
- // Save 1 in xmm3 - we need this several times later on.
- __ mov(answer.reg(), Immediate(1));
- __ cvtsi2sd(xmm3, Operand(answer.reg()));
-
- Label exponent_nonsmi;
- Label base_nonsmi;
- // If the exponent is a heap number go to that specific case.
- __ test(exponent.reg(), Immediate(kSmiTagMask));
- __ j(not_zero, &exponent_nonsmi);
- __ test(base.reg(), Immediate(kSmiTagMask));
- __ j(not_zero, &base_nonsmi);
-
- // Optimized version when y is an integer.
- Label powi;
- __ SmiUntag(base.reg());
- __ cvtsi2sd(xmm0, Operand(base.reg()));
- __ jmp(&powi);
- // exponent is smi and base is a heapnumber.
- __ bind(&base_nonsmi);
- __ cmp(FieldOperand(base.reg(), HeapObject::kMapOffset),
- FACTORY->heap_number_map());
- call_runtime.Branch(not_equal);
-
- __ movdbl(xmm0, FieldOperand(base.reg(), HeapNumber::kValueOffset));
-
- // Optimized version of pow if y is an integer.
- __ bind(&powi);
- __ SmiUntag(exponent.reg());
-
- // Save exponent in base as we need to check if exponent is negative later.
- // We know that base and exponent are in different registers.
- __ mov(base.reg(), exponent.reg());
-
- // Get absolute value of exponent.
- Label no_neg;
- __ cmp(exponent.reg(), 0);
- __ j(greater_equal, &no_neg);
- __ neg(exponent.reg());
- __ bind(&no_neg);
-
- // Load xmm1 with 1.
- __ movsd(xmm1, xmm3);
- Label while_true;
- Label no_multiply;
-
- __ bind(&while_true);
- __ shr(exponent.reg(), 1);
- __ j(not_carry, &no_multiply);
- __ mulsd(xmm1, xmm0);
- __ bind(&no_multiply);
- __ test(exponent.reg(), Operand(exponent.reg()));
- __ mulsd(xmm0, xmm0);
- __ j(not_zero, &while_true);
-
- // x has the original value of y - if y is negative return 1/result.
- __ test(base.reg(), Operand(base.reg()));
- __ j(positive, &allocate_return);
- // Special case if xmm1 has reached infinity.
- __ mov(answer.reg(), Immediate(0x7FB00000));
- __ movd(xmm0, Operand(answer.reg()));
- __ cvtss2sd(xmm0, xmm0);
- __ ucomisd(xmm0, xmm1);
- call_runtime.Branch(equal);
- __ divsd(xmm3, xmm1);
- __ movsd(xmm1, xmm3);
- __ jmp(&allocate_return);
-
- // exponent (or both) is a heapnumber - no matter what we should now work
- // on doubles.
- __ bind(&exponent_nonsmi);
- __ cmp(FieldOperand(exponent.reg(), HeapObject::kMapOffset),
- FACTORY->heap_number_map());
- call_runtime.Branch(not_equal);
- __ movdbl(xmm1, FieldOperand(exponent.reg(), HeapNumber::kValueOffset));
- // Test if exponent is nan.
- __ ucomisd(xmm1, xmm1);
- call_runtime.Branch(parity_even);
-
- Label base_not_smi;
- Label handle_special_cases;
- __ test(base.reg(), Immediate(kSmiTagMask));
- __ j(not_zero, &base_not_smi);
- __ SmiUntag(base.reg());
- __ cvtsi2sd(xmm0, Operand(base.reg()));
- __ jmp(&handle_special_cases);
- __ bind(&base_not_smi);
- __ cmp(FieldOperand(base.reg(), HeapObject::kMapOffset),
- FACTORY->heap_number_map());
- call_runtime.Branch(not_equal);
- __ mov(answer.reg(), FieldOperand(base.reg(), HeapNumber::kExponentOffset));
- __ and_(answer.reg(), HeapNumber::kExponentMask);
- __ cmp(Operand(answer.reg()), Immediate(HeapNumber::kExponentMask));
- // base is NaN or +/-Infinity
- call_runtime.Branch(greater_equal);
- __ movdbl(xmm0, FieldOperand(base.reg(), HeapNumber::kValueOffset));
-
- // base is in xmm0 and exponent is in xmm1.
- __ bind(&handle_special_cases);
- Label not_minus_half;
- // Test for -0.5.
- // Load xmm2 with -0.5.
- __ mov(answer.reg(), Immediate(0xBF000000));
- __ movd(xmm2, Operand(answer.reg()));
- __ cvtss2sd(xmm2, xmm2);
- // xmm2 now has -0.5.
- __ ucomisd(xmm2, xmm1);
- __ j(not_equal, ¬_minus_half);
-
- // Calculates reciprocal of square root.
- // sqrtsd returns -0 when input is -0. ECMA spec requires +0.
- __ xorpd(xmm1, xmm1);
- __ addsd(xmm1, xmm0);
- __ sqrtsd(xmm1, xmm1);
- __ divsd(xmm3, xmm1);
- __ movsd(xmm1, xmm3);
- __ jmp(&allocate_return);
-
- // Test for 0.5.
- __ bind(¬_minus_half);
- // Load xmm2 with 0.5.
- // Since xmm3 is 1 and xmm2 is -0.5 this is simply xmm2 + xmm3.
- __ addsd(xmm2, xmm3);
- // xmm2 now has 0.5.
- __ ucomisd(xmm2, xmm1);
- call_runtime.Branch(not_equal);
- // Calculates square root.
- // sqrtsd returns -0 when input is -0. ECMA spec requires +0.
- __ xorpd(xmm1, xmm1);
- __ addsd(xmm1, xmm0);
- __ sqrtsd(xmm1, xmm1);
-
- JumpTarget done;
- Label failure, success;
- __ bind(&allocate_return);
- // Make a copy of the frame to enable us to handle allocation
- // failure after the JumpTarget jump.
- VirtualFrame* clone = new VirtualFrame(frame());
- __ AllocateHeapNumber(answer.reg(), exponent.reg(),
- base.reg(), &failure);
- __ movdbl(FieldOperand(answer.reg(), HeapNumber::kValueOffset), xmm1);
- // Remove the two original values from the frame - we only need those
- // in the case where we branch to runtime.
- frame()->Drop(2);
- exponent.Unuse();
- base.Unuse();
- done.Jump(&answer);
- // Use the copy of the original frame as our current frame.
- RegisterFile empty_regs;
- SetFrame(clone, &empty_regs);
- // If we experience an allocation failure we branch to runtime.
- __ bind(&failure);
- call_runtime.Bind();
- answer = frame()->CallRuntime(Runtime::kMath_pow_cfunction, 2);
-
- done.Bind(&answer);
- frame()->Push(&answer);
- }
-}
-
-
-void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
- ASSERT_EQ(args->length(), 1);
- Load(args->at(0));
- TranscendentalCacheStub stub(TranscendentalCache::SIN,
- TranscendentalCacheStub::TAGGED);
- Result result = frame_->CallStub(&stub, 1);
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
- ASSERT_EQ(args->length(), 1);
- Load(args->at(0));
- TranscendentalCacheStub stub(TranscendentalCache::COS,
- TranscendentalCacheStub::TAGGED);
- Result result = frame_->CallStub(&stub, 1);
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::GenerateMathLog(ZoneList<Expression*>* args) {
- ASSERT_EQ(args->length(), 1);
- Load(args->at(0));
- TranscendentalCacheStub stub(TranscendentalCache::LOG,
- TranscendentalCacheStub::TAGGED);
- Result result = frame_->CallStub(&stub, 1);
- frame_->Push(&result);
-}
-
-
-// Generates the Math.sqrt method. Please note - this function assumes that
-// the callsite has executed ToNumber on the argument.
-void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
- ASSERT_EQ(args->length(), 1);
- Load(args->at(0));
-
- if (!masm()->isolate()->cpu_features()->IsSupported(SSE2)) {
- Result result = frame()->CallRuntime(Runtime::kMath_sqrt, 1);
- frame()->Push(&result);
- } else {
- CpuFeatures::Scope use_sse2(SSE2);
- // Leave original value on the frame if we need to call runtime.
- frame()->Dup();
- Result result = frame()->Pop();
- result.ToRegister();
- frame()->Spill(result.reg());
- Label runtime;
- Label non_smi;
- Label load_done;
- JumpTarget end;
-
- __ test(result.reg(), Immediate(kSmiTagMask));
- __ j(not_zero, &non_smi);
- __ SmiUntag(result.reg());
- __ cvtsi2sd(xmm0, Operand(result.reg()));
- __ jmp(&load_done);
- __ bind(&non_smi);
- __ cmp(FieldOperand(result.reg(), HeapObject::kMapOffset),
- FACTORY->heap_number_map());
- __ j(not_equal, &runtime);
- __ movdbl(xmm0, FieldOperand(result.reg(), HeapNumber::kValueOffset));
-
- __ bind(&load_done);
- __ sqrtsd(xmm0, xmm0);
- // A copy of the virtual frame to allow us to go to runtime after the
- // JumpTarget jump.
- Result scratch = allocator()->Allocate();
- VirtualFrame* clone = new VirtualFrame(frame());
- __ AllocateHeapNumber(result.reg(), scratch.reg(), no_reg, &runtime);
-
- __ movdbl(FieldOperand(result.reg(), HeapNumber::kValueOffset), xmm0);
- frame()->Drop(1);
- scratch.Unuse();
- end.Jump(&result);
- // We only branch to runtime if we have an allocation error.
- // Use the copy of the original frame as our current frame.
- RegisterFile empty_regs;
- SetFrame(clone, &empty_regs);
- __ bind(&runtime);
- result = frame()->CallRuntime(Runtime::kMath_sqrt, 1);
-
- end.Bind(&result);
- frame()->Push(&result);
- }
-}
-
-
-void CodeGenerator::GenerateIsRegExpEquivalent(ZoneList<Expression*>* args) {
- ASSERT_EQ(2, args->length());
- Load(args->at(0));
- Load(args->at(1));
- Result right_res = frame_->Pop();
- Result left_res = frame_->Pop();
- right_res.ToRegister();
- left_res.ToRegister();
- Result tmp_res = allocator()->Allocate();
- ASSERT(tmp_res.is_valid());
- Register right = right_res.reg();
- Register left = left_res.reg();
- Register tmp = tmp_res.reg();
- right_res.Unuse();
- left_res.Unuse();
- tmp_res.Unuse();
- __ cmp(left, Operand(right));
- destination()->true_target()->Branch(equal);
- // Fail if either is a non-HeapObject.
- __ mov(tmp, left);
- __ and_(Operand(tmp), right);
- __ test(Operand(tmp), Immediate(kSmiTagMask));
- destination()->false_target()->Branch(equal);
- __ CmpObjectType(left, JS_REGEXP_TYPE, tmp);
- destination()->false_target()->Branch(not_equal);
- __ cmp(tmp, FieldOperand(right, HeapObject::kMapOffset));
- destination()->false_target()->Branch(not_equal);
- __ mov(tmp, FieldOperand(left, JSRegExp::kDataOffset));
- __ cmp(tmp, FieldOperand(right, JSRegExp::kDataOffset));
- destination()->Split(equal);
-}
-
-
-void CodeGenerator::GenerateHasCachedArrayIndex(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result value = frame_->Pop();
- value.ToRegister();
- ASSERT(value.is_valid());
- if (FLAG_debug_code) {
- __ AbortIfNotString(value.reg());
- }
-
- __ test(FieldOperand(value.reg(), String::kHashFieldOffset),
- Immediate(String::kContainsCachedArrayIndexMask));
-
- value.Unuse();
- destination()->Split(zero);
-}
-
-
-void CodeGenerator::GenerateGetCachedArrayIndex(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result string = frame_->Pop();
- string.ToRegister();
- if (FLAG_debug_code) {
- __ AbortIfNotString(string.reg());
- }
-
- Result number = allocator()->Allocate();
- ASSERT(number.is_valid());
- __ mov(number.reg(), FieldOperand(string.reg(), String::kHashFieldOffset));
- __ IndexFromHash(number.reg(), number.reg());
- string.Unuse();
- frame_->Push(&number);
-}
-
-
-void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
- ASSERT(!in_safe_int32_mode());
- if (CheckForInlineRuntimeCall(node)) {
- return;
- }
-
- ZoneList<Expression*>* args = node->arguments();
- Comment cmnt(masm_, "[ CallRuntime");
- const Runtime::Function* function = node->function();
-
- if (function == NULL) {
- // Push the builtins object found in the current global object.
- Result temp = allocator()->Allocate();
- ASSERT(temp.is_valid());
- __ mov(temp.reg(), GlobalObjectOperand());
- __ mov(temp.reg(), FieldOperand(temp.reg(), GlobalObject::kBuiltinsOffset));
- frame_->Push(&temp);
- }
-
- // Push the arguments ("left-to-right").
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- }
-
- if (function == NULL) {
- // Call the JS runtime function.
- frame_->Push(node->name());
- Result answer = frame_->CallCallIC(RelocInfo::CODE_TARGET,
- arg_count,
- loop_nesting_);
- frame_->RestoreContextRegister();
- frame_->Push(&answer);
- } else {
- // Call the C runtime function.
- Result answer = frame_->CallRuntime(function, arg_count);
- frame_->Push(&answer);
- }
-}
-
-
-void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
- Comment cmnt(masm_, "[ UnaryOperation");
-
- Token::Value op = node->op();
-
- if (op == Token::NOT) {
- // Swap the true and false targets but keep the same actual label
- // as the fall through.
- destination()->Invert();
- LoadCondition(node->expression(), destination(), true);
- // Swap the labels back.
- destination()->Invert();
-
- } else if (op == Token::DELETE) {
- Property* property = node->expression()->AsProperty();
- if (property != NULL) {
- Load(property->obj());
- Load(property->key());
- frame_->Push(Smi::FromInt(strict_mode_flag()));
- Result answer = frame_->InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION, 3);
- frame_->Push(&answer);
- return;
- }
-
- Variable* variable = node->expression()->AsVariableProxy()->AsVariable();
- if (variable != NULL) {
- // Delete of an unqualified identifier is disallowed in strict mode
- // but "delete this" is.
- ASSERT(strict_mode_flag() == kNonStrictMode || variable->is_this());
- Slot* slot = variable->AsSlot();
- if (variable->is_global()) {
- LoadGlobal();
- frame_->Push(variable->name());
- frame_->Push(Smi::FromInt(kNonStrictMode));
- Result answer = frame_->InvokeBuiltin(Builtins::DELETE,
- CALL_FUNCTION, 3);
- frame_->Push(&answer);
-
- } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
- // Call the runtime to delete from the context holding the named
- // variable. Sync the virtual frame eagerly so we can push the
- // arguments directly into place.
- frame_->SyncRange(0, frame_->element_count() - 1);
- frame_->EmitPush(esi);
- frame_->EmitPush(Immediate(variable->name()));
- Result answer = frame_->CallRuntime(Runtime::kDeleteContextSlot, 2);
- frame_->Push(&answer);
- } else {
- // Default: Result of deleting non-global, not dynamically
- // introduced variables is false.
- frame_->Push(FACTORY->false_value());
- }
- } else {
- // Default: Result of deleting expressions is true.
- Load(node->expression()); // may have side-effects
- frame_->SetElementAt(0, FACTORY->true_value());
- }
-
- } else if (op == Token::TYPEOF) {
- // Special case for loading the typeof expression; see comment on
- // LoadTypeofExpression().
- LoadTypeofExpression(node->expression());
- Result answer = frame_->CallRuntime(Runtime::kTypeof, 1);
- frame_->Push(&answer);
-
- } else if (op == Token::VOID) {
- Expression* expression = node->expression();
- if (expression && expression->AsLiteral() && (
- expression->AsLiteral()->IsTrue() ||
- expression->AsLiteral()->IsFalse() ||
- expression->AsLiteral()->handle()->IsNumber() ||
- expression->AsLiteral()->handle()->IsString() ||
- expression->AsLiteral()->handle()->IsJSRegExp() ||
- expression->AsLiteral()->IsNull())) {
- // Omit evaluating the value of the primitive literal.
- // It will be discarded anyway, and can have no side effect.
- frame_->Push(FACTORY->undefined_value());
- } else {
- Load(node->expression());
- frame_->SetElementAt(0, FACTORY->undefined_value());
- }
-
- } else {
- if (in_safe_int32_mode()) {
- Visit(node->expression());
- Result value = frame_->Pop();
- ASSERT(value.is_untagged_int32());
- // Registers containing an int32 value are not multiply used.
- ASSERT(!value.is_register() || !frame_->is_used(value.reg()));
- value.ToRegister();
- switch (op) {
- case Token::SUB: {
- __ neg(value.reg());
- frame_->Push(&value);
- if (node->no_negative_zero()) {
- // -MIN_INT is MIN_INT with the overflow flag set.
- unsafe_bailout_->Branch(overflow);
- } else {
- // MIN_INT and 0 both have bad negations. They both have 31 zeros.
- __ test(value.reg(), Immediate(0x7FFFFFFF));
- unsafe_bailout_->Branch(zero);
- }
- break;
- }
- case Token::BIT_NOT: {
- __ not_(value.reg());
- frame_->Push(&value);
- break;
- }
- case Token::ADD: {
- // Unary plus has no effect on int32 values.
- frame_->Push(&value);
- break;
- }
- default:
- UNREACHABLE();
- break;
- }
- } else {
- Load(node->expression());
- bool can_overwrite = node->expression()->ResultOverwriteAllowed();
- UnaryOverwriteMode overwrite =
- can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
- bool no_negative_zero = node->expression()->no_negative_zero();
- switch (op) {
- case Token::NOT:
- case Token::DELETE:
- case Token::TYPEOF:
- UNREACHABLE(); // handled above
- break;
-
- case Token::SUB: {
- GenericUnaryOpStub stub(
- Token::SUB,
- overwrite,
- NO_UNARY_FLAGS,
- no_negative_zero ? kIgnoreNegativeZero : kStrictNegativeZero);
- Result operand = frame_->Pop();
- Result answer = frame_->CallStub(&stub, &operand);
- answer.set_type_info(TypeInfo::Number());
- frame_->Push(&answer);
- break;
- }
- case Token::BIT_NOT: {
- // Smi check.
- JumpTarget smi_label;
- JumpTarget continue_label;
- Result operand = frame_->Pop();
- TypeInfo operand_info = operand.type_info();
- operand.ToRegister();
- if (operand_info.IsSmi()) {
- if (FLAG_debug_code) __ AbortIfNotSmi(operand.reg());
- frame_->Spill(operand.reg());
- // Set smi tag bit. It will be reset by the not operation.
- __ lea(operand.reg(), Operand(operand.reg(), kSmiTagMask));
- __ not_(operand.reg());
- Result answer = operand;
- answer.set_type_info(TypeInfo::Smi());
- frame_->Push(&answer);
- } else {
- __ test(operand.reg(), Immediate(kSmiTagMask));
- smi_label.Branch(zero, &operand, taken);
-
- GenericUnaryOpStub stub(Token::BIT_NOT,
- overwrite,
- NO_UNARY_SMI_CODE_IN_STUB);
- Result answer = frame_->CallStub(&stub, &operand);
- continue_label.Jump(&answer);
-
- smi_label.Bind(&answer);
- answer.ToRegister();
- frame_->Spill(answer.reg());
- // Set smi tag bit. It will be reset by the not operation.
- __ lea(answer.reg(), Operand(answer.reg(), kSmiTagMask));
- __ not_(answer.reg());
-
- continue_label.Bind(&answer);
- answer.set_type_info(TypeInfo::Integer32());
- frame_->Push(&answer);
- }
- break;
- }
- case Token::ADD: {
- // Smi check.
- JumpTarget continue_label;
- Result operand = frame_->Pop();
- TypeInfo operand_info = operand.type_info();
- operand.ToRegister();
- __ test(operand.reg(), Immediate(kSmiTagMask));
- continue_label.Branch(zero, &operand, taken);
-
- frame_->Push(&operand);
- Result answer = frame_->InvokeBuiltin(Builtins::TO_NUMBER,
- CALL_FUNCTION, 1);
-
- continue_label.Bind(&answer);
- if (operand_info.IsSmi()) {
- answer.set_type_info(TypeInfo::Smi());
- } else if (operand_info.IsInteger32()) {
- answer.set_type_info(TypeInfo::Integer32());
- } else {
- answer.set_type_info(TypeInfo::Number());
- }
- frame_->Push(&answer);
- break;
- }
- default:
- UNREACHABLE();
- }
- }
- }
-}
-
-
-// The value in dst was optimistically incremented or decremented. The
-// result overflowed or was not smi tagged. Undo the operation, call
-// into the runtime to convert the argument to a number, and call the
-// specialized add or subtract stub. The result is left in dst.
-class DeferredPrefixCountOperation: public DeferredCode {
- public:
- DeferredPrefixCountOperation(Register dst,
- bool is_increment,
- TypeInfo input_type)
- : dst_(dst), is_increment_(is_increment), input_type_(input_type) {
- set_comment("[ DeferredCountOperation");
- }
-
- virtual void Generate();
-
- private:
- Register dst_;
- bool is_increment_;
- TypeInfo input_type_;
-};
-
-
-void DeferredPrefixCountOperation::Generate() {
- // Undo the optimistic smi operation.
- if (is_increment_) {
- __ sub(Operand(dst_), Immediate(Smi::FromInt(1)));
- } else {
- __ add(Operand(dst_), Immediate(Smi::FromInt(1)));
- }
- Register left;
- if (input_type_.IsNumber()) {
- left = dst_;
- } else {
- __ push(dst_);
- __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
- left = eax;
- }
-
- GenericBinaryOpStub stub(is_increment_ ? Token::ADD : Token::SUB,
- NO_OVERWRITE,
- NO_GENERIC_BINARY_FLAGS,
- TypeInfo::Number());
- stub.GenerateCall(masm_, left, Smi::FromInt(1));
-
- if (!dst_.is(eax)) __ mov(dst_, eax);
-}
-
-
-// The value in dst was optimistically incremented or decremented. The
-// result overflowed or was not smi tagged. Undo the operation and call
-// into the runtime to convert the argument to a number. Update the
-// original value in old. Call the specialized add or subtract stub.
-// The result is left in dst.
-class DeferredPostfixCountOperation: public DeferredCode {
- public:
- DeferredPostfixCountOperation(Register dst,
- Register old,
- bool is_increment,
- TypeInfo input_type)
- : dst_(dst),
- old_(old),
- is_increment_(is_increment),
- input_type_(input_type) {
- set_comment("[ DeferredCountOperation");
- }
-
- virtual void Generate();
-
- private:
- Register dst_;
- Register old_;
- bool is_increment_;
- TypeInfo input_type_;
-};
-
-
-void DeferredPostfixCountOperation::Generate() {
- // Undo the optimistic smi operation.
- if (is_increment_) {
- __ sub(Operand(dst_), Immediate(Smi::FromInt(1)));
- } else {
- __ add(Operand(dst_), Immediate(Smi::FromInt(1)));
- }
- Register left;
- if (input_type_.IsNumber()) {
- __ push(dst_); // Save the input to use as the old value.
- left = dst_;
- } else {
- __ push(dst_);
- __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
- __ push(eax); // Save the result of ToNumber to use as the old value.
- left = eax;
- }
-
- GenericBinaryOpStub stub(is_increment_ ? Token::ADD : Token::SUB,
- NO_OVERWRITE,
- NO_GENERIC_BINARY_FLAGS,
- TypeInfo::Number());
- stub.GenerateCall(masm_, left, Smi::FromInt(1));
-
- if (!dst_.is(eax)) __ mov(dst_, eax);
- __ pop(old_);
-}
-
-
-void CodeGenerator::VisitCountOperation(CountOperation* node) {
- ASSERT(!in_safe_int32_mode());
- Comment cmnt(masm_, "[ CountOperation");
-
- bool is_postfix = node->is_postfix();
- bool is_increment = node->op() == Token::INC;
-
- Variable* var = node->expression()->AsVariableProxy()->AsVariable();
- bool is_const = (var != NULL && var->mode() == Variable::CONST);
-
- // Postfix operations need a stack slot under the reference to hold
- // the old value while the new value is being stored. This is so that
- // in the case that storing the new value requires a call, the old
- // value will be in the frame to be spilled.
- if (is_postfix) frame_->Push(Smi::FromInt(0));
-
- // A constant reference is not saved to, so a constant reference is not a
- // compound assignment reference.
- { Reference target(this, node->expression(), !is_const);
- if (target.is_illegal()) {
- // Spoof the virtual frame to have the expected height (one higher
- // than on entry).
- if (!is_postfix) frame_->Push(Smi::FromInt(0));
- return;
- }
- target.TakeValue();
-
- Result new_value = frame_->Pop();
- new_value.ToRegister();
-
- Result old_value; // Only allocated in the postfix case.
- if (is_postfix) {
- // Allocate a temporary to preserve the old value.
- old_value = allocator_->Allocate();
- ASSERT(old_value.is_valid());
- __ mov(old_value.reg(), new_value.reg());
-
- // The return value for postfix operations is ToNumber(input).
- // Keep more precise type info if the input is some kind of
- // number already. If the input is not a number we have to wait
- // for the deferred code to convert it.
- if (new_value.type_info().IsNumber()) {
- old_value.set_type_info(new_value.type_info());
- }
- }
-
- // Ensure the new value is writable.
- frame_->Spill(new_value.reg());
-
- Result tmp;
- if (new_value.is_smi()) {
- if (FLAG_debug_code) __ AbortIfNotSmi(new_value.reg());
- } else {
- // We don't know statically if the input is a smi.
- // In order to combine the overflow and the smi tag check, we need
- // to be able to allocate a byte register. We attempt to do so
- // without spilling. If we fail, we will generate separate overflow
- // and smi tag checks.
- // We allocate and clear a temporary byte register before performing
- // the count operation since clearing the register using xor will clear
- // the overflow flag.
- tmp = allocator_->AllocateByteRegisterWithoutSpilling();
- if (tmp.is_valid()) {
- __ Set(tmp.reg(), Immediate(0));
- }
- }
-
- if (is_increment) {
- __ add(Operand(new_value.reg()), Immediate(Smi::FromInt(1)));
- } else {
- __ sub(Operand(new_value.reg()), Immediate(Smi::FromInt(1)));
- }
-
- DeferredCode* deferred = NULL;
- if (is_postfix) {
- deferred = new DeferredPostfixCountOperation(new_value.reg(),
- old_value.reg(),
- is_increment,
- new_value.type_info());
- } else {
- deferred = new DeferredPrefixCountOperation(new_value.reg(),
- is_increment,
- new_value.type_info());
- }
-
- if (new_value.is_smi()) {
- // In case we have a smi as input just check for overflow.
- deferred->Branch(overflow);
- } else {
- // If the count operation didn't overflow and the result is a valid
- // smi, we're done. Otherwise, we jump to the deferred slow-case
- // code.
- // We combine the overflow and the smi tag check if we could
- // successfully allocate a temporary byte register.
- if (tmp.is_valid()) {
- __ setcc(overflow, tmp.reg());
- __ or_(Operand(tmp.reg()), new_value.reg());
- __ test(tmp.reg(), Immediate(kSmiTagMask));
- tmp.Unuse();
- deferred->Branch(not_zero);
- } else {
- // Otherwise we test separately for overflow and smi tag.
- deferred->Branch(overflow);
- __ test(new_value.reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
- }
- }
- deferred->BindExit();
-
- // Postfix count operations return their input converted to
- // number. The case when the input is already a number is covered
- // above in the allocation code for old_value.
- if (is_postfix && !new_value.type_info().IsNumber()) {
- old_value.set_type_info(TypeInfo::Number());
- }
-
- // The result of ++ or -- is an Integer32 if the
- // input is a smi. Otherwise it is a number.
- if (new_value.is_smi()) {
- new_value.set_type_info(TypeInfo::Integer32());
- } else {
- new_value.set_type_info(TypeInfo::Number());
- }
-
- // Postfix: store the old value in the allocated slot under the
- // reference.
- if (is_postfix) frame_->SetElementAt(target.size(), &old_value);
-
- frame_->Push(&new_value);
- // Non-constant: update the reference.
- if (!is_const) target.SetValue(NOT_CONST_INIT);
- }
-
- // Postfix: drop the new value and use the old.
- if (is_postfix) frame_->Drop();
-}
-
-
-void CodeGenerator::Int32BinaryOperation(BinaryOperation* node) {
- Token::Value op = node->op();
- Comment cmnt(masm_, "[ Int32BinaryOperation");
- ASSERT(in_safe_int32_mode());
- ASSERT(safe_int32_mode_enabled());
- ASSERT(FLAG_safe_int32_compiler);
-
- if (op == Token::COMMA) {
- // Discard left value.
- frame_->Nip(1);
- return;
- }
-
- Result right = frame_->Pop();
- Result left = frame_->Pop();
-
- ASSERT(right.is_untagged_int32());
- ASSERT(left.is_untagged_int32());
- // Registers containing an int32 value are not multiply used.
- ASSERT(!left.is_register() || !frame_->is_used(left.reg()));
- ASSERT(!right.is_register() || !frame_->is_used(right.reg()));
-
- switch (op) {
- case Token::COMMA:
- case Token::OR:
- case Token::AND:
- UNREACHABLE();
- break;
- case Token::BIT_OR:
- case Token::BIT_XOR:
- case Token::BIT_AND:
- if (left.is_constant() || right.is_constant()) {
- int32_t value; // Put constant in value, non-constant in left.
- // Constants are known to be int32 values, from static analysis,
- // or else will be converted to int32 by implicit ECMA [[ToInt32]].
- if (left.is_constant()) {
- ASSERT(left.handle()->IsSmi() || left.handle()->IsHeapNumber());
- value = NumberToInt32(*left.handle());
- left = right;
- } else {
- ASSERT(right.handle()->IsSmi() || right.handle()->IsHeapNumber());
- value = NumberToInt32(*right.handle());
- }
-
- left.ToRegister();
- if (op == Token::BIT_OR) {
- __ or_(Operand(left.reg()), Immediate(value));
- } else if (op == Token::BIT_XOR) {
- __ xor_(Operand(left.reg()), Immediate(value));
- } else {
- ASSERT(op == Token::BIT_AND);
- __ and_(Operand(left.reg()), Immediate(value));
- }
- } else {
- ASSERT(left.is_register());
- ASSERT(right.is_register());
- if (op == Token::BIT_OR) {
- __ or_(left.reg(), Operand(right.reg()));
- } else if (op == Token::BIT_XOR) {
- __ xor_(left.reg(), Operand(right.reg()));
- } else {
- ASSERT(op == Token::BIT_AND);
- __ and_(left.reg(), Operand(right.reg()));
- }
- }
- frame_->Push(&left);
- right.Unuse();
- break;
- case Token::SAR:
- case Token::SHL:
- case Token::SHR: {
- bool test_shr_overflow = false;
- left.ToRegister();
- if (right.is_constant()) {
- ASSERT(right.handle()->IsSmi() || right.handle()->IsHeapNumber());
- int shift_amount = NumberToInt32(*right.handle()) & 0x1F;
- if (op == Token::SAR) {
- __ sar(left.reg(), shift_amount);
- } else if (op == Token::SHL) {
- __ shl(left.reg(), shift_amount);
- } else {
- ASSERT(op == Token::SHR);
- __ shr(left.reg(), shift_amount);
- if (shift_amount == 0) test_shr_overflow = true;
- }
- } else {
- // Move right to ecx
- if (left.is_register() && left.reg().is(ecx)) {
- right.ToRegister();
- __ xchg(left.reg(), right.reg());
- left = right; // Left is unused here, copy of right unused by Push.
- } else {
- right.ToRegister(ecx);
- left.ToRegister();
- }
- if (op == Token::SAR) {
- __ sar_cl(left.reg());
- } else if (op == Token::SHL) {
- __ shl_cl(left.reg());
- } else {
- ASSERT(op == Token::SHR);
- __ shr_cl(left.reg());
- test_shr_overflow = true;
- }
- }
- {
- Register left_reg = left.reg();
- frame_->Push(&left);
- right.Unuse();
- if (test_shr_overflow && !node->to_int32()) {
- // Uint32 results with top bit set are not Int32 values.
- // If they will be forced to Int32, skip the test.
- // Test is needed because shr with shift amount 0 does not set flags.
- __ test(left_reg, Operand(left_reg));
- unsafe_bailout_->Branch(sign);
- }
- }
- break;
- }
- case Token::ADD:
- case Token::SUB:
- case Token::MUL:
- if ((left.is_constant() && op != Token::SUB) || right.is_constant()) {
- int32_t value; // Put constant in value, non-constant in left.
- if (right.is_constant()) {
- ASSERT(right.handle()->IsSmi() || right.handle()->IsHeapNumber());
- value = NumberToInt32(*right.handle());
- } else {
- ASSERT(left.handle()->IsSmi() || left.handle()->IsHeapNumber());
- value = NumberToInt32(*left.handle());
- left = right;
- }
-
- left.ToRegister();
- if (op == Token::ADD) {
- __ add(Operand(left.reg()), Immediate(value));
- } else if (op == Token::SUB) {
- __ sub(Operand(left.reg()), Immediate(value));
- } else {
- ASSERT(op == Token::MUL);
- __ imul(left.reg(), left.reg(), value);
- }
- } else {
- left.ToRegister();
- ASSERT(left.is_register());
- ASSERT(right.is_register());
- if (op == Token::ADD) {
- __ add(left.reg(), Operand(right.reg()));
- } else if (op == Token::SUB) {
- __ sub(left.reg(), Operand(right.reg()));
- } else {
- ASSERT(op == Token::MUL);
- // We have statically verified that a negative zero can be ignored.
- __ imul(left.reg(), Operand(right.reg()));
- }
- }
- right.Unuse();
- frame_->Push(&left);
- if (!node->to_int32() || op == Token::MUL) {
- // If ToInt32 is called on the result of ADD, SUB, we don't
- // care about overflows.
- // Result of MUL can be non-representable precisely in double so
- // we have to check for overflow.
- unsafe_bailout_->Branch(overflow);
- }
- break;
- case Token::DIV:
- case Token::MOD: {
- if (right.is_register() && (right.reg().is(eax) || right.reg().is(edx))) {
- if (left.is_register() && left.reg().is(edi)) {
- right.ToRegister(ebx);
- } else {
- right.ToRegister(edi);
- }
- }
- left.ToRegister(eax);
- Result edx_reg = allocator_->Allocate(edx);
- right.ToRegister();
- // The results are unused here because BreakTarget::Branch cannot handle
- // live results.
- Register right_reg = right.reg();
- left.Unuse();
- right.Unuse();
- edx_reg.Unuse();
- __ cmp(right_reg, 0);
- // Ensure divisor is positive: no chance of non-int32 or -0 result.
- unsafe_bailout_->Branch(less_equal);
- __ cdq(); // Sign-extend eax into edx:eax
- __ idiv(right_reg);
- if (op == Token::MOD) {
- // Negative zero can arise as a negative divident with a zero result.
- if (!node->no_negative_zero()) {
- Label not_negative_zero;
- __ test(edx, Operand(edx));
- __ j(not_zero, ¬_negative_zero);
- __ test(eax, Operand(eax));
- unsafe_bailout_->Branch(negative);
- __ bind(¬_negative_zero);
- }
- Result edx_result(edx, TypeInfo::Integer32());
- edx_result.set_untagged_int32(true);
- frame_->Push(&edx_result);
- } else {
- ASSERT(op == Token::DIV);
- __ test(edx, Operand(edx));
- unsafe_bailout_->Branch(not_equal);
- Result eax_result(eax, TypeInfo::Integer32());
- eax_result.set_untagged_int32(true);
- frame_->Push(&eax_result);
- }
- break;
- }
- default:
- UNREACHABLE();
- break;
- }
-}
-
-
-void CodeGenerator::GenerateLogicalBooleanOperation(BinaryOperation* node) {
- // According to ECMA-262 section 11.11, page 58, the binary logical
- // operators must yield the result of one of the two expressions
- // before any ToBoolean() conversions. This means that the value
- // produced by a && or || operator is not necessarily a boolean.
-
- // NOTE: If the left hand side produces a materialized value (not
- // control flow), we force the right hand side to do the same. This
- // is necessary because we assume that if we get control flow on the
- // last path out of an expression we got it on all paths.
- if (node->op() == Token::AND) {
- ASSERT(!in_safe_int32_mode());
- JumpTarget is_true;
- ControlDestination dest(&is_true, destination()->false_target(), true);
- LoadCondition(node->left(), &dest, false);
-
- if (dest.false_was_fall_through()) {
- // The current false target was used as the fall-through. If
- // there are no dangling jumps to is_true then the left
- // subexpression was unconditionally false. Otherwise we have
- // paths where we do have to evaluate the right subexpression.
- if (is_true.is_linked()) {
- // We need to compile the right subexpression. If the jump to
- // the current false target was a forward jump then we have a
- // valid frame, we have just bound the false target, and we
- // have to jump around the code for the right subexpression.
- if (has_valid_frame()) {
- destination()->false_target()->Unuse();
- destination()->false_target()->Jump();
- }
- is_true.Bind();
- // The left subexpression compiled to control flow, so the
- // right one is free to do so as well.
- LoadCondition(node->right(), destination(), false);
- } else {
- // We have actually just jumped to or bound the current false
- // target but the current control destination is not marked as
- // used.
- destination()->Use(false);
- }
-
- } else if (dest.is_used()) {
- // The left subexpression compiled to control flow (and is_true
- // was just bound), so the right is free to do so as well.
- LoadCondition(node->right(), destination(), false);
-
- } else {
- // We have a materialized value on the frame, so we exit with
- // one on all paths. There are possibly also jumps to is_true
- // from nested subexpressions.
- JumpTarget pop_and_continue;
- JumpTarget exit;
-
- // Avoid popping the result if it converts to 'false' using the
- // standard ToBoolean() conversion as described in ECMA-262,
- // section 9.2, page 30.
- //
- // Duplicate the TOS value. The duplicate will be popped by
- // ToBoolean.
- frame_->Dup();
- ControlDestination dest(&pop_and_continue, &exit, true);
- ToBoolean(&dest);
-
- // Pop the result of evaluating the first part.
- frame_->Drop();
-
- // Compile right side expression.
- is_true.Bind();
- Load(node->right());
-
- // Exit (always with a materialized value).
- exit.Bind();
- }
-
- } else {
- ASSERT(node->op() == Token::OR);
- ASSERT(!in_safe_int32_mode());
- JumpTarget is_false;
- ControlDestination dest(destination()->true_target(), &is_false, false);
- LoadCondition(node->left(), &dest, false);
-
- if (dest.true_was_fall_through()) {
- // The current true target was used as the fall-through. If
- // there are no dangling jumps to is_false then the left
- // subexpression was unconditionally true. Otherwise we have
- // paths where we do have to evaluate the right subexpression.
- if (is_false.is_linked()) {
- // We need to compile the right subexpression. If the jump to
- // the current true target was a forward jump then we have a
- // valid frame, we have just bound the true target, and we
- // have to jump around the code for the right subexpression.
- if (has_valid_frame()) {
- destination()->true_target()->Unuse();
- destination()->true_target()->Jump();
- }
- is_false.Bind();
- // The left subexpression compiled to control flow, so the
- // right one is free to do so as well.
- LoadCondition(node->right(), destination(), false);
- } else {
- // We have just jumped to or bound the current true target but
- // the current control destination is not marked as used.
- destination()->Use(true);
- }
-
- } else if (dest.is_used()) {
- // The left subexpression compiled to control flow (and is_false
- // was just bound), so the right is free to do so as well.
- LoadCondition(node->right(), destination(), false);
-
- } else {
- // We have a materialized value on the frame, so we exit with
- // one on all paths. There are possibly also jumps to is_false
- // from nested subexpressions.
- JumpTarget pop_and_continue;
- JumpTarget exit;
-
- // Avoid popping the result if it converts to 'true' using the
- // standard ToBoolean() conversion as described in ECMA-262,
- // section 9.2, page 30.
- //
- // Duplicate the TOS value. The duplicate will be popped by
- // ToBoolean.
- frame_->Dup();
- ControlDestination dest(&exit, &pop_and_continue, false);
- ToBoolean(&dest);
-
- // Pop the result of evaluating the first part.
- frame_->Drop();
-
- // Compile right side expression.
- is_false.Bind();
- Load(node->right());
-
- // Exit (always with a materialized value).
- exit.Bind();
- }
- }
-}
-
-
-void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
- Comment cmnt(masm_, "[ BinaryOperation");
-
- if (node->op() == Token::AND || node->op() == Token::OR) {
- GenerateLogicalBooleanOperation(node);
- } else if (in_safe_int32_mode()) {
- Visit(node->left());
- Visit(node->right());
- Int32BinaryOperation(node);
- } else {
- // NOTE: The code below assumes that the slow cases (calls to runtime)
- // never return a constant/immutable object.
- OverwriteMode overwrite_mode = NO_OVERWRITE;
- if (node->left()->ResultOverwriteAllowed()) {
- overwrite_mode = OVERWRITE_LEFT;
- } else if (node->right()->ResultOverwriteAllowed()) {
- overwrite_mode = OVERWRITE_RIGHT;
- }
-
- if (node->left()->IsTrivial()) {
- Load(node->right());
- Result right = frame_->Pop();
- frame_->Push(node->left());
- frame_->Push(&right);
- } else {
- Load(node->left());
- Load(node->right());
- }
- GenericBinaryOperation(node, overwrite_mode);
- }
-}
-
-
-void CodeGenerator::VisitThisFunction(ThisFunction* node) {
- ASSERT(!in_safe_int32_mode());
- frame_->PushFunction();
-}
-
-
-void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
- ASSERT(!in_safe_int32_mode());
- Comment cmnt(masm_, "[ CompareOperation");
-
- bool left_already_loaded = false;
-
- // Get the expressions from the node.
- Expression* left = node->left();
- Expression* right = node->right();
- Token::Value op = node->op();
- // To make typeof testing for natives implemented in JavaScript really
- // efficient, we generate special code for expressions of the form:
- // 'typeof <expression> == <string>'.
- UnaryOperation* operation = left->AsUnaryOperation();
- if ((op == Token::EQ || op == Token::EQ_STRICT) &&
- (operation != NULL && operation->op() == Token::TYPEOF) &&
- (right->AsLiteral() != NULL &&
- right->AsLiteral()->handle()->IsString())) {
- Handle<String> check(String::cast(*right->AsLiteral()->handle()));
-
- // Load the operand and move it to a register.
- LoadTypeofExpression(operation->expression());
- Result answer = frame_->Pop();
- answer.ToRegister();
-
- if (check->Equals(HEAP->number_symbol())) {
- __ test(answer.reg(), Immediate(kSmiTagMask));
- destination()->true_target()->Branch(zero);
- frame_->Spill(answer.reg());
- __ mov(answer.reg(), FieldOperand(answer.reg(), HeapObject::kMapOffset));
- __ cmp(answer.reg(), FACTORY->heap_number_map());
- answer.Unuse();
- destination()->Split(equal);
-
- } else if (check->Equals(HEAP->string_symbol())) {
- __ test(answer.reg(), Immediate(kSmiTagMask));
- destination()->false_target()->Branch(zero);
-
- // It can be an undetectable string object.
- Result temp = allocator()->Allocate();
- ASSERT(temp.is_valid());
- __ mov(temp.reg(), FieldOperand(answer.reg(), HeapObject::kMapOffset));
- __ test_b(FieldOperand(temp.reg(), Map::kBitFieldOffset),
- 1 << Map::kIsUndetectable);
- destination()->false_target()->Branch(not_zero);
- __ CmpInstanceType(temp.reg(), FIRST_NONSTRING_TYPE);
- temp.Unuse();
- answer.Unuse();
- destination()->Split(below);
-
- } else if (check->Equals(HEAP->boolean_symbol())) {
- __ cmp(answer.reg(), FACTORY->true_value());
- destination()->true_target()->Branch(equal);
- __ cmp(answer.reg(), FACTORY->false_value());
- answer.Unuse();
- destination()->Split(equal);
-
- } else if (check->Equals(HEAP->undefined_symbol())) {
- __ cmp(answer.reg(), FACTORY->undefined_value());
- destination()->true_target()->Branch(equal);
-
- __ test(answer.reg(), Immediate(kSmiTagMask));
- destination()->false_target()->Branch(zero);
-
- // It can be an undetectable object.
- frame_->Spill(answer.reg());
- __ mov(answer.reg(), FieldOperand(answer.reg(), HeapObject::kMapOffset));
- __ test_b(FieldOperand(answer.reg(), Map::kBitFieldOffset),
- 1 << Map::kIsUndetectable);
- answer.Unuse();
- destination()->Split(not_zero);
-
- } else if (check->Equals(HEAP->function_symbol())) {
- __ test(answer.reg(), Immediate(kSmiTagMask));
- destination()->false_target()->Branch(zero);
- frame_->Spill(answer.reg());
- __ CmpObjectType(answer.reg(), JS_FUNCTION_TYPE, answer.reg());
- destination()->true_target()->Branch(equal);
- // Regular expressions are callable so typeof == 'function'.
- __ CmpInstanceType(answer.reg(), JS_REGEXP_TYPE);
- answer.Unuse();
- destination()->Split(equal);
- } else if (check->Equals(HEAP->object_symbol())) {
- __ test(answer.reg(), Immediate(kSmiTagMask));
- destination()->false_target()->Branch(zero);
- __ cmp(answer.reg(), FACTORY->null_value());
- destination()->true_target()->Branch(equal);
-
- Result map = allocator()->Allocate();
- ASSERT(map.is_valid());
- // Regular expressions are typeof == 'function', not 'object'.
- __ CmpObjectType(answer.reg(), JS_REGEXP_TYPE, map.reg());
- destination()->false_target()->Branch(equal);
-
- // It can be an undetectable object.
- __ test_b(FieldOperand(map.reg(), Map::kBitFieldOffset),
- 1 << Map::kIsUndetectable);
- destination()->false_target()->Branch(not_zero);
- // Do a range test for JSObject type. We can't use
- // MacroAssembler::IsInstanceJSObjectType, because we are using a
- // ControlDestination, so we copy its implementation here.
- __ movzx_b(map.reg(), FieldOperand(map.reg(), Map::kInstanceTypeOffset));
- __ sub(Operand(map.reg()), Immediate(FIRST_JS_OBJECT_TYPE));
- __ cmp(map.reg(), LAST_JS_OBJECT_TYPE - FIRST_JS_OBJECT_TYPE);
- answer.Unuse();
- map.Unuse();
- destination()->Split(below_equal);
- } else {
- // Uncommon case: typeof testing against a string literal that is
- // never returned from the typeof operator.
- answer.Unuse();
- destination()->Goto(false);
- }
- return;
- } else if (op == Token::LT &&
- right->AsLiteral() != NULL &&
- right->AsLiteral()->handle()->IsHeapNumber()) {
- Handle<HeapNumber> check(HeapNumber::cast(*right->AsLiteral()->handle()));
- if (check->value() == 2147483648.0) { // 0x80000000.
- Load(left);
- left_already_loaded = true;
- Result lhs = frame_->Pop();
- lhs.ToRegister();
- __ test(lhs.reg(), Immediate(kSmiTagMask));
- destination()->true_target()->Branch(zero); // All Smis are less.
- Result scratch = allocator()->Allocate();
- ASSERT(scratch.is_valid());
- __ mov(scratch.reg(), FieldOperand(lhs.reg(), HeapObject::kMapOffset));
- __ cmp(scratch.reg(), FACTORY->heap_number_map());
- JumpTarget not_a_number;
- not_a_number.Branch(not_equal, &lhs);
- __ mov(scratch.reg(),
- FieldOperand(lhs.reg(), HeapNumber::kExponentOffset));
- __ cmp(Operand(scratch.reg()), Immediate(0xfff00000));
- not_a_number.Branch(above_equal, &lhs); // It's a negative NaN or -Inf.
- const uint32_t borderline_exponent =
- (HeapNumber::kExponentBias + 31) << HeapNumber::kExponentShift;
- __ cmp(Operand(scratch.reg()), Immediate(borderline_exponent));
- scratch.Unuse();
- lhs.Unuse();
- destination()->true_target()->Branch(less);
- destination()->false_target()->Jump();
-
- not_a_number.Bind(&lhs);
- frame_->Push(&lhs);
- }
- }
-
- Condition cc = no_condition;
- bool strict = false;
- switch (op) {
- case Token::EQ_STRICT:
- strict = true;
- // Fall through
- case Token::EQ:
- cc = equal;
- break;
- case Token::LT:
- cc = less;
- break;
- case Token::GT:
- cc = greater;
- break;
- case Token::LTE:
- cc = less_equal;
- break;
- case Token::GTE:
- cc = greater_equal;
- break;
- case Token::IN: {
- if (!left_already_loaded) Load(left);
- Load(right);
- Result answer = frame_->InvokeBuiltin(Builtins::IN, CALL_FUNCTION, 2);
- frame_->Push(&answer); // push the result
- return;
- }
- case Token::INSTANCEOF: {
- if (!left_already_loaded) Load(left);
- Load(right);
- InstanceofStub stub(InstanceofStub::kNoFlags);
- Result answer = frame_->CallStub(&stub, 2);
- answer.ToRegister();
- __ test(answer.reg(), Operand(answer.reg()));
- answer.Unuse();
- destination()->Split(zero);
- return;
- }
- default:
- UNREACHABLE();
- }
-
- if (left->IsTrivial()) {
- if (!left_already_loaded) {
- Load(right);
- Result right_result = frame_->Pop();
- frame_->Push(left);
- frame_->Push(&right_result);
- } else {
- Load(right);
- }
- } else {
- if (!left_already_loaded) Load(left);
- Load(right);
- }
- Comparison(node, cc, strict, destination());
-}
-
-
-void CodeGenerator::VisitCompareToNull(CompareToNull* node) {
- ASSERT(!in_safe_int32_mode());
- Comment cmnt(masm_, "[ CompareToNull");
-
- Load(node->expression());
- Result operand = frame_->Pop();
- operand.ToRegister();
- __ cmp(operand.reg(), FACTORY->null_value());
- if (node->is_strict()) {
- operand.Unuse();
- destination()->Split(equal);
- } else {
- // The 'null' value is only equal to 'undefined' if using non-strict
- // comparisons.
- destination()->true_target()->Branch(equal);
- __ cmp(operand.reg(), FACTORY->undefined_value());
- destination()->true_target()->Branch(equal);
- __ test(operand.reg(), Immediate(kSmiTagMask));
- destination()->false_target()->Branch(equal);
-
- // It can be an undetectable object.
- // Use a scratch register in preference to spilling operand.reg().
- Result temp = allocator()->Allocate();
- ASSERT(temp.is_valid());
- __ mov(temp.reg(),
- FieldOperand(operand.reg(), HeapObject::kMapOffset));
- __ test_b(FieldOperand(temp.reg(), Map::kBitFieldOffset),
- 1 << Map::kIsUndetectable);
- temp.Unuse();
- operand.Unuse();
- destination()->Split(not_zero);
- }
-}
-
-
-#ifdef DEBUG
-bool CodeGenerator::HasValidEntryRegisters() {
- return (allocator()->count(eax) == (frame()->is_used(eax) ? 1 : 0))
- && (allocator()->count(ebx) == (frame()->is_used(ebx) ? 1 : 0))
- && (allocator()->count(ecx) == (frame()->is_used(ecx) ? 1 : 0))
- && (allocator()->count(edx) == (frame()->is_used(edx) ? 1 : 0))
- && (allocator()->count(edi) == (frame()->is_used(edi) ? 1 : 0));
-}
-#endif
-
-
-// Emit a LoadIC call to get the value from receiver and leave it in
-// dst.
-class DeferredReferenceGetNamedValue: public DeferredCode {
- public:
- DeferredReferenceGetNamedValue(Register dst,
- Register receiver,
- Handle<String> name,
- bool is_contextual)
- : dst_(dst),
- receiver_(receiver),
- name_(name),
- is_contextual_(is_contextual),
- is_dont_delete_(false) {
- set_comment(is_contextual
- ? "[ DeferredReferenceGetNamedValue (contextual)"
- : "[ DeferredReferenceGetNamedValue");
- }
-
- virtual void Generate();
-
- Label* patch_site() { return &patch_site_; }
-
- void set_is_dont_delete(bool value) {
- ASSERT(is_contextual_);
- is_dont_delete_ = value;
- }
-
- private:
- Label patch_site_;
- Register dst_;
- Register receiver_;
- Handle<String> name_;
- bool is_contextual_;
- bool is_dont_delete_;
-};
-
-
-void DeferredReferenceGetNamedValue::Generate() {
- if (!receiver_.is(eax)) {
- __ mov(eax, receiver_);
- }
- __ Set(ecx, Immediate(name_));
- Handle<Code> ic(masm()->isolate()->builtins()->builtin(
- Builtins::kLoadIC_Initialize));
- RelocInfo::Mode mode = is_contextual_
- ? RelocInfo::CODE_TARGET_CONTEXT
- : RelocInfo::CODE_TARGET;
- __ call(ic, mode);
- // The call must be followed by:
- // - a test eax instruction to indicate that the inobject property
- // case was inlined.
- // - a mov ecx or mov edx instruction to indicate that the
- // contextual property load was inlined.
- //
- // Store the delta to the map check instruction here in the test
- // instruction. Use masm_-> instead of the __ macro since the
- // latter can't return a value.
- int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site());
- // Here we use masm_-> instead of the __ macro because this is the
- // instruction that gets patched and coverage code gets in the way.
- Counters* counters = masm()->isolate()->counters();
- if (is_contextual_) {
- masm_->mov(is_dont_delete_ ? edx : ecx, -delta_to_patch_site);
- __ IncrementCounter(counters->named_load_global_inline_miss(), 1);
- if (is_dont_delete_) {
- __ IncrementCounter(counters->dont_delete_hint_miss(), 1);
- }
- } else {
- masm_->test(eax, Immediate(-delta_to_patch_site));
- __ IncrementCounter(counters->named_load_inline_miss(), 1);
- }
-
- if (!dst_.is(eax)) __ mov(dst_, eax);
-}
-
-
-class DeferredReferenceGetKeyedValue: public DeferredCode {
- public:
- explicit DeferredReferenceGetKeyedValue(Register dst,
- Register receiver,
- Register key)
- : dst_(dst), receiver_(receiver), key_(key) {
- set_comment("[ DeferredReferenceGetKeyedValue");
- }
-
- virtual void Generate();
-
- Label* patch_site() { return &patch_site_; }
-
- private:
- Label patch_site_;
- Register dst_;
- Register receiver_;
- Register key_;
-};
-
-
-void DeferredReferenceGetKeyedValue::Generate() {
- if (!receiver_.is(eax)) {
- // Register eax is available for key.
- if (!key_.is(eax)) {
- __ mov(eax, key_);
- }
- if (!receiver_.is(edx)) {
- __ mov(edx, receiver_);
- }
- } else if (!key_.is(edx)) {
- // Register edx is available for receiver.
- if (!receiver_.is(edx)) {
- __ mov(edx, receiver_);
- }
- if (!key_.is(eax)) {
- __ mov(eax, key_);
- }
- } else {
- __ xchg(edx, eax);
- }
- // Calculate the delta from the IC call instruction to the map check
- // cmp instruction in the inlined version. This delta is stored in
- // a test(eax, delta) instruction after the call so that we can find
- // it in the IC initialization code and patch the cmp instruction.
- // This means that we cannot allow test instructions after calls to
- // KeyedLoadIC stubs in other places.
- Handle<Code> ic(masm()->isolate()->builtins()->builtin(
- Builtins::kKeyedLoadIC_Initialize));
- __ call(ic, RelocInfo::CODE_TARGET);
- // The delta from the start of the map-compare instruction to the
- // test instruction. We use masm_-> directly here instead of the __
- // macro because the macro sometimes uses macro expansion to turn
- // into something that can't return a value. This is encountered
- // when doing generated code coverage tests.
- int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site());
- // Here we use masm_-> instead of the __ macro because this is the
- // instruction that gets patched and coverage code gets in the way.
- masm_->test(eax, Immediate(-delta_to_patch_site));
- Counters* counters = masm()->isolate()->counters();
- __ IncrementCounter(counters->keyed_load_inline_miss(), 1);
-
- if (!dst_.is(eax)) __ mov(dst_, eax);
-}
-
-
-class DeferredReferenceSetKeyedValue: public DeferredCode {
- public:
- DeferredReferenceSetKeyedValue(Register value,
- Register key,
- Register receiver,
- Register scratch,
- StrictModeFlag strict_mode)
- : value_(value),
- key_(key),
- receiver_(receiver),
- scratch_(scratch),
- strict_mode_(strict_mode) {
- set_comment("[ DeferredReferenceSetKeyedValue");
- }
-
- virtual void Generate();
-
- Label* patch_site() { return &patch_site_; }
-
- private:
- Register value_;
- Register key_;
- Register receiver_;
- Register scratch_;
- Label patch_site_;
- StrictModeFlag strict_mode_;
-};
-
-
-void DeferredReferenceSetKeyedValue::Generate() {
- Counters* counters = masm()->isolate()->counters();
- __ IncrementCounter(counters->keyed_store_inline_miss(), 1);
- // Move value_ to eax, key_ to ecx, and receiver_ to edx.
- Register old_value = value_;
-
- // First, move value to eax.
- if (!value_.is(eax)) {
- if (key_.is(eax)) {
- // Move key_ out of eax, preferably to ecx.
- if (!value_.is(ecx) && !receiver_.is(ecx)) {
- __ mov(ecx, key_);
- key_ = ecx;
- } else {
- __ mov(scratch_, key_);
- key_ = scratch_;
- }
- }
- if (receiver_.is(eax)) {
- // Move receiver_ out of eax, preferably to edx.
- if (!value_.is(edx) && !key_.is(edx)) {
- __ mov(edx, receiver_);
- receiver_ = edx;
- } else {
- // Both moves to scratch are from eax, also, no valid path hits both.
- __ mov(scratch_, receiver_);
- receiver_ = scratch_;
- }
- }
- __ mov(eax, value_);
- value_ = eax;
- }
-
- // Now value_ is in eax. Move the other two to the right positions.
- // We do not update the variables key_ and receiver_ to ecx and edx.
- if (key_.is(ecx)) {
- if (!receiver_.is(edx)) {
- __ mov(edx, receiver_);
- }
- } else if (key_.is(edx)) {
- if (receiver_.is(ecx)) {
- __ xchg(edx, ecx);
- } else {
- __ mov(ecx, key_);
- if (!receiver_.is(edx)) {
- __ mov(edx, receiver_);
- }
- }
- } else { // Key is not in edx or ecx.
- if (!receiver_.is(edx)) {
- __ mov(edx, receiver_);
- }
- __ mov(ecx, key_);
- }
-
- // Call the IC stub.
- Handle<Code> ic(masm()->isolate()->builtins()->builtin(
- (strict_mode_ == kStrictMode) ? Builtins::kKeyedStoreIC_Initialize_Strict
- : Builtins::kKeyedStoreIC_Initialize));
- __ call(ic, RelocInfo::CODE_TARGET);
- // The delta from the start of the map-compare instruction to the
- // test instruction. We use masm_-> directly here instead of the
- // __ macro because the macro sometimes uses macro expansion to turn
- // into something that can't return a value. This is encountered
- // when doing generated code coverage tests.
- int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site());
- // Here we use masm_-> instead of the __ macro because this is the
- // instruction that gets patched and coverage code gets in the way.
- masm_->test(eax, Immediate(-delta_to_patch_site));
- // Restore value (returned from store IC) register.
- if (!old_value.is(eax)) __ mov(old_value, eax);
-}
-
-
-Result CodeGenerator::EmitNamedLoad(Handle<String> name, bool is_contextual) {
-#ifdef DEBUG
- int original_height = frame()->height();
-#endif
-
- Isolate* isolate = masm()->isolate();
- Factory* factory = isolate->factory();
- Counters* counters = isolate->counters();
-
- bool contextual_load_in_builtin =
- is_contextual &&
- (isolate->bootstrapper()->IsActive() ||
- (!info_->closure().is_null() && info_->closure()->IsBuiltin()));
-
- Result result;
- // Do not inline in the global code or when not in loop.
- if (scope()->is_global_scope() ||
- loop_nesting() == 0 ||
- contextual_load_in_builtin) {
- Comment cmnt(masm(), "[ Load from named Property");
- frame()->Push(name);
-
- RelocInfo::Mode mode = is_contextual
- ? RelocInfo::CODE_TARGET_CONTEXT
- : RelocInfo::CODE_TARGET;
- result = frame()->CallLoadIC(mode);
- // A test eax instruction following the call signals that the inobject
- // property case was inlined. Ensure that there is not a test eax
- // instruction here.
- __ nop();
- } else {
- // Inline the property load.
- Comment cmnt(masm(), is_contextual
- ? "[ Inlined contextual property load"
- : "[ Inlined named property load");
- Result receiver = frame()->Pop();
- receiver.ToRegister();
-
- result = allocator()->Allocate();
- ASSERT(result.is_valid());
- DeferredReferenceGetNamedValue* deferred =
- new DeferredReferenceGetNamedValue(result.reg(),
- receiver.reg(),
- name,
- is_contextual);
-
- if (!is_contextual) {
- // Check that the receiver is a heap object.
- __ test(receiver.reg(), Immediate(kSmiTagMask));
- deferred->Branch(zero);
- }
-
- __ bind(deferred->patch_site());
- // This is the map check instruction that will be patched (so we can't
- // use the double underscore macro that may insert instructions).
- // Initially use an invalid map to force a failure.
- masm()->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
- Immediate(factory->null_value()));
- // This branch is always a forwards branch so it's always a fixed size
- // which allows the assert below to succeed and patching to work.
- deferred->Branch(not_equal);
-
- // The delta from the patch label to the actual load must be
- // statically known.
- ASSERT(masm()->SizeOfCodeGeneratedSince(deferred->patch_site()) ==
- LoadIC::kOffsetToLoadInstruction);
-
- if (is_contextual) {
- // Load the (initialy invalid) cell and get its value.
- masm()->mov(result.reg(), factory->null_value());
- if (FLAG_debug_code) {
- __ cmp(FieldOperand(result.reg(), HeapObject::kMapOffset),
- factory->global_property_cell_map());
- __ Assert(equal, "Uninitialized inlined contextual load");
- }
- __ mov(result.reg(),
- FieldOperand(result.reg(), JSGlobalPropertyCell::kValueOffset));
- __ cmp(result.reg(), factory->the_hole_value());
- deferred->Branch(equal);
- bool is_dont_delete = false;
- if (!info_->closure().is_null()) {
- // When doing lazy compilation we can check if the global cell
- // already exists and use its "don't delete" status as a hint.
- AssertNoAllocation no_gc;
- v8::internal::GlobalObject* global_object =
- info_->closure()->context()->global();
- LookupResult lookup;
- global_object->LocalLookupRealNamedProperty(*name, &lookup);
- if (lookup.IsProperty() && lookup.type() == NORMAL) {
- ASSERT(lookup.holder() == global_object);
- ASSERT(global_object->property_dictionary()->ValueAt(
- lookup.GetDictionaryEntry())->IsJSGlobalPropertyCell());
- is_dont_delete = lookup.IsDontDelete();
- }
- }
- deferred->set_is_dont_delete(is_dont_delete);
- if (!is_dont_delete) {
- __ cmp(result.reg(), factory->the_hole_value());
- deferred->Branch(equal);
- } else if (FLAG_debug_code) {
- __ cmp(result.reg(), factory->the_hole_value());
- __ Check(not_equal, "DontDelete cells can't contain the hole");
- }
- __ IncrementCounter(counters->named_load_global_inline(), 1);
- if (is_dont_delete) {
- __ IncrementCounter(counters->dont_delete_hint_hit(), 1);
- }
- } else {
- // The initial (invalid) offset has to be large enough to force a 32-bit
- // instruction encoding to allow patching with an arbitrary offset. Use
- // kMaxInt (minus kHeapObjectTag).
- int offset = kMaxInt;
- masm()->mov(result.reg(), FieldOperand(receiver.reg(), offset));
- __ IncrementCounter(counters->named_load_inline(), 1);
- }
-
- deferred->BindExit();
- }
- ASSERT(frame()->height() == original_height - 1);
- return result;
-}
-
-
-Result CodeGenerator::EmitNamedStore(Handle<String> name, bool is_contextual) {
-#ifdef DEBUG
- int expected_height = frame()->height() - (is_contextual ? 1 : 2);
-#endif
-
- Result result;
- if (is_contextual || scope()->is_global_scope() || loop_nesting() == 0) {
- result = frame()->CallStoreIC(name, is_contextual, strict_mode_flag());
- // A test eax instruction following the call signals that the inobject
- // property case was inlined. Ensure that there is not a test eax
- // instruction here.
- __ nop();
- } else {
- // Inline the in-object property case.
- JumpTarget slow, done;
- Label patch_site;
-
- // Get the value and receiver from the stack.
- Result value = frame()->Pop();
- value.ToRegister();
- Result receiver = frame()->Pop();
- receiver.ToRegister();
-
- // Allocate result register.
- result = allocator()->Allocate();
- ASSERT(result.is_valid() && receiver.is_valid() && value.is_valid());
-
- // Check that the receiver is a heap object.
- __ test(receiver.reg(), Immediate(kSmiTagMask));
- slow.Branch(zero, &value, &receiver);
-
- // This is the map check instruction that will be patched (so we can't
- // use the double underscore macro that may insert instructions).
- // Initially use an invalid map to force a failure.
- __ bind(&patch_site);
- masm()->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
- Immediate(FACTORY->null_value()));
- // This branch is always a forwards branch so it's always a fixed size
- // which allows the assert below to succeed and patching to work.
- slow.Branch(not_equal, &value, &receiver);
-
- // The delta from the patch label to the store offset must be
- // statically known.
- ASSERT(masm()->SizeOfCodeGeneratedSince(&patch_site) ==
- StoreIC::kOffsetToStoreInstruction);
-
- // The initial (invalid) offset has to be large enough to force a 32-bit
- // instruction encoding to allow patching with an arbitrary offset. Use
- // kMaxInt (minus kHeapObjectTag).
- int offset = kMaxInt;
- __ mov(FieldOperand(receiver.reg(), offset), value.reg());
- __ mov(result.reg(), Operand(value.reg()));
-
- // Allocate scratch register for write barrier.
- Result scratch = allocator()->Allocate();
- ASSERT(scratch.is_valid());
-
- // The write barrier clobbers all input registers, so spill the
- // receiver and the value.
- frame_->Spill(receiver.reg());
- frame_->Spill(value.reg());
-
- // If the receiver and the value share a register allocate a new
- // register for the receiver.
- if (receiver.reg().is(value.reg())) {
- receiver = allocator()->Allocate();
- ASSERT(receiver.is_valid());
- __ mov(receiver.reg(), Operand(value.reg()));
- }
-
- // Update the write barrier. To save instructions in the inlined
- // version we do not filter smis.
- Label skip_write_barrier;
- __ InNewSpace(receiver.reg(), value.reg(), equal, &skip_write_barrier);
- int delta_to_record_write = masm_->SizeOfCodeGeneratedSince(&patch_site);
- __ lea(scratch.reg(), Operand(receiver.reg(), offset));
- __ RecordWriteHelper(receiver.reg(), scratch.reg(), value.reg());
- if (FLAG_debug_code) {
- __ mov(receiver.reg(), Immediate(BitCast<int32_t>(kZapValue)));
- __ mov(value.reg(), Immediate(BitCast<int32_t>(kZapValue)));
- __ mov(scratch.reg(), Immediate(BitCast<int32_t>(kZapValue)));
- }
- __ bind(&skip_write_barrier);
- value.Unuse();
- scratch.Unuse();
- receiver.Unuse();
- done.Jump(&result);
-
- slow.Bind(&value, &receiver);
- frame()->Push(&receiver);
- frame()->Push(&value);
- result = frame()->CallStoreIC(name, is_contextual, strict_mode_flag());
- // Encode the offset to the map check instruction and the offset
- // to the write barrier store address computation in a test eax
- // instruction.
- int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(&patch_site);
- __ test(eax,
- Immediate((delta_to_record_write << 16) | delta_to_patch_site));
- done.Bind(&result);
- }
-
- ASSERT_EQ(expected_height, frame()->height());
- return result;
-}
-
-
-Result CodeGenerator::EmitKeyedLoad() {
-#ifdef DEBUG
- int original_height = frame()->height();
-#endif
- Result result;
- // Inline array load code if inside of a loop. We do not know the
- // receiver map yet, so we initially generate the code with a check
- // against an invalid map. In the inline cache code, we patch the map
- // check if appropriate.
- if (loop_nesting() > 0) {
- Comment cmnt(masm_, "[ Inlined load from keyed Property");
-
- // Use a fresh temporary to load the elements without destroying
- // the receiver which is needed for the deferred slow case.
- Result elements = allocator()->Allocate();
- ASSERT(elements.is_valid());
-
- Result key = frame_->Pop();
- Result receiver = frame_->Pop();
- key.ToRegister();
- receiver.ToRegister();
-
- // If key and receiver are shared registers on the frame, their values will
- // be automatically saved and restored when going to deferred code.
- // The result is in elements, which is guaranteed non-shared.
- DeferredReferenceGetKeyedValue* deferred =
- new DeferredReferenceGetKeyedValue(elements.reg(),
- receiver.reg(),
- key.reg());
-
- __ test(receiver.reg(), Immediate(kSmiTagMask));
- deferred->Branch(zero);
-
- // Check that the receiver has the expected map.
- // Initially, use an invalid map. The map is patched in the IC
- // initialization code.
- __ bind(deferred->patch_site());
- // Use masm-> here instead of the double underscore macro since extra
- // coverage code can interfere with the patching.
- masm_->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
- Immediate(FACTORY->null_value()));
- deferred->Branch(not_equal);
-
- // Check that the key is a smi.
- if (!key.is_smi()) {
- __ test(key.reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
- } else {
- if (FLAG_debug_code) __ AbortIfNotSmi(key.reg());
- }
-
- // Get the elements array from the receiver.
- __ mov(elements.reg(),
- FieldOperand(receiver.reg(), JSObject::kElementsOffset));
- __ AssertFastElements(elements.reg());
-
- // Check that the key is within bounds.
- __ cmp(key.reg(),
- FieldOperand(elements.reg(), FixedArray::kLengthOffset));
- deferred->Branch(above_equal);
-
- // Load and check that the result is not the hole.
- // Key holds a smi.
- STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
- __ mov(elements.reg(),
- FieldOperand(elements.reg(),
- key.reg(),
- times_2,
- FixedArray::kHeaderSize));
- result = elements;
- __ cmp(Operand(result.reg()), Immediate(FACTORY->the_hole_value()));
- deferred->Branch(equal);
- __ IncrementCounter(masm_->isolate()->counters()->keyed_load_inline(), 1);
-
- deferred->BindExit();
- } else {
- Comment cmnt(masm_, "[ Load from keyed Property");
- result = frame_->CallKeyedLoadIC(RelocInfo::CODE_TARGET);
- // Make sure that we do not have a test instruction after the
- // call. A test instruction after the call is used to
- // indicate that we have generated an inline version of the
- // keyed load. The explicit nop instruction is here because
- // the push that follows might be peep-hole optimized away.
- __ nop();
- }
- ASSERT(frame()->height() == original_height - 2);
- return result;
-}
-
-
-Result CodeGenerator::EmitKeyedStore(StaticType* key_type) {
-#ifdef DEBUG
- int original_height = frame()->height();
-#endif
- Result result;
- // Generate inlined version of the keyed store if the code is in a loop
- // and the key is likely to be a smi.
- if (loop_nesting() > 0 && key_type->IsLikelySmi()) {
- Comment cmnt(masm(), "[ Inlined store to keyed Property");
-
- // Get the receiver, key and value into registers.
- result = frame()->Pop();
- Result key = frame()->Pop();
- Result receiver = frame()->Pop();
-
- Result tmp = allocator_->Allocate();
- ASSERT(tmp.is_valid());
- Result tmp2 = allocator_->Allocate();
- ASSERT(tmp2.is_valid());
-
- // Determine whether the value is a constant before putting it in a
- // register.
- bool value_is_constant = result.is_constant();
-
- // Make sure that value, key and receiver are in registers.
- result.ToRegister();
- key.ToRegister();
- receiver.ToRegister();
-
- DeferredReferenceSetKeyedValue* deferred =
- new DeferredReferenceSetKeyedValue(result.reg(),
- key.reg(),
- receiver.reg(),
- tmp.reg(),
- strict_mode_flag());
-
- // Check that the receiver is not a smi.
- __ test(receiver.reg(), Immediate(kSmiTagMask));
- deferred->Branch(zero);
-
- // Check that the key is a smi.
- if (!key.is_smi()) {
- __ test(key.reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
- } else {
- if (FLAG_debug_code) __ AbortIfNotSmi(key.reg());
- }
-
- // Check that the receiver is a JSArray.
- __ CmpObjectType(receiver.reg(), JS_ARRAY_TYPE, tmp.reg());
- deferred->Branch(not_equal);
-
- // Get the elements array from the receiver and check that it is not a
- // dictionary.
- __ mov(tmp.reg(),
- FieldOperand(receiver.reg(), JSArray::kElementsOffset));
-
- // Check whether it is possible to omit the write barrier. If the elements
- // array is in new space or the value written is a smi we can safely update
- // the elements array without write barrier.
- Label in_new_space;
- __ InNewSpace(tmp.reg(), tmp2.reg(), equal, &in_new_space);
- if (!value_is_constant) {
- __ test(result.reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
- }
-
- __ bind(&in_new_space);
- // Bind the deferred code patch site to be able to locate the fixed
- // array map comparison. When debugging, we patch this comparison to
- // always fail so that we will hit the IC call in the deferred code
- // which will allow the debugger to break for fast case stores.
- __ bind(deferred->patch_site());
- __ cmp(FieldOperand(tmp.reg(), HeapObject::kMapOffset),
- Immediate(FACTORY->fixed_array_map()));
- deferred->Branch(not_equal);
-
- // Check that the key is within bounds. Both the key and the length of
- // the JSArray are smis (because the fixed array check above ensures the
- // elements are in fast case). Use unsigned comparison to handle negative
- // keys.
- __ cmp(key.reg(),
- FieldOperand(receiver.reg(), JSArray::kLengthOffset));
- deferred->Branch(above_equal);
-
- // Store the value.
- __ mov(FixedArrayElementOperand(tmp.reg(), key.reg()), result.reg());
- __ IncrementCounter(masm_->isolate()->counters()->keyed_store_inline(), 1);
-
- deferred->BindExit();
- } else {
- result = frame()->CallKeyedStoreIC(strict_mode_flag());
- // Make sure that we do not have a test instruction after the
- // call. A test instruction after the call is used to
- // indicate that we have generated an inline version of the
- // keyed store.
- __ nop();
- }
- ASSERT(frame()->height() == original_height - 3);
- return result;
-}
-
-
-#undef __
-#define __ ACCESS_MASM(masm)
-
-
-Handle<String> Reference::GetName() {
- ASSERT(type_ == NAMED);
- Property* property = expression_->AsProperty();
- if (property == NULL) {
- // Global variable reference treated as a named property reference.
- VariableProxy* proxy = expression_->AsVariableProxy();
- ASSERT(proxy->AsVariable() != NULL);
- ASSERT(proxy->AsVariable()->is_global());
- return proxy->name();
- } else {
- Literal* raw_name = property->key()->AsLiteral();
- ASSERT(raw_name != NULL);
- return Handle<String>::cast(raw_name->handle());
- }
-}
-
-
-void Reference::GetValue() {
- ASSERT(!cgen_->in_spilled_code());
- ASSERT(cgen_->HasValidEntryRegisters());
- ASSERT(!is_illegal());
- MacroAssembler* masm = cgen_->masm();
-
- // Record the source position for the property load.
- Property* property = expression_->AsProperty();
- if (property != NULL) {
- cgen_->CodeForSourcePosition(property->position());
- }
-
- switch (type_) {
- case SLOT: {
- Comment cmnt(masm, "[ Load from Slot");
- Slot* slot = expression_->AsVariableProxy()->AsVariable()->AsSlot();
- ASSERT(slot != NULL);
- cgen_->LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
- if (!persist_after_get_) set_unloaded();
- break;
- }
-
- case NAMED: {
- Variable* var = expression_->AsVariableProxy()->AsVariable();
- bool is_global = var != NULL;
- ASSERT(!is_global || var->is_global());
- if (persist_after_get_) cgen_->frame()->Dup();
- Result result = cgen_->EmitNamedLoad(GetName(), is_global);
- if (!persist_after_get_) set_unloaded();
- cgen_->frame()->Push(&result);
- break;
- }
-
- case KEYED: {
- if (persist_after_get_) {
- cgen_->frame()->PushElementAt(1);
- cgen_->frame()->PushElementAt(1);
- }
- Result value = cgen_->EmitKeyedLoad();
- cgen_->frame()->Push(&value);
- if (!persist_after_get_) set_unloaded();
- break;
- }
-
- default:
- UNREACHABLE();
- }
-}
-
-
-void Reference::TakeValue() {
- // For non-constant frame-allocated slots, we invalidate the value in the
- // slot. For all others, we fall back on GetValue.
- ASSERT(!cgen_->in_spilled_code());
- ASSERT(!is_illegal());
- if (type_ != SLOT) {
- GetValue();
- return;
- }
-
- Slot* slot = expression_->AsVariableProxy()->AsVariable()->AsSlot();
- ASSERT(slot != NULL);
- if (slot->type() == Slot::LOOKUP ||
- slot->type() == Slot::CONTEXT ||
- slot->var()->mode() == Variable::CONST ||
- slot->is_arguments()) {
- GetValue();
- return;
- }
-
- // Only non-constant, frame-allocated parameters and locals can
- // reach here. Be careful not to use the optimizations for arguments
- // object access since it may not have been initialized yet.
- ASSERT(!slot->is_arguments());
- if (slot->type() == Slot::PARAMETER) {
- cgen_->frame()->TakeParameterAt(slot->index());
- } else {
- ASSERT(slot->type() == Slot::LOCAL);
- cgen_->frame()->TakeLocalAt(slot->index());
- }
-
- ASSERT(persist_after_get_);
- // Do not unload the reference, because it is used in SetValue.
-}
-
-
-void Reference::SetValue(InitState init_state) {
- ASSERT(cgen_->HasValidEntryRegisters());
- ASSERT(!is_illegal());
- MacroAssembler* masm = cgen_->masm();
- switch (type_) {
- case SLOT: {
- Comment cmnt(masm, "[ Store to Slot");
- Slot* slot = expression_->AsVariableProxy()->AsVariable()->AsSlot();
- ASSERT(slot != NULL);
- cgen_->StoreToSlot(slot, init_state);
- set_unloaded();
- break;
- }
-
- case NAMED: {
- Comment cmnt(masm, "[ Store to named Property");
- Result answer = cgen_->EmitNamedStore(GetName(), false);
- cgen_->frame()->Push(&answer);
- set_unloaded();
- break;
- }
-
- case KEYED: {
- Comment cmnt(masm, "[ Store to keyed Property");
- Property* property = expression()->AsProperty();
- ASSERT(property != NULL);
-
- Result answer = cgen_->EmitKeyedStore(property->key()->type());
- cgen_->frame()->Push(&answer);
- set_unloaded();
- break;
- }
-
- case UNLOADED:
- case ILLEGAL:
- UNREACHABLE();
- }
-}
-
-
-#undef __
-
#define __ masm.
-
static void MemCopyWrapper(void* dest, const void* src, size_t size) {
memcpy(dest, src, size);
}
-MemCopyFunction CreateMemCopyFunction() {
- HandleScope scope;
- MacroAssembler masm(NULL, 1 * KB);
+OS::MemCopyFunction CreateMemCopyFunction() {
+ size_t actual_size;
+ // Allocate buffer in executable space.
+ byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB,
+ &actual_size,
+ true));
+ if (buffer == NULL) return &MemCopyWrapper;
+ MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
// Generated code is put into a fixed, unmovable, buffer, and not into
// the V8 heap. We can't, and don't, refer to any relocatable addresses
@@ -10198,13 +84,13 @@
if (FLAG_debug_code) {
__ cmp(Operand(esp, kSizeOffset + stack_offset),
- Immediate(kMinComplexMemCopy));
+ Immediate(OS::kMinComplexMemCopy));
Label ok;
__ j(greater_equal, &ok);
__ int3();
__ bind(&ok);
}
- if (masm.isolate()->cpu_features()->IsSupported(SSE2)) {
+ if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope enable(SSE2);
__ push(edi);
__ push(esi);
@@ -10232,7 +118,6 @@
__ test(Operand(src), Immediate(0x0F));
__ j(not_zero, &unaligned_source);
{
- __ IncrementCounter(masm.isolate()->counters()->memcopy_aligned(), 1);
// Copy loop for aligned source and destination.
__ mov(edx, count);
Register loop_count = ecx;
@@ -10280,7 +165,6 @@
// Copy loop for unaligned source and aligned destination.
// If source is not aligned, we can't read it as efficiently.
__ bind(&unaligned_source);
- __ IncrementCounter(masm.isolate()->counters()->memcopy_unaligned(), 1);
__ mov(edx, ecx);
Register loop_count = ecx;
Register count = edx;
@@ -10324,7 +208,6 @@
}
} else {
- __ IncrementCounter(masm.isolate()->counters()->memcopy_noxmm(), 1);
// SSE2 not supported. Unlikely to happen in practice.
__ push(edi);
__ push(esi);
@@ -10371,13 +254,8 @@
masm.GetCode(&desc);
ASSERT(desc.reloc_size == 0);
- // Copy the generated code into an executable chunk and return a pointer
- // to the first instruction in it as a C++ function pointer.
- LargeObjectChunk* chunk = LargeObjectChunk::New(desc.instr_size, EXECUTABLE);
- if (chunk == NULL) return &MemCopyWrapper;
- memcpy(chunk->GetStartAddress(), desc.buffer, desc.instr_size);
- CPU::FlushICache(chunk->GetStartAddress(), desc.instr_size);
- return FUNCTION_CAST<MemCopyFunction>(chunk->GetStartAddress());
+ CPU::FlushICache(buffer, actual_size);
+ return FUNCTION_CAST<OS::MemCopyFunction>(buffer);
}
#undef __
diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h
index acd651b..8f090b1 100644
--- a/src/ia32/codegen-ia32.h
+++ b/src/ia32/codegen-ia32.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -30,275 +30,18 @@
#include "ast.h"
#include "ic-inl.h"
-#include "jump-target-heavy.h"
namespace v8 {
namespace internal {
// Forward declarations
class CompilationInfo;
-class DeferredCode;
-class FrameRegisterState;
-class RegisterAllocator;
-class RegisterFile;
-class RuntimeCallHelper;
-
-
-// -------------------------------------------------------------------------
-// Reference support
-
-// A reference is a C++ stack-allocated object that puts a
-// reference on the virtual frame. The reference may be consumed
-// by GetValue, TakeValue and SetValue.
-// When the lifetime (scope) of a valid reference ends, it must have
-// been consumed, and be in state UNLOADED.
-class Reference BASE_EMBEDDED {
- public:
- // The values of the types is important, see size().
- enum Type { UNLOADED = -2, ILLEGAL = -1, SLOT = 0, NAMED = 1, KEYED = 2 };
- Reference(CodeGenerator* cgen,
- Expression* expression,
- bool persist_after_get = false);
- ~Reference();
-
- Expression* expression() const { return expression_; }
- Type type() const { return type_; }
- void set_type(Type value) {
- ASSERT_EQ(ILLEGAL, type_);
- type_ = value;
- }
-
- void set_unloaded() {
- ASSERT_NE(ILLEGAL, type_);
- ASSERT_NE(UNLOADED, type_);
- type_ = UNLOADED;
- }
- // The size the reference takes up on the stack.
- int size() const {
- return (type_ < SLOT) ? 0 : type_;
- }
-
- bool is_illegal() const { return type_ == ILLEGAL; }
- bool is_slot() const { return type_ == SLOT; }
- bool is_property() const { return type_ == NAMED || type_ == KEYED; }
- bool is_unloaded() const { return type_ == UNLOADED; }
-
- // Return the name. Only valid for named property references.
- Handle<String> GetName();
-
- // Generate code to push the value of the reference on top of the
- // expression stack. The reference is expected to be already on top of
- // the expression stack, and it is consumed by the call unless the
- // reference is for a compound assignment.
- // If the reference is not consumed, it is left in place under its value.
- void GetValue();
-
- // Like GetValue except that the slot is expected to be written to before
- // being read from again. The value of the reference may be invalidated,
- // causing subsequent attempts to read it to fail.
- void TakeValue();
-
- // Generate code to store the value on top of the expression stack in the
- // reference. The reference is expected to be immediately below the value
- // on the expression stack. The value is stored in the location specified
- // by the reference, and is left on top of the stack, after the reference
- // is popped from beneath it (unloaded).
- void SetValue(InitState init_state);
-
- private:
- CodeGenerator* cgen_;
- Expression* expression_;
- Type type_;
- // Keep the reference on the stack after get, so it can be used by set later.
- bool persist_after_get_;
-};
-
-
-// -------------------------------------------------------------------------
-// Control destinations.
-
-// A control destination encapsulates a pair of jump targets and a
-// flag indicating which one is the preferred fall-through. The
-// preferred fall-through must be unbound, the other may be already
-// bound (ie, a backward target).
-//
-// The true and false targets may be jumped to unconditionally or
-// control may split conditionally. Unconditional jumping and
-// splitting should be emitted in tail position (as the last thing
-// when compiling an expression) because they can cause either label
-// to be bound or the non-fall through to be jumped to leaving an
-// invalid virtual frame.
-//
-// The labels in the control destination can be extracted and
-// manipulated normally without affecting the state of the
-// destination.
-
-class ControlDestination BASE_EMBEDDED {
- public:
- ControlDestination(JumpTarget* true_target,
- JumpTarget* false_target,
- bool true_is_fall_through)
- : true_target_(true_target),
- false_target_(false_target),
- true_is_fall_through_(true_is_fall_through),
- is_used_(false) {
- ASSERT(true_is_fall_through ? !true_target->is_bound()
- : !false_target->is_bound());
- }
-
- // Accessors for the jump targets. Directly jumping or branching to
- // or binding the targets will not update the destination's state.
- JumpTarget* true_target() const { return true_target_; }
- JumpTarget* false_target() const { return false_target_; }
-
- // True if the the destination has been jumped to unconditionally or
- // control has been split to both targets. This predicate does not
- // test whether the targets have been extracted and manipulated as
- // raw jump targets.
- bool is_used() const { return is_used_; }
-
- // True if the destination is used and the true target (respectively
- // false target) was the fall through. If the target is backward,
- // "fall through" included jumping unconditionally to it.
- bool true_was_fall_through() const {
- return is_used_ && true_is_fall_through_;
- }
-
- bool false_was_fall_through() const {
- return is_used_ && !true_is_fall_through_;
- }
-
- // Emit a branch to one of the true or false targets, and bind the
- // other target. Because this binds the fall-through target, it
- // should be emitted in tail position (as the last thing when
- // compiling an expression).
- void Split(Condition cc) {
- ASSERT(!is_used_);
- if (true_is_fall_through_) {
- false_target_->Branch(NegateCondition(cc));
- true_target_->Bind();
- } else {
- true_target_->Branch(cc);
- false_target_->Bind();
- }
- is_used_ = true;
- }
-
- // Emit an unconditional jump in tail position, to the true target
- // (if the argument is true) or the false target. The "jump" will
- // actually bind the jump target if it is forward, jump to it if it
- // is backward.
- void Goto(bool where) {
- ASSERT(!is_used_);
- JumpTarget* target = where ? true_target_ : false_target_;
- if (target->is_bound()) {
- target->Jump();
- } else {
- target->Bind();
- }
- is_used_ = true;
- true_is_fall_through_ = where;
- }
-
- // Mark this jump target as used as if Goto had been called, but
- // without generating a jump or binding a label (the control effect
- // should have already happened). This is used when the left
- // subexpression of the short-circuit boolean operators are
- // compiled.
- void Use(bool where) {
- ASSERT(!is_used_);
- ASSERT((where ? true_target_ : false_target_)->is_bound());
- is_used_ = true;
- true_is_fall_through_ = where;
- }
-
- // Swap the true and false targets but keep the same actual label as
- // the fall through. This is used when compiling negated
- // expressions, where we want to swap the targets but preserve the
- // state.
- void Invert() {
- JumpTarget* temp_target = true_target_;
- true_target_ = false_target_;
- false_target_ = temp_target;
-
- true_is_fall_through_ = !true_is_fall_through_;
- }
-
- private:
- // True and false jump targets.
- JumpTarget* true_target_;
- JumpTarget* false_target_;
-
- // Before using the destination: true if the true target is the
- // preferred fall through, false if the false target is. After
- // using the destination: true if the true target was actually used
- // as the fall through, false if the false target was.
- bool true_is_fall_through_;
-
- // True if the Split or Goto functions have been called.
- bool is_used_;
-};
-
-
-// -------------------------------------------------------------------------
-// Code generation state
-
-// The state is passed down the AST by the code generator (and back up, in
-// the form of the state of the jump target pair). It is threaded through
-// the call stack. Constructing a state implicitly pushes it on the owning
-// code generator's stack of states, and destroying one implicitly pops it.
-//
-// The code generator state is only used for expressions, so statements have
-// the initial state.
-
-class CodeGenState BASE_EMBEDDED {
- public:
- // Create an initial code generator state. Destroying the initial state
- // leaves the code generator with a NULL state.
- explicit CodeGenState(CodeGenerator* owner);
-
- // Create a code generator state based on a code generator's current
- // state. The new state has its own control destination.
- CodeGenState(CodeGenerator* owner, ControlDestination* destination);
-
- // Destroy a code generator state and restore the owning code generator's
- // previous state.
- ~CodeGenState();
-
- // Accessors for the state.
- ControlDestination* destination() const { return destination_; }
-
- private:
- // The owning code generator.
- CodeGenerator* owner_;
-
- // A control destination in case the expression has a control-flow
- // effect.
- ControlDestination* destination_;
-
- // The previous state of the owning code generator, restored when
- // this state is destroyed.
- CodeGenState* previous_;
-};
-
-
-// -------------------------------------------------------------------------
-// Arguments allocation mode.
-
-enum ArgumentsAllocationMode {
- NO_ARGUMENTS_ALLOCATION,
- EAGER_ARGUMENTS_ALLOCATION,
- LAZY_ARGUMENTS_ALLOCATION
-};
-
// -------------------------------------------------------------------------
// CodeGenerator
-class CodeGenerator: public AstVisitor {
+class CodeGenerator {
public:
- static bool MakeCode(CompilationInfo* info);
-
// Printing of AST, etc. as requested by flags.
static void MakeCodePrologue(CompilationInfo* info);
@@ -318,33 +61,7 @@
int pos,
bool right_here = false);
- // Accessors
- MacroAssembler* masm() { return masm_; }
- VirtualFrame* frame() const { return frame_; }
- inline Handle<Script> script();
- bool has_valid_frame() const { return frame_ != NULL; }
-
- // Set the virtual frame to be new_frame, with non-frame register
- // reference counts given by non_frame_registers. The non-frame
- // register reference counts of the old frame are returned in
- // non_frame_registers.
- void SetFrame(VirtualFrame* new_frame, RegisterFile* non_frame_registers);
-
- void DeleteFrame();
-
- RegisterAllocator* allocator() const { return allocator_; }
-
- CodeGenState* state() { return state_; }
- void set_state(CodeGenState* state) { state_ = state; }
-
- void AddDeferred(DeferredCode* code) { deferred_.Add(code); }
-
- bool in_spilled_code() const { return in_spilled_code_; }
- void set_in_spilled_code(bool flag) { in_spilled_code_ = flag; }
-
- // Return a position of the element at |index_as_smi| + |additional_offset|
- // in FixedArray pointer to which is held in |array|. |index_as_smi| is Smi.
static Operand FixedArrayElementOperand(Register array,
Register index_as_smi,
int additional_offset = 0) {
@@ -353,445 +70,6 @@
}
private:
- // Type of a member function that generates inline code for a native function.
- typedef void (CodeGenerator::*InlineFunctionGenerator)
- (ZoneList<Expression*>*);
-
- static const InlineFunctionGenerator kInlineFunctionGenerators[];
-
- // Construction/Destruction
- explicit CodeGenerator(MacroAssembler* masm);
-
- // Accessors
- inline bool is_eval();
- inline Scope* scope();
- inline bool is_strict_mode();
- inline StrictModeFlag strict_mode_flag();
-
- // Generating deferred code.
- void ProcessDeferred();
-
- // State
- ControlDestination* destination() const { return state_->destination(); }
-
- // Control of side-effect-free int32 expression compilation.
- bool in_safe_int32_mode() { return in_safe_int32_mode_; }
- void set_in_safe_int32_mode(bool value) { in_safe_int32_mode_ = value; }
- bool safe_int32_mode_enabled() {
- return FLAG_safe_int32_compiler && safe_int32_mode_enabled_;
- }
- void set_safe_int32_mode_enabled(bool value) {
- safe_int32_mode_enabled_ = value;
- }
- void set_unsafe_bailout(BreakTarget* unsafe_bailout) {
- unsafe_bailout_ = unsafe_bailout;
- }
-
- // Take the Result that is an untagged int32, and convert it to a tagged
- // Smi or HeapNumber. Remove the untagged_int32 flag from the result.
- void ConvertInt32ResultToNumber(Result* value);
- void ConvertInt32ResultToSmi(Result* value);
-
- // Track loop nesting level.
- int loop_nesting() const { return loop_nesting_; }
- void IncrementLoopNesting() { loop_nesting_++; }
- void DecrementLoopNesting() { loop_nesting_--; }
-
- // Node visitors.
- void VisitStatements(ZoneList<Statement*>* statements);
-
- virtual void VisitSlot(Slot* node);
-#define DEF_VISIT(type) \
- virtual void Visit##type(type* node);
- AST_NODE_LIST(DEF_VISIT)
-#undef DEF_VISIT
-
- // Visit a statement and then spill the virtual frame if control flow can
- // reach the end of the statement (ie, it does not exit via break,
- // continue, return, or throw). This function is used temporarily while
- // the code generator is being transformed.
- void VisitAndSpill(Statement* statement);
-
- // Visit a list of statements and then spill the virtual frame if control
- // flow can reach the end of the list.
- void VisitStatementsAndSpill(ZoneList<Statement*>* statements);
-
- // Main code generation function
- void Generate(CompilationInfo* info);
-
- // Generate the return sequence code. Should be called no more than
- // once per compiled function, immediately after binding the return
- // target (which can not be done more than once).
- void GenerateReturnSequence(Result* return_value);
-
- // Returns the arguments allocation mode.
- ArgumentsAllocationMode ArgumentsMode();
-
- // Store the arguments object and allocate it if necessary.
- Result StoreArgumentsObject(bool initial);
-
- // The following are used by class Reference.
- void LoadReference(Reference* ref);
-
- Operand SlotOperand(Slot* slot, Register tmp);
-
- Operand ContextSlotOperandCheckExtensions(Slot* slot,
- Result tmp,
- JumpTarget* slow);
-
- // Expressions
- void LoadCondition(Expression* expr,
- ControlDestination* destination,
- bool force_control);
- void Load(Expression* expr);
- void LoadGlobal();
- void LoadGlobalReceiver();
-
- // Generate code to push the value of an expression on top of the frame
- // and then spill the frame fully to memory. This function is used
- // temporarily while the code generator is being transformed.
- void LoadAndSpill(Expression* expression);
-
- // Evaluate an expression and place its value on top of the frame,
- // using, or not using, the side-effect-free expression compiler.
- void LoadInSafeInt32Mode(Expression* expr, BreakTarget* unsafe_bailout);
- void LoadWithSafeInt32ModeDisabled(Expression* expr);
-
- // Read a value from a slot and leave it on top of the expression stack.
- void LoadFromSlot(Slot* slot, TypeofState typeof_state);
- void LoadFromSlotCheckForArguments(Slot* slot, TypeofState typeof_state);
- Result LoadFromGlobalSlotCheckExtensions(Slot* slot,
- TypeofState typeof_state,
- JumpTarget* slow);
-
- // Support for loading from local/global variables and arguments
- // whose location is known unless they are shadowed by
- // eval-introduced bindings. Generates no code for unsupported slot
- // types and therefore expects to fall through to the slow jump target.
- void EmitDynamicLoadFromSlotFastCase(Slot* slot,
- TypeofState typeof_state,
- Result* result,
- JumpTarget* slow,
- JumpTarget* done);
-
- // Store the value on top of the expression stack into a slot, leaving the
- // value in place.
- void StoreToSlot(Slot* slot, InitState init_state);
-
- // Support for compiling assignment expressions.
- void EmitSlotAssignment(Assignment* node);
- void EmitNamedPropertyAssignment(Assignment* node);
- void EmitKeyedPropertyAssignment(Assignment* node);
-
- // Receiver is passed on the frame and consumed.
- Result EmitNamedLoad(Handle<String> name, bool is_contextual);
-
- // If the store is contextual, value is passed on the frame and consumed.
- // Otherwise, receiver and value are passed on the frame and consumed.
- Result EmitNamedStore(Handle<String> name, bool is_contextual);
-
- // Receiver and key are passed on the frame and consumed.
- Result EmitKeyedLoad();
-
- // Receiver, key, and value are passed on the frame and consumed.
- Result EmitKeyedStore(StaticType* key_type);
-
- // Special code for typeof expressions: Unfortunately, we must
- // be careful when loading the expression in 'typeof'
- // expressions. We are not allowed to throw reference errors for
- // non-existing properties of the global object, so we must make it
- // look like an explicit property access, instead of an access
- // through the context chain.
- void LoadTypeofExpression(Expression* x);
-
- // Translate the value on top of the frame into control flow to the
- // control destination.
- void ToBoolean(ControlDestination* destination);
-
- // Generate code that computes a shortcutting logical operation.
- void GenerateLogicalBooleanOperation(BinaryOperation* node);
-
- void GenericBinaryOperation(BinaryOperation* expr,
- OverwriteMode overwrite_mode);
-
- // Emits code sequence that jumps to a JumpTarget if the inputs
- // are both smis. Cannot be in MacroAssembler because it takes
- // advantage of TypeInfo to skip unneeded checks.
- // Allocates a temporary register, possibly spilling from the frame,
- // if it needs to check both left and right.
- void JumpIfBothSmiUsingTypeInfo(Result* left,
- Result* right,
- JumpTarget* both_smi);
-
- // Emits code sequence that jumps to deferred code if the inputs
- // are not both smis. Cannot be in MacroAssembler because it takes
- // a deferred code object.
- void JumpIfNotBothSmiUsingTypeInfo(Register left,
- Register right,
- Register scratch,
- TypeInfo left_info,
- TypeInfo right_info,
- DeferredCode* deferred);
-
- // Emits code sequence that jumps to the label if the inputs
- // are not both smis.
- void JumpIfNotBothSmiUsingTypeInfo(Register left,
- Register right,
- Register scratch,
- TypeInfo left_info,
- TypeInfo right_info,
- Label* on_non_smi);
-
- // If possible, combine two constant smi values using op to produce
- // a smi result, and push it on the virtual frame, all at compile time.
- // Returns true if it succeeds. Otherwise it has no effect.
- bool FoldConstantSmis(Token::Value op, int left, int right);
-
- // Emit code to perform a binary operation on a constant
- // smi and a likely smi. Consumes the Result operand.
- Result ConstantSmiBinaryOperation(BinaryOperation* expr,
- Result* operand,
- Handle<Object> constant_operand,
- bool reversed,
- OverwriteMode overwrite_mode);
-
- // Emit code to perform a binary operation on two likely smis.
- // The code to handle smi arguments is produced inline.
- // Consumes the Results left and right.
- Result LikelySmiBinaryOperation(BinaryOperation* expr,
- Result* left,
- Result* right,
- OverwriteMode overwrite_mode);
-
-
- // Emit code to perform a binary operation on two untagged int32 values.
- // The values are on top of the frame, and the result is pushed on the frame.
- void Int32BinaryOperation(BinaryOperation* node);
-
-
- // Generate a stub call from the virtual frame.
- Result GenerateGenericBinaryOpStubCall(GenericBinaryOpStub* stub,
- Result* left,
- Result* right);
-
- void Comparison(AstNode* node,
- Condition cc,
- bool strict,
- ControlDestination* destination);
-
- // If at least one of the sides is a constant smi, generate optimized code.
- void ConstantSmiComparison(Condition cc,
- bool strict,
- ControlDestination* destination,
- Result* left_side,
- Result* right_side,
- bool left_side_constant_smi,
- bool right_side_constant_smi,
- bool is_loop_condition);
-
- void GenerateInlineNumberComparison(Result* left_side,
- Result* right_side,
- Condition cc,
- ControlDestination* dest);
-
- // To prevent long attacker-controlled byte sequences, integer constants
- // from the JavaScript source are loaded in two parts if they are larger
- // than 17 bits.
- static const int kMaxSmiInlinedBits = 17;
- bool IsUnsafeSmi(Handle<Object> value);
- // Load an integer constant x into a register target or into the stack using
- // at most 16 bits of user-controlled data per assembly operation.
- void MoveUnsafeSmi(Register target, Handle<Object> value);
- void StoreUnsafeSmiToLocal(int offset, Handle<Object> value);
- void PushUnsafeSmi(Handle<Object> value);
-
- void CallWithArguments(ZoneList<Expression*>* arguments,
- CallFunctionFlags flags,
- int position);
-
- // An optimized implementation of expressions of the form
- // x.apply(y, arguments). We call x the applicand and y the receiver.
- // The optimization avoids allocating an arguments object if possible.
- void CallApplyLazy(Expression* applicand,
- Expression* receiver,
- VariableProxy* arguments,
- int position);
-
- void CheckStack();
-
- bool CheckForInlineRuntimeCall(CallRuntime* node);
-
- void ProcessDeclarations(ZoneList<Declaration*>* declarations);
-
- // Declare global variables and functions in the given array of
- // name/value pairs.
- void DeclareGlobals(Handle<FixedArray> pairs);
-
- // Instantiate the function based on the shared function info.
- Result InstantiateFunction(Handle<SharedFunctionInfo> function_info,
- bool pretenure);
-
- // Support for types.
- void GenerateIsSmi(ZoneList<Expression*>* args);
- void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
- void GenerateIsArray(ZoneList<Expression*>* args);
- void GenerateIsRegExp(ZoneList<Expression*>* args);
- void GenerateIsObject(ZoneList<Expression*>* args);
- void GenerateIsSpecObject(ZoneList<Expression*>* args);
- void GenerateIsFunction(ZoneList<Expression*>* args);
- void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
- void GenerateIsStringWrapperSafeForDefaultValueOf(
- ZoneList<Expression*>* args);
-
- // Support for construct call checks.
- void GenerateIsConstructCall(ZoneList<Expression*>* args);
-
- // Support for arguments.length and arguments[?].
- void GenerateArgumentsLength(ZoneList<Expression*>* args);
- void GenerateArguments(ZoneList<Expression*>* args);
-
- // Support for accessing the class and value fields of an object.
- void GenerateClassOf(ZoneList<Expression*>* args);
- void GenerateValueOf(ZoneList<Expression*>* args);
- void GenerateSetValueOf(ZoneList<Expression*>* args);
-
- // Fast support for charCodeAt(n).
- void GenerateStringCharCodeAt(ZoneList<Expression*>* args);
-
- // Fast support for string.charAt(n) and string[n].
- void GenerateStringCharFromCode(ZoneList<Expression*>* args);
-
- // Fast support for string.charAt(n) and string[n].
- void GenerateStringCharAt(ZoneList<Expression*>* args);
-
- // Fast support for object equality testing.
- void GenerateObjectEquals(ZoneList<Expression*>* args);
-
- void GenerateLog(ZoneList<Expression*>* args);
-
- void GenerateGetFramePointer(ZoneList<Expression*>* args);
-
- // Fast support for Math.random().
- void GenerateRandomHeapNumber(ZoneList<Expression*>* args);
-
- // Fast support for StringAdd.
- void GenerateStringAdd(ZoneList<Expression*>* args);
-
- // Fast support for SubString.
- void GenerateSubString(ZoneList<Expression*>* args);
-
- // Fast support for StringCompare.
- void GenerateStringCompare(ZoneList<Expression*>* args);
-
- // Support for direct calls from JavaScript to native RegExp code.
- void GenerateRegExpExec(ZoneList<Expression*>* args);
-
- // Construct a RegExp exec result with two in-object properties.
- void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
-
- // Support for fast native caches.
- void GenerateGetFromCache(ZoneList<Expression*>* args);
-
- // Fast support for number to string.
- void GenerateNumberToString(ZoneList<Expression*>* args);
-
- // Fast swapping of elements. Takes three expressions, the object and two
- // indices. This should only be used if the indices are known to be
- // non-negative and within bounds of the elements array at the call site.
- void GenerateSwapElements(ZoneList<Expression*>* args);
-
- // Fast call for custom callbacks.
- void GenerateCallFunction(ZoneList<Expression*>* args);
-
- // Fast call to math functions.
- void GenerateMathPow(ZoneList<Expression*>* args);
- void GenerateMathSin(ZoneList<Expression*>* args);
- void GenerateMathCos(ZoneList<Expression*>* args);
- void GenerateMathSqrt(ZoneList<Expression*>* args);
- void GenerateMathLog(ZoneList<Expression*>* args);
-
- // Check whether two RegExps are equivalent.
- void GenerateIsRegExpEquivalent(ZoneList<Expression*>* args);
-
- void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args);
- void GenerateGetCachedArrayIndex(ZoneList<Expression*>* args);
- void GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args);
-
- // Simple condition analysis.
- enum ConditionAnalysis {
- ALWAYS_TRUE,
- ALWAYS_FALSE,
- DONT_KNOW
- };
- ConditionAnalysis AnalyzeCondition(Expression* cond);
-
- // Methods used to indicate which source code is generated for. Source
- // positions are collected by the assembler and emitted with the relocation
- // information.
- void CodeForFunctionPosition(FunctionLiteral* fun);
- void CodeForReturnPosition(FunctionLiteral* fun);
- void CodeForStatementPosition(Statement* stmt);
- void CodeForDoWhileConditionPosition(DoWhileStatement* stmt);
- void CodeForSourcePosition(int pos);
-
- void SetTypeForStackSlot(Slot* slot, TypeInfo info);
-
-#ifdef DEBUG
- // True if the registers are valid for entry to a block. There should
- // be no frame-external references to (non-reserved) registers.
- bool HasValidEntryRegisters();
-#endif
-
- ZoneList<DeferredCode*> deferred_;
-
- // Assembler
- MacroAssembler* masm_; // to generate code
-
- CompilationInfo* info_;
-
- // Code generation state
- VirtualFrame* frame_;
- RegisterAllocator* allocator_;
- CodeGenState* state_;
- int loop_nesting_;
- bool in_safe_int32_mode_;
- bool safe_int32_mode_enabled_;
-
- // Jump targets.
- // The target of the return from the function.
- BreakTarget function_return_;
- // The target of the bailout from a side-effect-free int32 subexpression.
- BreakTarget* unsafe_bailout_;
-
- // True if the function return is shadowed (ie, jumping to the target
- // function_return_ does not jump to the true function return, but rather
- // to some unlinking code).
- bool function_return_is_shadowed_;
-
- // True when we are in code that expects the virtual frame to be fully
- // spilled. Some virtual frame function are disabled in DEBUG builds when
- // called from spilled code, because they do not leave the virtual frame
- // in a spilled state.
- bool in_spilled_code_;
-
- // A cookie that is used for JIT IMM32 Encoding. Initialized to a
- // random number when the command-line
- // FLAG_mask_constants_with_cookie is true, zero otherwise.
- int jit_cookie_;
-
- friend class VirtualFrame;
- friend class Isolate;
- friend class JumpTarget;
- friend class Reference;
- friend class Result;
- friend class FastCodeGenerator;
- friend class FullCodeGenerator;
- friend class FullCodeGenSyntaxChecker;
- friend class LCodeGen;
-
- friend class CodeGeneratorPatcher; // Used in test-log-stack-tracer.cc
- friend class InlineRuntimeFunctionsTable;
-
DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
};
diff --git a/src/ia32/cpu-ia32.cc b/src/ia32/cpu-ia32.cc
index 286ed7b..615dbfe 100644
--- a/src/ia32/cpu-ia32.cc
+++ b/src/ia32/cpu-ia32.cc
@@ -42,12 +42,12 @@
namespace internal {
void CPU::Setup() {
- CpuFeatures* cpu_features = Isolate::Current()->cpu_features();
- cpu_features->Clear();
- cpu_features->Probe(true);
- if (!cpu_features->IsSupported(SSE2) || Serializer::enabled()) {
- V8::DisableCrankshaft();
- }
+ CpuFeatures::Probe();
+}
+
+
+bool CPU::SupportsCrankshaft() {
+ return CpuFeatures::IsSupported(SSE2);
}
diff --git a/src/ia32/debug-ia32.cc b/src/ia32/debug-ia32.cc
index 33c5251..2389948 100644
--- a/src/ia32/debug-ia32.cc
+++ b/src/ia32/debug-ia32.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -29,7 +29,7 @@
#if defined(V8_TARGET_ARCH_IA32)
-#include "codegen-inl.h"
+#include "codegen.h"
#include "debug.h"
diff --git a/src/ia32/deoptimizer-ia32.cc b/src/ia32/deoptimizer-ia32.cc
index c6342d7..72fdac8 100644
--- a/src/ia32/deoptimizer-ia32.cc
+++ b/src/ia32/deoptimizer-ia32.cc
@@ -641,14 +641,16 @@
__ neg(edx);
// Allocate a new deoptimizer object.
- __ PrepareCallCFunction(5, eax);
+ __ PrepareCallCFunction(6, eax);
__ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
__ mov(Operand(esp, 0 * kPointerSize), eax); // Function.
__ mov(Operand(esp, 1 * kPointerSize), Immediate(type())); // Bailout type.
__ mov(Operand(esp, 2 * kPointerSize), ebx); // Bailout id.
__ mov(Operand(esp, 3 * kPointerSize), ecx); // Code address or 0.
__ mov(Operand(esp, 4 * kPointerSize), edx); // Fp-to-sp delta.
- __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate), 5);
+ __ mov(Operand(esp, 5 * kPointerSize),
+ Immediate(ExternalReference::isolate_address()));
+ __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate), 6);
// Preserve deoptimizer object in register eax and get the input
// frame descriptor pointer.
diff --git a/src/ia32/frames-ia32.h b/src/ia32/frames-ia32.h
index 8084694..0f95abd 100644
--- a/src/ia32/frames-ia32.h
+++ b/src/ia32/frames-ia32.h
@@ -108,7 +108,7 @@
public:
// FP-relative.
static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset;
- static const int kSavedRegistersOffset = +2 * kPointerSize;
+ static const int kLastParameterOffset = +2 * kPointerSize;
static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset;
// Caller SP-relative.
diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc
index 16c39c5..69d5e77 100644
--- a/src/ia32/full-codegen-ia32.cc
+++ b/src/ia32/full-codegen-ia32.cc
@@ -30,7 +30,7 @@
#if defined(V8_TARGET_ARCH_IA32)
#include "code-stubs.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "compiler.h"
#include "debug.h"
#include "full-codegen.h"
@@ -231,7 +231,7 @@
}
{ Comment cmnt(masm_, "[ Stack check");
- PrepareForBailout(info->function(), NO_REGISTERS);
+ PrepareForBailoutForId(AstNode::kFunctionEntryId, NO_REGISTERS);
NearLabel ok;
ExternalReference stack_limit =
ExternalReference::address_of_stack_limit(isolate());
@@ -773,7 +773,7 @@
// Compile all the tests with branches to their bodies.
for (int i = 0; i < clauses->length(); i++) {
CaseClause* clause = clauses->at(i);
- clause->body_target()->entry_label()->Unuse();
+ clause->body_target()->Unuse();
// The default is not a test, but remember it as final fall through.
if (clause->is_default()) {
@@ -801,7 +801,7 @@
__ cmp(edx, Operand(eax));
__ j(not_equal, &next_test);
__ Drop(1); // Switch value is no longer needed.
- __ jmp(clause->body_target()->entry_label());
+ __ jmp(clause->body_target());
__ bind(&slow_case);
}
@@ -812,7 +812,7 @@
__ test(eax, Operand(eax));
__ j(not_equal, &next_test);
__ Drop(1); // Switch value is no longer needed.
- __ jmp(clause->body_target()->entry_label());
+ __ jmp(clause->body_target());
}
// Discard the test value and jump to the default if present, otherwise to
@@ -822,14 +822,14 @@
if (default_clause == NULL) {
__ jmp(nested_statement.break_target());
} else {
- __ jmp(default_clause->body_target()->entry_label());
+ __ jmp(default_clause->body_target());
}
// Compile all the case bodies.
for (int i = 0; i < clauses->length(); i++) {
Comment cmnt(masm_, "[ Case body");
CaseClause* clause = clauses->at(i);
- __ bind(clause->body_target()->entry_label());
+ __ bind(clause->body_target());
PrepareForBailoutForId(clause->EntryId(), NO_REGISTERS);
VisitStatements(clause->statements());
}
@@ -1563,27 +1563,26 @@
}
}
+ // For compound assignments we need another deoptimization point after the
+ // variable/property load.
if (expr->is_compound()) {
{ AccumulatorValueContext context(this);
switch (assign_type) {
case VARIABLE:
EmitVariableLoad(expr->target()->AsVariableProxy()->var());
+ PrepareForBailout(expr->target(), TOS_REG);
break;
case NAMED_PROPERTY:
EmitNamedPropertyLoad(property);
+ PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
break;
case KEYED_PROPERTY:
EmitKeyedPropertyLoad(property);
+ PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
break;
}
}
- // For property compound assignments we need another deoptimization
- // point after the property load.
- if (property != NULL) {
- PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
- }
-
Token::Value op = expr->binary_op();
__ push(eax); // Left operand goes on the stack.
VisitForAccumulatorValue(expr->value());
@@ -2268,15 +2267,6 @@
}
}
} else {
- // Call to some other expression. If the expression is an anonymous
- // function literal not called in a loop, mark it as one that should
- // also use the full code generator.
- FunctionLiteral* lit = fun->AsFunctionLiteral();
- if (lit != NULL &&
- lit->name()->Equals(isolate()->heap()->empty_string()) &&
- loop_depth() == 0) {
- lit->set_try_full_codegen(true);
- }
{ PreservePositionScope scope(masm()->positions_recorder());
VisitForStackValue(fun);
}
@@ -2458,10 +2448,73 @@
context()->PrepareTest(&materialize_true, &materialize_false,
&if_true, &if_false, &fall_through);
- // TODO(3110205): Implement this.
- // Currently unimplemented. Emit false, a safe choice.
+ if (FLAG_debug_code) __ AbortIfSmi(eax);
+
+ // Check whether this map has already been checked to be safe for default
+ // valueOf.
+ __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
+ __ test_b(FieldOperand(ebx, Map::kBitField2Offset),
+ 1 << Map::kStringWrapperSafeForDefaultValueOf);
+ __ j(not_zero, if_true);
+
+ // Check for fast case object. Return false for slow case objects.
+ __ mov(ecx, FieldOperand(eax, JSObject::kPropertiesOffset));
+ __ mov(ecx, FieldOperand(ecx, HeapObject::kMapOffset));
+ __ cmp(ecx, FACTORY->hash_table_map());
+ __ j(equal, if_false);
+
+ // Look for valueOf symbol in the descriptor array, and indicate false if
+ // found. The type is not checked, so if it is a transition it is a false
+ // negative.
+ __ mov(ebx, FieldOperand(ebx, Map::kInstanceDescriptorsOffset));
+ __ mov(ecx, FieldOperand(ebx, FixedArray::kLengthOffset));
+ // ebx: descriptor array
+ // ecx: length of descriptor array
+ // Calculate the end of the descriptor array.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize == 1);
+ STATIC_ASSERT(kPointerSize == 4);
+ __ lea(ecx, Operand(ebx, ecx, times_2, FixedArray::kHeaderSize));
+ // Calculate location of the first key name.
+ __ add(Operand(ebx),
+ Immediate(FixedArray::kHeaderSize +
+ DescriptorArray::kFirstIndex * kPointerSize));
+ // Loop through all the keys in the descriptor array. If one of these is the
+ // symbol valueOf the result is false.
+ Label entry, loop;
+ __ jmp(&entry);
+ __ bind(&loop);
+ __ mov(edx, FieldOperand(ebx, 0));
+ __ cmp(edx, FACTORY->value_of_symbol());
+ __ j(equal, if_false);
+ __ add(Operand(ebx), Immediate(kPointerSize));
+ __ bind(&entry);
+ __ cmp(ebx, Operand(ecx));
+ __ j(not_equal, &loop);
+
+ // Reload map as register ebx was used as temporary above.
+ __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
+
+ // If a valueOf property is not found on the object check that it's
+ // prototype is the un-modified String prototype. If not result is false.
+ __ mov(ecx, FieldOperand(ebx, Map::kPrototypeOffset));
+ __ test(ecx, Immediate(kSmiTagMask));
+ __ j(zero, if_false);
+ __ mov(ecx, FieldOperand(ecx, HeapObject::kMapOffset));
+ __ mov(edx, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ __ mov(edx,
+ FieldOperand(edx, GlobalObject::kGlobalContextOffset));
+ __ cmp(ecx,
+ ContextOperand(edx,
+ Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX));
+ __ j(not_equal, if_false);
+ // Set the bit in the map to indicate that it has been checked safe for
+ // default valueOf and set true result.
+ __ or_(FieldOperand(ebx, Map::kBitField2Offset),
+ Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf));
+ __ jmp(if_true);
+
PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
- __ jmp(if_false);
context()->Plug(if_true, if_false);
}
@@ -2717,15 +2770,16 @@
__ bind(&heapnumber_allocated);
- __ PrepareCallCFunction(0, ebx);
+ __ PrepareCallCFunction(1, ebx);
+ __ mov(Operand(esp, 0), Immediate(ExternalReference::isolate_address()));
__ CallCFunction(ExternalReference::random_uint32_function(isolate()),
- 0);
+ 1);
// Convert 32 random bits in eax to 0.(32 random bits) in a double
// by computing:
// ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
// This is implemented on both SSE2 and FPU.
- if (isolate()->cpu_features()->IsSupported(SSE2)) {
+ if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope fscope(SSE2);
__ mov(ebx, Immediate(0x49800000)); // 1.0 x 2^20 as single.
__ movd(xmm1, Operand(ebx));
@@ -2800,7 +2854,7 @@
VisitForStackValue(args->at(0));
VisitForStackValue(args->at(1));
- if (isolate()->cpu_features()->IsSupported(SSE2)) {
+ if (CpuFeatures::IsSupported(SSE2)) {
MathPowStub stub;
__ CallStub(&stub);
} else {
@@ -3033,15 +3087,14 @@
void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) {
ASSERT(args->length() >= 2);
- int arg_count = args->length() - 2; // For receiver and function.
- VisitForStackValue(args->at(0)); // Receiver.
- for (int i = 0; i < arg_count; i++) {
- VisitForStackValue(args->at(i + 1));
+ int arg_count = args->length() - 2; // 2 ~ receiver and function.
+ for (int i = 0; i < arg_count + 1; ++i) {
+ VisitForStackValue(args->at(i));
}
- VisitForAccumulatorValue(args->at(arg_count + 1)); // Function.
+ VisitForAccumulatorValue(args->last()); // Function.
- // InvokeFunction requires function in edi. Move it in there.
- if (!result_register().is(edi)) __ mov(edi, result_register());
+ // InvokeFunction requires the function in edi. Move it in there.
+ __ mov(edi, result_register());
ParameterCount count(arg_count);
__ InvokeFunction(edi, count, CALL_FUNCTION);
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
@@ -3778,7 +3831,11 @@
// We need a second deoptimization point after loading the value
// in case evaluating the property load my have a side effect.
- PrepareForBailout(expr->increment(), TOS_REG);
+ if (assign_type == VARIABLE) {
+ PrepareForBailout(expr->expression(), TOS_REG);
+ } else {
+ PrepareForBailoutForId(expr->CountId(), TOS_REG);
+ }
// Call ToNumber only if operand is not a smi.
NearLabel no_conversion;
@@ -4196,30 +4253,7 @@
default:
break;
}
-
__ call(ic, mode);
-
- // Crankshaft doesn't need patching of inlined loads and stores.
- // When compiling the snapshot we need to produce code that works
- // with and without Crankshaft.
- if (V8::UseCrankshaft() && !Serializer::enabled()) {
- return;
- }
-
- // If we're calling a (keyed) load or store stub, we have to mark
- // the call as containing no inlined code so we will not attempt to
- // patch it.
- switch (ic->kind()) {
- case Code::LOAD_IC:
- case Code::KEYED_LOAD_IC:
- case Code::STORE_IC:
- case Code::KEYED_STORE_IC:
- __ nop(); // Signals no inlined code.
- break;
- default:
- // Do nothing.
- break;
- }
}
@@ -4240,7 +4274,6 @@
default:
break;
}
-
__ call(ic, RelocInfo::CODE_TARGET);
if (patch_site != NULL && patch_site->is_bound()) {
patch_site->EmitPatchInfo();
diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc
index 48ffc73..4106f01 100644
--- a/src/ia32/ic-ia32.cc
+++ b/src/ia32/ic-ia32.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -29,7 +29,7 @@
#if defined(V8_TARGET_ARCH_IA32)
-#include "codegen-inl.h"
+#include "codegen.h"
#include "ic-inl.h"
#include "runtime.h"
#include "stub-cache.h"
@@ -371,12 +371,6 @@
}
-// The offset from the inlined patch site to the start of the
-// inlined load instruction. It is 7 bytes (test eax, imm) plus
-// 6 bytes (jne slow_label).
-const int LoadIC::kOffsetToLoadInstruction = 13;
-
-
void LoadIC::GenerateArrayLength(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : receiver
@@ -1273,172 +1267,6 @@
}
-bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) {
- if (V8::UseCrankshaft()) return false;
-
- // The address of the instruction following the call.
- Address test_instruction_address =
- address + Assembler::kCallTargetAddressOffset;
- // If the instruction following the call is not a test eax, nothing
- // was inlined.
- if (*test_instruction_address != Assembler::kTestEaxByte) return false;
-
- Address delta_address = test_instruction_address + 1;
- // The delta to the start of the map check instruction.
- int delta = *reinterpret_cast<int*>(delta_address);
-
- // The map address is the last 4 bytes of the 7-byte
- // operand-immediate compare instruction, so we add 3 to get the
- // offset to the last 4 bytes.
- Address map_address = test_instruction_address + delta + 3;
- *(reinterpret_cast<Object**>(map_address)) = map;
-
- // The offset is in the last 4 bytes of a six byte
- // memory-to-register move instruction, so we add 2 to get the
- // offset to the last 4 bytes.
- Address offset_address =
- test_instruction_address + delta + kOffsetToLoadInstruction + 2;
- *reinterpret_cast<int*>(offset_address) = offset - kHeapObjectTag;
- return true;
-}
-
-
-// One byte opcode for mov ecx,0xXXXXXXXX.
-// Marks inlined contextual loads using all kinds of cells. Generated
-// code has the hole check:
-// mov reg, <cell>
-// mov reg, (<cell>, value offset)
-// cmp reg, <the hole>
-// je slow
-// ;; use reg
-static const byte kMovEcxByte = 0xB9;
-
-// One byte opcode for mov edx,0xXXXXXXXX.
-// Marks inlined contextual loads using only "don't delete"
-// cells. Generated code doesn't have the hole check:
-// mov reg, <cell>
-// mov reg, (<cell>, value offset)
-// ;; use reg
-static const byte kMovEdxByte = 0xBA;
-
-bool LoadIC::PatchInlinedContextualLoad(Address address,
- Object* map,
- Object* cell,
- bool is_dont_delete) {
- if (V8::UseCrankshaft()) return false;
-
- // The address of the instruction following the call.
- Address mov_instruction_address =
- address + Assembler::kCallTargetAddressOffset;
- // If the instruction following the call is not a mov ecx/edx,
- // nothing was inlined.
- byte b = *mov_instruction_address;
- if (b != kMovEcxByte && b != kMovEdxByte) return false;
- // If we don't have the hole check generated, we can only support
- // "don't delete" cells.
- if (b == kMovEdxByte && !is_dont_delete) return false;
-
- Address delta_address = mov_instruction_address + 1;
- // The delta to the start of the map check instruction.
- int delta = *reinterpret_cast<int*>(delta_address);
-
- // The map address is the last 4 bytes of the 7-byte
- // operand-immediate compare instruction, so we add 3 to get the
- // offset to the last 4 bytes.
- Address map_address = mov_instruction_address + delta + 3;
- *(reinterpret_cast<Object**>(map_address)) = map;
-
- // The cell is in the last 4 bytes of a five byte mov reg, imm32
- // instruction, so we add 1 to get the offset to the last 4 bytes.
- Address offset_address =
- mov_instruction_address + delta + kOffsetToLoadInstruction + 1;
- *reinterpret_cast<Object**>(offset_address) = cell;
- return true;
-}
-
-
-bool StoreIC::PatchInlinedStore(Address address, Object* map, int offset) {
- if (V8::UseCrankshaft()) return false;
-
- // The address of the instruction following the call.
- Address test_instruction_address =
- address + Assembler::kCallTargetAddressOffset;
-
- // If the instruction following the call is not a test eax, nothing
- // was inlined.
- if (*test_instruction_address != Assembler::kTestEaxByte) return false;
-
- // Extract the encoded deltas from the test eax instruction.
- Address encoded_offsets_address = test_instruction_address + 1;
- int encoded_offsets = *reinterpret_cast<int*>(encoded_offsets_address);
- int delta_to_map_check = -(encoded_offsets & 0xFFFF);
- int delta_to_record_write = encoded_offsets >> 16;
-
- // Patch the map to check. The map address is the last 4 bytes of
- // the 7-byte operand-immediate compare instruction.
- Address map_check_address = test_instruction_address + delta_to_map_check;
- Address map_address = map_check_address + 3;
- *(reinterpret_cast<Object**>(map_address)) = map;
-
- // Patch the offset in the store instruction. The offset is in the
- // last 4 bytes of a six byte register-to-memory move instruction.
- Address offset_address =
- map_check_address + StoreIC::kOffsetToStoreInstruction + 2;
- // The offset should have initial value (kMaxInt - 1), cleared value
- // (-1) or we should be clearing the inlined version.
- ASSERT(*reinterpret_cast<int*>(offset_address) == kMaxInt - 1 ||
- *reinterpret_cast<int*>(offset_address) == -1 ||
- (offset == 0 && map == HEAP->null_value()));
- *reinterpret_cast<int*>(offset_address) = offset - kHeapObjectTag;
-
- // Patch the offset in the write-barrier code. The offset is the
- // last 4 bytes of a six byte lea instruction.
- offset_address = map_check_address + delta_to_record_write + 2;
- // The offset should have initial value (kMaxInt), cleared value
- // (-1) or we should be clearing the inlined version.
- ASSERT(*reinterpret_cast<int*>(offset_address) == kMaxInt ||
- *reinterpret_cast<int*>(offset_address) == -1 ||
- (offset == 0 && map == HEAP->null_value()));
- *reinterpret_cast<int*>(offset_address) = offset - kHeapObjectTag;
-
- return true;
-}
-
-
-static bool PatchInlinedMapCheck(Address address, Object* map) {
- if (V8::UseCrankshaft()) return false;
-
- Address test_instruction_address =
- address + Assembler::kCallTargetAddressOffset;
- // The keyed load has a fast inlined case if the IC call instruction
- // is immediately followed by a test instruction.
- if (*test_instruction_address != Assembler::kTestEaxByte) return false;
-
- // Fetch the offset from the test instruction to the map cmp
- // instruction. This offset is stored in the last 4 bytes of the 5
- // byte test instruction.
- Address delta_address = test_instruction_address + 1;
- int delta = *reinterpret_cast<int*>(delta_address);
- // Compute the map address. The map address is in the last 4 bytes
- // of the 7-byte operand-immediate compare instruction, so we add 3
- // to the offset to get the map address.
- Address map_address = test_instruction_address + delta + 3;
- // Patch the map check.
- *(reinterpret_cast<Object**>(map_address)) = map;
- return true;
-}
-
-
-bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) {
- return PatchInlinedMapCheck(address, map);
-}
-
-
-bool KeyedStoreIC::PatchInlinedStore(Address address, Object* map) {
- return PatchInlinedMapCheck(address, map);
-}
-
-
void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : key
@@ -1519,12 +1347,6 @@
}
-// The offset from the inlined patch site to the start of the inlined
-// store instruction. It is 7 bytes (test reg, imm) plus 6 bytes (jne
-// slow_label).
-const int StoreIC::kOffsetToStoreInstruction = 13;
-
-
void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : value
diff --git a/src/ia32/jump-target-ia32.cc b/src/ia32/jump-target-ia32.cc
deleted file mode 100644
index 76c0d02..0000000
--- a/src/ia32/jump-target-ia32.cc
+++ /dev/null
@@ -1,437 +0,0 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "v8.h"
-
-#if defined(V8_TARGET_ARCH_IA32)
-
-#include "codegen-inl.h"
-#include "jump-target-inl.h"
-#include "register-allocator-inl.h"
-#include "virtual-frame-inl.h"
-
-namespace v8 {
-namespace internal {
-
-// -------------------------------------------------------------------------
-// JumpTarget implementation.
-
-#define __ ACCESS_MASM(cgen()->masm())
-
-void JumpTarget::DoJump() {
- ASSERT(cgen()->has_valid_frame());
- // Live non-frame registers are not allowed at unconditional jumps
- // because we have no way of invalidating the corresponding results
- // which are still live in the C++ code.
- ASSERT(cgen()->HasValidEntryRegisters());
-
- if (is_bound()) {
- // Backward jump. There is an expected frame to merge to.
- ASSERT(direction_ == BIDIRECTIONAL);
- cgen()->frame()->PrepareMergeTo(entry_frame_);
- cgen()->frame()->MergeTo(entry_frame_);
- cgen()->DeleteFrame();
- __ jmp(&entry_label_);
- } else if (entry_frame_ != NULL) {
- // Forward jump with a preconfigured entry frame. Assert the
- // current frame matches the expected one and jump to the block.
- ASSERT(cgen()->frame()->Equals(entry_frame_));
- cgen()->DeleteFrame();
- __ jmp(&entry_label_);
- } else {
- // Forward jump. Remember the current frame and emit a jump to
- // its merge code.
- AddReachingFrame(cgen()->frame());
- RegisterFile empty;
- cgen()->SetFrame(NULL, &empty);
- __ jmp(&merge_labels_.last());
- }
-}
-
-
-void JumpTarget::DoBranch(Condition cc, Hint hint) {
- ASSERT(cgen() != NULL);
- ASSERT(cgen()->has_valid_frame());
-
- if (is_bound()) {
- ASSERT(direction_ == BIDIRECTIONAL);
- // Backward branch. We have an expected frame to merge to on the
- // backward edge.
-
- // Swap the current frame for a copy (we do the swapping to get
- // the off-frame registers off the fall through) to use for the
- // branch.
- VirtualFrame* fall_through_frame = cgen()->frame();
- VirtualFrame* branch_frame = new VirtualFrame(fall_through_frame);
- RegisterFile non_frame_registers;
- cgen()->SetFrame(branch_frame, &non_frame_registers);
-
- // Check if we can avoid merge code.
- cgen()->frame()->PrepareMergeTo(entry_frame_);
- if (cgen()->frame()->Equals(entry_frame_)) {
- // Branch right in to the block.
- cgen()->DeleteFrame();
- __ j(cc, &entry_label_, hint);
- cgen()->SetFrame(fall_through_frame, &non_frame_registers);
- return;
- }
-
- // Check if we can reuse existing merge code.
- for (int i = 0; i < reaching_frames_.length(); i++) {
- if (reaching_frames_[i] != NULL &&
- cgen()->frame()->Equals(reaching_frames_[i])) {
- // Branch to the merge code.
- cgen()->DeleteFrame();
- __ j(cc, &merge_labels_[i], hint);
- cgen()->SetFrame(fall_through_frame, &non_frame_registers);
- return;
- }
- }
-
- // To emit the merge code here, we negate the condition and branch
- // around the merge code on the fall through path.
- Label original_fall_through;
- __ j(NegateCondition(cc), &original_fall_through, NegateHint(hint));
- cgen()->frame()->MergeTo(entry_frame_);
- cgen()->DeleteFrame();
- __ jmp(&entry_label_);
- cgen()->SetFrame(fall_through_frame, &non_frame_registers);
- __ bind(&original_fall_through);
-
- } else if (entry_frame_ != NULL) {
- // Forward branch with a preconfigured entry frame. Assert the
- // current frame matches the expected one and branch to the block.
- ASSERT(cgen()->frame()->Equals(entry_frame_));
- // Explicitly use the macro assembler instead of __ as forward
- // branches are expected to be a fixed size (no inserted
- // coverage-checking instructions please). This is used in
- // Reference::GetValue.
- cgen()->masm()->j(cc, &entry_label_, hint);
-
- } else {
- // Forward branch. A copy of the current frame is remembered and
- // a branch to the merge code is emitted. Explicitly use the
- // macro assembler instead of __ as forward branches are expected
- // to be a fixed size (no inserted coverage-checking instructions
- // please). This is used in Reference::GetValue.
- AddReachingFrame(new VirtualFrame(cgen()->frame()));
- cgen()->masm()->j(cc, &merge_labels_.last(), hint);
- }
-}
-
-
-void JumpTarget::Call() {
- // Call is used to push the address of the catch block on the stack as
- // a return address when compiling try/catch and try/finally. We
- // fully spill the frame before making the call. The expected frame
- // at the label (which should be the only one) is the spilled current
- // frame plus an in-memory return address. The "fall-through" frame
- // at the return site is the spilled current frame.
- ASSERT(cgen() != NULL);
- ASSERT(cgen()->has_valid_frame());
- // There are no non-frame references across the call.
- ASSERT(cgen()->HasValidEntryRegisters());
- ASSERT(!is_linked());
-
- cgen()->frame()->SpillAll();
- VirtualFrame* target_frame = new VirtualFrame(cgen()->frame());
- target_frame->Adjust(1);
- // We do not expect a call with a preconfigured entry frame.
- ASSERT(entry_frame_ == NULL);
- AddReachingFrame(target_frame);
- __ call(&merge_labels_.last());
-}
-
-
-void JumpTarget::DoBind() {
- ASSERT(cgen() != NULL);
- ASSERT(!is_bound());
-
- // Live non-frame registers are not allowed at the start of a basic
- // block.
- ASSERT(!cgen()->has_valid_frame() || cgen()->HasValidEntryRegisters());
-
- // Fast case: the jump target was manually configured with an entry
- // frame to use.
- if (entry_frame_ != NULL) {
- // Assert no reaching frames to deal with.
- ASSERT(reaching_frames_.is_empty());
- ASSERT(!cgen()->has_valid_frame());
-
- RegisterFile empty;
- if (direction_ == BIDIRECTIONAL) {
- // Copy the entry frame so the original can be used for a
- // possible backward jump.
- cgen()->SetFrame(new VirtualFrame(entry_frame_), &empty);
- } else {
- // Take ownership of the entry frame.
- cgen()->SetFrame(entry_frame_, &empty);
- entry_frame_ = NULL;
- }
- __ bind(&entry_label_);
- return;
- }
-
- if (!is_linked()) {
- ASSERT(cgen()->has_valid_frame());
- if (direction_ == FORWARD_ONLY) {
- // Fast case: no forward jumps and no possible backward jumps.
- // The stack pointer can be floating above the top of the
- // virtual frame before the bind. Afterward, it should not.
- VirtualFrame* frame = cgen()->frame();
- int difference = frame->stack_pointer_ - (frame->element_count() - 1);
- if (difference > 0) {
- frame->stack_pointer_ -= difference;
- __ add(Operand(esp), Immediate(difference * kPointerSize));
- }
- } else {
- ASSERT(direction_ == BIDIRECTIONAL);
- // Fast case: no forward jumps, possible backward ones. Remove
- // constants and copies above the watermark on the fall-through
- // frame and use it as the entry frame.
- cgen()->frame()->MakeMergable();
- entry_frame_ = new VirtualFrame(cgen()->frame());
- }
- __ bind(&entry_label_);
- return;
- }
-
- if (direction_ == FORWARD_ONLY &&
- !cgen()->has_valid_frame() &&
- reaching_frames_.length() == 1) {
- // Fast case: no fall-through, a single forward jump, and no
- // possible backward jumps. Pick up the only reaching frame, take
- // ownership of it, and use it for the block about to be emitted.
- VirtualFrame* frame = reaching_frames_[0];
- RegisterFile empty;
- cgen()->SetFrame(frame, &empty);
- reaching_frames_[0] = NULL;
- __ bind(&merge_labels_[0]);
-
- // The stack pointer can be floating above the top of the
- // virtual frame before the bind. Afterward, it should not.
- int difference = frame->stack_pointer_ - (frame->element_count() - 1);
- if (difference > 0) {
- frame->stack_pointer_ -= difference;
- __ add(Operand(esp), Immediate(difference * kPointerSize));
- }
-
- __ bind(&entry_label_);
- return;
- }
-
- // If there is a current frame, record it as the fall-through. It
- // is owned by the reaching frames for now.
- bool had_fall_through = false;
- if (cgen()->has_valid_frame()) {
- had_fall_through = true;
- AddReachingFrame(cgen()->frame()); // Return value ignored.
- RegisterFile empty;
- cgen()->SetFrame(NULL, &empty);
- }
-
- // Compute the frame to use for entry to the block.
- ComputeEntryFrame();
-
- // Some moves required to merge to an expected frame require purely
- // frame state changes, and do not require any code generation.
- // Perform those first to increase the possibility of finding equal
- // frames below.
- for (int i = 0; i < reaching_frames_.length(); i++) {
- if (reaching_frames_[i] != NULL) {
- reaching_frames_[i]->PrepareMergeTo(entry_frame_);
- }
- }
-
- if (is_linked()) {
- // There were forward jumps. Handle merging the reaching frames
- // to the entry frame.
-
- // Loop over the (non-null) reaching frames and process any that
- // need merge code. Iterate backwards through the list to handle
- // the fall-through frame first. Set frames that will be
- // processed after 'i' to NULL if we want to avoid processing
- // them.
- for (int i = reaching_frames_.length() - 1; i >= 0; i--) {
- VirtualFrame* frame = reaching_frames_[i];
-
- if (frame != NULL) {
- // Does the frame (probably) need merge code?
- if (!frame->Equals(entry_frame_)) {
- // We could have a valid frame as the fall through to the
- // binding site or as the fall through from a previous merge
- // code block. Jump around the code we are about to
- // generate.
- if (cgen()->has_valid_frame()) {
- cgen()->DeleteFrame();
- __ jmp(&entry_label_);
- }
- // Pick up the frame for this block. Assume ownership if
- // there cannot be backward jumps.
- RegisterFile empty;
- if (direction_ == BIDIRECTIONAL) {
- cgen()->SetFrame(new VirtualFrame(frame), &empty);
- } else {
- cgen()->SetFrame(frame, &empty);
- reaching_frames_[i] = NULL;
- }
- __ bind(&merge_labels_[i]);
-
- // Loop over the remaining (non-null) reaching frames,
- // looking for any that can share merge code with this one.
- for (int j = 0; j < i; j++) {
- VirtualFrame* other = reaching_frames_[j];
- if (other != NULL && other->Equals(cgen()->frame())) {
- // Set the reaching frame element to null to avoid
- // processing it later, and then bind its entry label.
- reaching_frames_[j] = NULL;
- __ bind(&merge_labels_[j]);
- }
- }
-
- // Emit the merge code.
- cgen()->frame()->MergeTo(entry_frame_);
- } else if (i == reaching_frames_.length() - 1 && had_fall_through) {
- // If this is the fall through frame, and it didn't need
- // merge code, we need to pick up the frame so we can jump
- // around subsequent merge blocks if necessary.
- RegisterFile empty;
- cgen()->SetFrame(frame, &empty);
- reaching_frames_[i] = NULL;
- }
- }
- }
-
- // The code generator may not have a current frame if there was no
- // fall through and none of the reaching frames needed merging.
- // In that case, clone the entry frame as the current frame.
- if (!cgen()->has_valid_frame()) {
- RegisterFile empty;
- cgen()->SetFrame(new VirtualFrame(entry_frame_), &empty);
- }
-
- // There may be unprocessed reaching frames that did not need
- // merge code. They will have unbound merge labels. Bind their
- // merge labels to be the same as the entry label and deallocate
- // them.
- for (int i = 0; i < reaching_frames_.length(); i++) {
- if (!merge_labels_[i].is_bound()) {
- reaching_frames_[i] = NULL;
- __ bind(&merge_labels_[i]);
- }
- }
-
- // There are non-NULL reaching frames with bound labels for each
- // merge block, but only on backward targets.
- } else {
- // There were no forward jumps. There must be a current frame and
- // this must be a bidirectional target.
- ASSERT(reaching_frames_.length() == 1);
- ASSERT(reaching_frames_[0] != NULL);
- ASSERT(direction_ == BIDIRECTIONAL);
-
- // Use a copy of the reaching frame so the original can be saved
- // for possible reuse as a backward merge block.
- RegisterFile empty;
- cgen()->SetFrame(new VirtualFrame(reaching_frames_[0]), &empty);
- __ bind(&merge_labels_[0]);
- cgen()->frame()->MergeTo(entry_frame_);
- }
-
- __ bind(&entry_label_);
-}
-
-
-void BreakTarget::Jump() {
- // Drop leftover statement state from the frame before merging, without
- // emitting code.
- ASSERT(cgen()->has_valid_frame());
- int count = cgen()->frame()->height() - expected_height_;
- cgen()->frame()->ForgetElements(count);
- DoJump();
-}
-
-
-void BreakTarget::Jump(Result* arg) {
- // Drop leftover statement state from the frame before merging, without
- // emitting code.
- ASSERT(cgen()->has_valid_frame());
- int count = cgen()->frame()->height() - expected_height_;
- cgen()->frame()->ForgetElements(count);
- cgen()->frame()->Push(arg);
- DoJump();
-}
-
-
-void BreakTarget::Bind() {
-#ifdef DEBUG
- // All the forward-reaching frames should have been adjusted at the
- // jumps to this target.
- for (int i = 0; i < reaching_frames_.length(); i++) {
- ASSERT(reaching_frames_[i] == NULL ||
- reaching_frames_[i]->height() == expected_height_);
- }
-#endif
- // Drop leftover statement state from the frame before merging, even on
- // the fall through. This is so we can bind the return target with state
- // on the frame.
- if (cgen()->has_valid_frame()) {
- int count = cgen()->frame()->height() - expected_height_;
- cgen()->frame()->ForgetElements(count);
- }
- DoBind();
-}
-
-
-void BreakTarget::Bind(Result* arg) {
-#ifdef DEBUG
- // All the forward-reaching frames should have been adjusted at the
- // jumps to this target.
- for (int i = 0; i < reaching_frames_.length(); i++) {
- ASSERT(reaching_frames_[i] == NULL ||
- reaching_frames_[i]->height() == expected_height_ + 1);
- }
-#endif
- // Drop leftover statement state from the frame before merging, even on
- // the fall through. This is so we can bind the return target with state
- // on the frame.
- if (cgen()->has_valid_frame()) {
- int count = cgen()->frame()->height() - expected_height_;
- cgen()->frame()->ForgetElements(count);
- cgen()->frame()->Push(arg);
- }
- DoBind();
- *arg = cgen()->frame()->Pop();
-}
-
-
-#undef __
-
-
-} } // namespace v8::internal
-
-#endif // V8_TARGET_ARCH_IA32
diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc
index 1691098..46c71e8 100644
--- a/src/ia32/lithium-codegen-ia32.cc
+++ b/src/ia32/lithium-codegen-ia32.cc
@@ -77,7 +77,7 @@
void LCodeGen::FinishCode(Handle<Code> code) {
ASSERT(is_done());
- code->set_stack_slots(StackSlotCount());
+ code->set_stack_slots(GetStackSlotCount());
code->set_safepoint_table_offset(safepoints_.GetCodeOffset());
PopulateDeoptimizationData(code);
Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(code);
@@ -132,7 +132,7 @@
__ push(edi); // Callee's JS function.
// Reserve space for the stack slots needed by the code.
- int slots = StackSlotCount();
+ int slots = GetStackSlotCount();
if (slots > 0) {
if (FLAG_debug_code) {
__ mov(Operand(eax), Immediate(slots));
@@ -254,7 +254,7 @@
bool LCodeGen::GenerateSafepointTable() {
ASSERT(is_done());
- safepoints_.Emit(masm(), StackSlotCount());
+ safepoints_.Emit(masm(), GetStackSlotCount());
return !is_aborted();
}
@@ -386,7 +386,7 @@
translation->StoreDoubleStackSlot(op->index());
} else if (op->IsArgument()) {
ASSERT(is_tagged);
- int src_index = StackSlotCount() + op->index();
+ int src_index = GetStackSlotCount() + op->index();
translation->StoreStackSlot(src_index);
} else if (op->IsRegister()) {
Register reg = ToRegister(op);
@@ -408,20 +408,21 @@
}
-void LCodeGen::CallCode(Handle<Code> code,
- RelocInfo::Mode mode,
- LInstruction* instr,
- bool adjusted) {
+void LCodeGen::CallCodeGeneric(Handle<Code> code,
+ RelocInfo::Mode mode,
+ LInstruction* instr,
+ ContextMode context_mode,
+ SafepointMode safepoint_mode) {
ASSERT(instr != NULL);
LPointerMap* pointers = instr->pointer_map();
RecordPosition(pointers->position());
- if (!adjusted) {
+ if (context_mode == RESTORE_CONTEXT) {
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
}
__ call(code, mode);
- RegisterLazyDeoptimization(instr);
+ RegisterLazyDeoptimization(instr, safepoint_mode);
// Signal that we don't inline smi code before these stubs in the
// optimizing code generator.
@@ -432,25 +433,44 @@
}
+void LCodeGen::CallCode(Handle<Code> code,
+ RelocInfo::Mode mode,
+ LInstruction* instr,
+ ContextMode context_mode) {
+ CallCodeGeneric(code, mode, instr, context_mode, RECORD_SIMPLE_SAFEPOINT);
+}
+
+
void LCodeGen::CallRuntime(const Runtime::Function* fun,
int argc,
LInstruction* instr,
- bool adjusted) {
+ ContextMode context_mode) {
ASSERT(instr != NULL);
ASSERT(instr->HasPointerMap());
LPointerMap* pointers = instr->pointer_map();
RecordPosition(pointers->position());
- if (!adjusted) {
+ if (context_mode == RESTORE_CONTEXT) {
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
}
__ CallRuntime(fun, argc);
- RegisterLazyDeoptimization(instr);
+ RegisterLazyDeoptimization(instr, RECORD_SIMPLE_SAFEPOINT);
}
-void LCodeGen::RegisterLazyDeoptimization(LInstruction* instr) {
+void LCodeGen::CallRuntimeFromDeferred(Runtime::FunctionId id,
+ int argc,
+ LInstruction* instr) {
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ __ CallRuntimeSaveDoubles(id);
+ RecordSafepointWithRegisters(
+ instr->pointer_map(), argc, Safepoint::kNoDeoptimizationIndex);
+}
+
+
+void LCodeGen::RegisterLazyDeoptimization(LInstruction* instr,
+ SafepointMode safepoint_mode) {
// Create the environment to bailout to. If the call has side effects
// execution has to continue after the call otherwise execution can continue
// from a previous bailout point repeating the call.
@@ -462,8 +482,16 @@
}
RegisterEnvironmentForDeoptimization(deoptimization_environment);
- RecordSafepoint(instr->pointer_map(),
- deoptimization_environment->deoptimization_index());
+ if (safepoint_mode == RECORD_SIMPLE_SAFEPOINT) {
+ RecordSafepoint(instr->pointer_map(),
+ deoptimization_environment->deoptimization_index());
+ } else {
+ ASSERT(safepoint_mode == RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS);
+ RecordSafepointWithRegisters(
+ instr->pointer_map(),
+ 0,
+ deoptimization_environment->deoptimization_index());
+ }
}
@@ -612,6 +640,7 @@
Safepoint::Kind kind,
int arguments,
int deoptimization_index) {
+ ASSERT(kind == expected_safepoint_kind_);
const ZoneList<LOperand*>* operands = pointers->operands();
Safepoint safepoint = safepoints_.DefineSafepoint(masm(),
kind, arguments, deoptimization_index);
@@ -697,38 +726,38 @@
switch (instr->hydrogen()->major_key()) {
case CodeStub::RegExpConstructResult: {
RegExpConstructResultStub stub;
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
break;
}
case CodeStub::RegExpExec: {
RegExpExecStub stub;
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
break;
}
case CodeStub::SubString: {
SubStringStub stub;
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
break;
}
case CodeStub::NumberToString: {
NumberToStringStub stub;
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
break;
}
case CodeStub::StringAdd: {
StringAddStub stub(NO_STRING_ADD_FLAGS);
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
break;
}
case CodeStub::StringCompare: {
StringCompareStub stub;
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
break;
}
case CodeStub::TranscendentalCache: {
TranscendentalCacheStub stub(instr->transcendental_type(),
TranscendentalCacheStub::TAGGED);
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
break;
}
default:
@@ -1062,7 +1091,7 @@
uint64_t int_val = BitCast<uint64_t, double>(v);
int32_t lower = static_cast<int32_t>(int_val);
int32_t upper = static_cast<int32_t>(int_val >> (kBitsPerInt));
- if (isolate()->cpu_features()->IsSupported(SSE4_1)) {
+ if (CpuFeatures::IsSupported(SSE4_1)) {
CpuFeatures::Scope scope(SSE4_1);
if (lower != 0) {
__ Set(temp, Immediate(lower));
@@ -1143,7 +1172,7 @@
void LCodeGen::DoThrow(LThrow* instr) {
__ push(ToOperand(instr->InputAt(0)));
- CallRuntime(Runtime::kThrow, 1, instr, false);
+ CallRuntime(Runtime::kThrow, 1, instr, RESTORE_CONTEXT);
if (FLAG_debug_code) {
Comment("Unreachable code.");
@@ -1218,7 +1247,7 @@
ASSERT(ToRegister(instr->result()).is(eax));
TypeRecordingBinaryOpStub stub(instr->op(), NO_OVERWRITE);
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, false);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, RESTORE_CONTEXT);
}
@@ -1330,12 +1359,8 @@
void LCodeGen::DoDeferredStackCheck(LGoto* instr) {
- __ pushad();
- __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
- __ CallRuntimeSaveDoubles(Runtime::kStackGuard);
- RecordSafepointWithRegisters(
- instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
- __ popad();
+ PushSafepointRegistersScope scope(this);
+ CallRuntimeFromDeferred(Runtime::kStackGuard, 0, instr);
}
void LCodeGen::DoGoto(LGoto* instr) {
@@ -1837,7 +1862,7 @@
// Object and function are in fixed registers defined by the stub.
ASSERT(ToRegister(instr->context()).is(esi));
InstanceofStub stub(InstanceofStub::kArgsInRegisters);
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
NearLabel true_value, done;
__ test(eax, Operand(eax));
@@ -1856,7 +1881,7 @@
int false_block = chunk_->LookupDestination(instr->false_block_id());
InstanceofStub stub(InstanceofStub::kArgsInRegisters);
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
__ test(eax, Operand(eax));
EmitBranch(true_block, false_block, zero);
}
@@ -1928,7 +1953,7 @@
void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
Label* map_check) {
- __ PushSafepointRegisters();
+ PushSafepointRegistersScope scope(this);
InstanceofStub::Flags flags = InstanceofStub::kNoFlags;
flags = static_cast<InstanceofStub::Flags>(
@@ -1939,20 +1964,24 @@
flags | InstanceofStub::kReturnTrueFalseObject);
InstanceofStub stub(flags);
- // Get the temp register reserved by the instruction. This needs to be edi as
- // its slot of the pushing of safepoint registers is used to communicate the
- // offset to the location of the map check.
+ // Get the temp register reserved by the instruction. This needs to be a
+ // register which is pushed last by PushSafepointRegisters as top of the
+ // stack is used to pass the offset to the location of the map check to
+ // the stub.
Register temp = ToRegister(instr->TempAt(0));
- ASSERT(temp.is(edi));
+ ASSERT(MacroAssembler::SafepointRegisterStackIndex(temp) == 0);
__ mov(InstanceofStub::right(), Immediate(instr->function()));
static const int kAdditionalDelta = 16;
int delta = masm_->SizeOfCodeGeneratedSince(map_check) + kAdditionalDelta;
__ mov(temp, Immediate(delta));
__ StoreToSafepointRegisterSlot(temp, temp);
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, false);
+ CallCodeGeneric(stub.GetCode(),
+ RelocInfo::CODE_TARGET,
+ instr,
+ RESTORE_CONTEXT,
+ RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS);
// Put the result value into the eax slot and restore all registers.
__ StoreToSafepointRegisterSlot(eax, eax);
- __ PopSafepointRegisters();
}
@@ -1980,7 +2009,7 @@
Token::Value op = instr->op();
Handle<Code> ic = CompareIC::GetUninitialized(op);
- CallCode(ic, RelocInfo::CODE_TARGET, instr, false);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr, RESTORE_CONTEXT);
Condition condition = ComputeCompareCondition(op);
if (op == Token::GT || op == Token::LTE) {
@@ -2003,7 +2032,7 @@
int false_block = chunk_->LookupDestination(instr->false_block_id());
Handle<Code> ic = CompareIC::GetUninitialized(op);
- CallCode(ic, RelocInfo::CODE_TARGET, instr, false);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr, RESTORE_CONTEXT);
// The compare stub expects compare condition and the input operands
// reversed for GT and LTE.
@@ -2028,11 +2057,11 @@
}
__ mov(esp, ebp);
__ pop(ebp);
- __ Ret((ParameterCount() + 1) * kPointerSize, ecx);
+ __ Ret((GetParameterCount() + 1) * kPointerSize, ecx);
}
-void LCodeGen::DoLoadGlobal(LLoadGlobal* instr) {
+void LCodeGen::DoLoadGlobalCell(LLoadGlobalCell* instr) {
Register result = ToRegister(instr->result());
__ mov(result, Operand::Cell(instr->hydrogen()->cell()));
if (instr->hydrogen()->check_hole_value()) {
@@ -2042,7 +2071,20 @@
}
-void LCodeGen::DoStoreGlobal(LStoreGlobal* instr) {
+void LCodeGen::DoLoadGlobalGeneric(LLoadGlobalGeneric* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ ASSERT(ToRegister(instr->global_object()).is(eax));
+ ASSERT(ToRegister(instr->result()).is(eax));
+
+ __ mov(ecx, instr->name());
+ RelocInfo::Mode mode = instr->for_typeof() ? RelocInfo::CODE_TARGET :
+ RelocInfo::CODE_TARGET_CONTEXT;
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ CallCode(ic, mode, instr, CONTEXT_ADJUSTED);
+}
+
+
+void LCodeGen::DoStoreGlobalCell(LStoreGlobalCell* instr) {
Register value = ToRegister(instr->InputAt(0));
Operand cell_operand = Operand::Cell(instr->hydrogen()->cell());
@@ -2060,6 +2102,19 @@
}
+void LCodeGen::DoStoreGlobalGeneric(LStoreGlobalGeneric* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ ASSERT(ToRegister(instr->global_object()).is(edx));
+ ASSERT(ToRegister(instr->value()).is(eax));
+
+ __ mov(ecx, instr->name());
+ Handle<Code> ic = instr->strict_mode()
+ ? isolate()->builtins()->StoreIC_Initialize_Strict()
+ : isolate()->builtins()->StoreIC_Initialize();
+ CallCode(ic, RelocInfo::CODE_TARGET_CONTEXT, instr, CONTEXT_ADJUSTED);
+}
+
+
void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
Register context = ToRegister(instr->context());
Register result = ToRegister(instr->result());
@@ -2122,7 +2177,7 @@
ASSERT(instr->hydrogen()->need_generic());
__ mov(ecx, name);
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
- CallCode(ic, RelocInfo::CODE_TARGET, instr, false);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr, RESTORE_CONTEXT);
} else {
NearLabel done;
for (int i = 0; i < map_count - 1; ++i) {
@@ -2144,7 +2199,7 @@
__ bind(&generic);
__ mov(ecx, name);
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
- CallCode(ic, RelocInfo::CODE_TARGET, instr, false);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr, RESTORE_CONTEXT);
} else {
DeoptimizeIf(not_equal, instr->environment());
EmitLoadField(result, object, map, name);
@@ -2161,7 +2216,7 @@
__ mov(ecx, instr->name());
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
- CallCode(ic, RelocInfo::CODE_TARGET, instr);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
}
@@ -2304,11 +2359,11 @@
break;
case kExternalUnsignedIntArray:
__ mov(result, Operand(external_pointer, key, times_4, 0));
- __ test(Operand(result), Immediate(0x80000000));
+ __ test(result, Operand(result));
// TODO(danno): we could be more clever here, perhaps having a special
// version of the stub that detects if the overflow case actually
// happens, and generate code that returns a double rather than int.
- DeoptimizeIf(not_zero, instr->environment());
+ DeoptimizeIf(negative, instr->environment());
break;
case kExternalFloatArray:
UNREACHABLE();
@@ -2324,7 +2379,7 @@
ASSERT(ToRegister(instr->key()).is(eax));
Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
- CallCode(ic, RelocInfo::CODE_TARGET, instr);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
}
@@ -2438,7 +2493,7 @@
SafepointGenerator safepoint_generator(this,
pointers,
env->deoptimization_index());
- v8::internal::ParameterCount actual(eax);
+ ParameterCount actual(eax);
__ InvokeFunction(function, actual, CALL_FUNCTION, &safepoint_generator);
}
@@ -2512,7 +2567,7 @@
}
// Setup deoptimization.
- RegisterLazyDeoptimization(instr);
+ RegisterLazyDeoptimization(instr, RECORD_SIMPLE_SAFEPOINT);
}
@@ -2534,7 +2589,7 @@
Register tmp2 = tmp.is(ecx) ? edx : input_reg.is(ecx) ? edx : ecx;
// Preserve the value of all registers.
- __ PushSafepointRegisters();
+ PushSafepointRegistersScope scope(this);
Label negative;
__ mov(tmp, FieldOperand(input_reg, HeapNumber::kExponentOffset));
@@ -2555,10 +2610,8 @@
// Slow case: Call the runtime system to do the number allocation.
__ bind(&slow);
- __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
- __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber);
- RecordSafepointWithRegisters(
- instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
+ CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr);
+
// Set the pointer to the new heap number in tmp.
if (!tmp.is(eax)) __ mov(tmp, eax);
@@ -2574,7 +2627,6 @@
__ StoreToSafepointRegisterSlot(input_reg, tmp);
__ bind(&done);
- __ PopSafepointRegisters();
}
@@ -2655,25 +2707,16 @@
Register output_reg = ToRegister(instr->result());
XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0));
+ Label below_half, done;
// xmm_scratch = 0.5
ExternalReference one_half = ExternalReference::address_of_one_half();
__ movdbl(xmm_scratch, Operand::StaticVariable(one_half));
+ __ ucomisd(xmm_scratch, input_reg);
+ __ j(above, &below_half);
// input = input + 0.5
__ addsd(input_reg, xmm_scratch);
- // We need to return -0 for the input range [-0.5, 0[, otherwise
- // compute Math.floor(value + 0.5).
- if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
- __ ucomisd(input_reg, xmm_scratch);
- DeoptimizeIf(below_equal, instr->environment());
- } else {
- // If we don't need to bailout on -0, we check only bailout
- // on negative inputs.
- __ xorpd(xmm_scratch, xmm_scratch); // Zero the register.
- __ ucomisd(input_reg, xmm_scratch);
- DeoptimizeIf(below, instr->environment());
- }
// Compute Math.floor(value + 0.5).
// Use truncating instruction (OK because input is positive).
@@ -2682,6 +2725,27 @@
// Overflow is signalled with minint.
__ cmp(output_reg, 0x80000000u);
DeoptimizeIf(equal, instr->environment());
+ __ jmp(&done);
+
+ __ bind(&below_half);
+
+ // We return 0 for the input range [+0, 0.5[, or [-0.5, 0.5[ if
+ // we can ignore the difference between a result of -0 and +0.
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ // If the sign is positive, we return +0.
+ __ movmskpd(output_reg, input_reg);
+ __ test(output_reg, Immediate(1));
+ DeoptimizeIf(not_zero, instr->environment());
+ } else {
+ // If the input is >= -0.5, we return +0.
+ __ mov(output_reg, Immediate(0xBF000000));
+ __ movd(xmm_scratch, Operand(output_reg));
+ __ cvtss2sd(xmm_scratch, xmm_scratch);
+ __ ucomisd(input_reg, xmm_scratch);
+ DeoptimizeIf(below, instr->environment());
+ }
+ __ Set(output_reg, Immediate(0));
+ __ bind(&done);
}
@@ -2763,10 +2827,32 @@
void LCodeGen::DoMathLog(LUnaryMathOperation* instr) {
- ASSERT(ToDoubleRegister(instr->result()).is(xmm1));
- TranscendentalCacheStub stub(TranscendentalCache::LOG,
- TranscendentalCacheStub::UNTAGGED);
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, false);
+ ASSERT(instr->InputAt(0)->Equals(instr->result()));
+ XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0));
+ NearLabel positive, done, zero, negative;
+ __ xorpd(xmm0, xmm0);
+ __ ucomisd(input_reg, xmm0);
+ __ j(above, &positive);
+ __ j(equal, &zero);
+ ExternalReference nan = ExternalReference::address_of_nan();
+ __ movdbl(input_reg, Operand::StaticVariable(nan));
+ __ jmp(&done);
+ __ bind(&zero);
+ __ push(Immediate(0xFFF00000));
+ __ push(Immediate(0));
+ __ movdbl(input_reg, Operand(esp, 0));
+ __ add(Operand(esp), Immediate(kDoubleSize));
+ __ jmp(&done);
+ __ bind(&positive);
+ __ fldln2();
+ __ sub(Operand(esp), Immediate(kDoubleSize));
+ __ movdbl(Operand(esp, 0), input_reg);
+ __ fld_d(Operand(esp, 0));
+ __ fyl2x();
+ __ fstp_d(Operand(esp, 0));
+ __ movdbl(input_reg, Operand(esp, 0));
+ __ add(Operand(esp), Immediate(kDoubleSize));
+ __ bind(&done);
}
@@ -2774,7 +2860,7 @@
ASSERT(ToDoubleRegister(instr->result()).is(xmm1));
TranscendentalCacheStub stub(TranscendentalCache::COS,
TranscendentalCacheStub::UNTAGGED);
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, false);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, RESTORE_CONTEXT);
}
@@ -2782,7 +2868,7 @@
ASSERT(ToDoubleRegister(instr->result()).is(xmm1));
TranscendentalCacheStub stub(TranscendentalCache::SIN,
TranscendentalCacheStub::UNTAGGED);
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, false);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, RESTORE_CONTEXT);
}
@@ -2819,6 +2905,21 @@
}
+void LCodeGen::DoInvokeFunction(LInvokeFunction* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ ASSERT(ToRegister(instr->function()).is(edi));
+ ASSERT(instr->HasPointerMap());
+ ASSERT(instr->HasDeoptimizationEnvironment());
+ LPointerMap* pointers = instr->pointer_map();
+ LEnvironment* env = instr->deoptimization_environment();
+ RecordPosition(pointers->position());
+ RegisterEnvironmentForDeoptimization(env);
+ SafepointGenerator generator(this, pointers, env->deoptimization_index());
+ ParameterCount count(instr->arity());
+ __ InvokeFunction(edi, count, CALL_FUNCTION, &generator);
+}
+
+
void LCodeGen::DoCallKeyed(LCallKeyed* instr) {
ASSERT(ToRegister(instr->context()).is(esi));
ASSERT(ToRegister(instr->key()).is(ecx));
@@ -2827,7 +2928,7 @@
int arity = instr->arity();
Handle<Code> ic = isolate()->stub_cache()->
ComputeKeyedCallInitialize(arity, NOT_IN_LOOP);
- CallCode(ic, RelocInfo::CODE_TARGET, instr);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
}
@@ -2839,7 +2940,7 @@
Handle<Code> ic = isolate()->stub_cache()->
ComputeCallInitialize(arity, NOT_IN_LOOP);
__ mov(ecx, instr->name());
- CallCode(ic, RelocInfo::CODE_TARGET, instr);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
}
@@ -2849,7 +2950,7 @@
int arity = instr->arity();
CallFunctionStub stub(arity, NOT_IN_LOOP, RECEIVER_MIGHT_BE_VALUE);
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
__ Drop(1);
}
@@ -2862,7 +2963,7 @@
Handle<Code> ic = isolate()->stub_cache()->
ComputeCallInitialize(arity, NOT_IN_LOOP);
__ mov(ecx, instr->name());
- CallCode(ic, RelocInfo::CODE_TARGET_CONTEXT, instr);
+ CallCode(ic, RelocInfo::CODE_TARGET_CONTEXT, instr, CONTEXT_ADJUSTED);
}
@@ -2880,12 +2981,12 @@
Handle<Code> builtin = isolate()->builtins()->JSConstructCall();
__ Set(eax, Immediate(instr->arity()));
- CallCode(builtin, RelocInfo::CONSTRUCT_CALL, instr);
+ CallCode(builtin, RelocInfo::CONSTRUCT_CALL, instr, CONTEXT_ADJUSTED);
}
void LCodeGen::DoCallRuntime(LCallRuntime* instr) {
- CallRuntime(instr->function(), instr->arity(), instr, false);
+ CallRuntime(instr->function(), instr->arity(), instr, RESTORE_CONTEXT);
}
@@ -2925,10 +3026,10 @@
ASSERT(ToRegister(instr->value()).is(eax));
__ mov(ecx, instr->name());
- Handle<Code> ic = info_->is_strict()
+ Handle<Code> ic = instr->strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
- CallCode(ic, RelocInfo::CODE_TARGET, instr);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
}
@@ -3025,10 +3126,10 @@
ASSERT(ToRegister(instr->key()).is(ecx));
ASSERT(ToRegister(instr->value()).is(eax));
- Handle<Code> ic = info_->is_strict()
+ Handle<Code> ic = instr->strict_mode()
? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
: isolate()->builtins()->KeyedStoreIC_Initialize();
- CallCode(ic, RelocInfo::CODE_TARGET, instr);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
}
@@ -3146,7 +3247,7 @@
// contained in the register pointer map.
__ Set(result, Immediate(0));
- __ PushSafepointRegisters();
+ PushSafepointRegistersScope scope(this);
__ push(string);
// Push the index as a smi. This is safe because of the checks in
// DoStringCharCodeAt above.
@@ -3159,16 +3260,12 @@
__ SmiTag(index);
__ push(index);
}
- __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
- __ CallRuntimeSaveDoubles(Runtime::kStringCharCodeAt);
- RecordSafepointWithRegisters(
- instr->pointer_map(), 2, Safepoint::kNoDeoptimizationIndex);
+ CallRuntimeFromDeferred(Runtime::kStringCharCodeAt, 2, instr);
if (FLAG_debug_code) {
__ AbortIfNotSmi(eax);
}
__ SmiUntag(eax);
__ StoreToSafepointRegisterSlot(result, eax);
- __ PopSafepointRegisters();
}
@@ -3211,14 +3308,11 @@
// contained in the register pointer map.
__ Set(result, Immediate(0));
- __ PushSafepointRegisters();
+ PushSafepointRegistersScope scope(this);
__ SmiTag(char_code);
__ push(char_code);
- __ CallRuntimeSaveDoubles(Runtime::kCharFromCode);
- RecordSafepointWithRegisters(
- instr->pointer_map(), 1, Safepoint::kNoDeoptimizationIndex);
+ CallRuntimeFromDeferred(Runtime::kCharFromCode, 1, instr);
__ StoreToSafepointRegisterSlot(result, eax);
- __ PopSafepointRegisters();
}
@@ -3229,6 +3323,22 @@
}
+void LCodeGen::DoStringAdd(LStringAdd* instr) {
+ if (instr->left()->IsConstantOperand()) {
+ __ push(ToImmediate(instr->left()));
+ } else {
+ __ push(ToOperand(instr->left()));
+ }
+ if (instr->right()->IsConstantOperand()) {
+ __ push(ToImmediate(instr->right()));
+ } else {
+ __ push(ToOperand(instr->right()));
+ }
+ StringAddStub stub(NO_STRING_CHECK_IN_STUB);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, RESTORE_CONTEXT);
+}
+
+
void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) {
LOperand* input = instr->InputAt(0);
ASSERT(input->IsRegister() || input->IsStackSlot());
@@ -3265,7 +3375,7 @@
Register tmp = reg.is(eax) ? ecx : eax;
// Preserve the value of all registers.
- __ PushSafepointRegisters();
+ PushSafepointRegistersScope scope(this);
// There was overflow, so bits 30 and 31 of the original integer
// disagree. Try to allocate a heap number in new space and store
@@ -3287,10 +3397,7 @@
// integer value.
__ StoreToSafepointRegisterSlot(reg, Immediate(0));
- __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
- __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber);
- RecordSafepointWithRegisters(
- instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
+ CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr);
if (!reg.is(eax)) __ mov(reg, eax);
// Done. Put the value in xmm0 into the value of the allocated heap
@@ -3298,7 +3405,6 @@
__ bind(&done);
__ movdbl(FieldOperand(reg, HeapNumber::kValueOffset), xmm0);
__ StoreToSafepointRegisterSlot(reg, reg);
- __ PopSafepointRegisters();
}
@@ -3334,13 +3440,9 @@
Register reg = ToRegister(instr->result());
__ Set(reg, Immediate(0));
- __ PushSafepointRegisters();
- __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
- __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber);
- RecordSafepointWithRegisters(
- instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
+ PushSafepointRegistersScope scope(this);
+ CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr);
__ StoreToSafepointRegisterSlot(reg, eax);
- __ PopSafepointRegisters();
}
@@ -3427,7 +3529,7 @@
__ jmp(&done);
__ bind(&heap_number);
- if (isolate()->cpu_features()->IsSupported(SSE3)) {
+ if (CpuFeatures::IsSupported(SSE3)) {
CpuFeatures::Scope scope(SSE3);
NearLabel convert;
// Use more powerful conversion when sse3 is available.
@@ -3537,7 +3639,7 @@
// the JS bitwise operations.
__ cvttsd2si(result_reg, Operand(input_reg));
__ cmp(result_reg, 0x80000000u);
- if (isolate()->cpu_features()->IsSupported(SSE3)) {
+ if (CpuFeatures::IsSupported(SSE3)) {
// This will deoptimize if the exponent of the input in out of range.
CpuFeatures::Scope scope(SSE3);
NearLabel convert, done;
@@ -3755,16 +3857,16 @@
FastCloneShallowArrayStub::Mode mode =
FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS;
FastCloneShallowArrayStub stub(mode, length);
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, false);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, RESTORE_CONTEXT);
} else if (instr->hydrogen()->depth() > 1) {
- CallRuntime(Runtime::kCreateArrayLiteral, 3, instr, false);
+ CallRuntime(Runtime::kCreateArrayLiteral, 3, instr, RESTORE_CONTEXT);
} else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) {
- CallRuntime(Runtime::kCreateArrayLiteralShallow, 3, instr, false);
+ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3, instr, RESTORE_CONTEXT);
} else {
FastCloneShallowArrayStub::Mode mode =
FastCloneShallowArrayStub::CLONE_ELEMENTS;
FastCloneShallowArrayStub stub(mode, length);
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, false);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, RESTORE_CONTEXT);
}
}
@@ -3786,9 +3888,12 @@
// Pick the right runtime function to call.
if (instr->hydrogen()->depth() > 1) {
- CallRuntime(Runtime::kCreateObjectLiteral, 4, instr);
+ CallRuntime(Runtime::kCreateObjectLiteral, 4, instr, CONTEXT_ADJUSTED);
} else {
- CallRuntime(Runtime::kCreateObjectLiteralShallow, 4, instr);
+ CallRuntime(Runtime::kCreateObjectLiteralShallow,
+ 4,
+ instr,
+ CONTEXT_ADJUSTED);
}
}
@@ -3796,7 +3901,7 @@
void LCodeGen::DoToFastProperties(LToFastProperties* instr) {
ASSERT(ToRegister(instr->InputAt(0)).is(eax));
__ push(eax);
- CallRuntime(Runtime::kToFastProperties, 1, instr);
+ CallRuntime(Runtime::kToFastProperties, 1, instr, CONTEXT_ADJUSTED);
}
@@ -3821,7 +3926,7 @@
__ push(Immediate(Smi::FromInt(instr->hydrogen()->literal_index())));
__ push(Immediate(instr->hydrogen()->pattern()));
__ push(Immediate(instr->hydrogen()->flags()));
- CallRuntime(Runtime::kMaterializeRegExpLiteral, 4, instr, false);
+ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4, instr, RESTORE_CONTEXT);
__ mov(ebx, eax);
__ bind(&materialized);
@@ -3833,7 +3938,7 @@
__ bind(&runtime_allocate);
__ push(ebx);
__ push(Immediate(Smi::FromInt(size)));
- CallRuntime(Runtime::kAllocateInNewSpace, 1, instr, false);
+ CallRuntime(Runtime::kAllocateInNewSpace, 1, instr, RESTORE_CONTEXT);
__ pop(ebx);
__ bind(&allocated);
@@ -3861,14 +3966,14 @@
FastNewClosureStub stub(
shared_info->strict_mode() ? kStrictMode : kNonStrictMode);
__ push(Immediate(shared_info));
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, false);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, RESTORE_CONTEXT);
} else {
__ push(Operand(ebp, StandardFrameConstants::kContextOffset));
__ push(Immediate(shared_info));
__ push(Immediate(pretenure
? factory()->true_value()
: factory()->false_value()));
- CallRuntime(Runtime::kNewClosure, 3, instr, false);
+ CallRuntime(Runtime::kNewClosure, 3, instr, RESTORE_CONTEXT);
}
}
@@ -3880,7 +3985,7 @@
} else {
__ push(ToOperand(input));
}
- CallRuntime(Runtime::kTypeof, 1, instr, false);
+ CallRuntime(Runtime::kTypeof, 1, instr, RESTORE_CONTEXT);
}
@@ -4083,7 +4188,7 @@
__ j(above_equal, &done);
StackCheckStub stub;
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, false);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, RESTORE_CONTEXT);
__ bind(&done);
}
diff --git a/src/ia32/lithium-codegen-ia32.h b/src/ia32/lithium-codegen-ia32.h
index 4414e6a..f8bbea3 100644
--- a/src/ia32/lithium-codegen-ia32.h
+++ b/src/ia32/lithium-codegen-ia32.h
@@ -61,7 +61,8 @@
deferred_(8),
osr_pc_offset_(-1),
deoptimization_reloc_size(),
- resolver_(this) {
+ resolver_(this),
+ expected_safepoint_kind_(Safepoint::kSimple) {
PopulateDeoptimizationLiteralsWithInlinedFunctions();
}
@@ -129,7 +130,7 @@
bool is_aborted() const { return status_ == ABORTED; }
int strict_mode_flag() const {
- return info()->is_strict() ? kStrictMode : kNonStrictMode;
+ return info()->is_strict_mode() ? kStrictMode : kNonStrictMode;
}
LChunk* chunk() const { return chunk_; }
@@ -146,8 +147,8 @@
Register temporary,
Register temporary2);
- int StackSlotCount() const { return chunk()->spill_slot_count(); }
- int ParameterCount() const { return scope()->num_parameters(); }
+ int GetStackSlotCount() const { return chunk()->spill_slot_count(); }
+ int GetParameterCount() const { return scope()->num_parameters(); }
void Abort(const char* format, ...);
void Comment(const char* format, ...);
@@ -164,16 +165,44 @@
bool GenerateRelocPadding();
bool GenerateSafepointTable();
- void CallCode(Handle<Code> code, RelocInfo::Mode mode, LInstruction* instr,
- bool adjusted = true);
- void CallRuntime(const Runtime::Function* fun, int argc, LInstruction* instr,
- bool adjusted = true);
- void CallRuntime(Runtime::FunctionId id, int argc, LInstruction* instr,
- bool adjusted = true) {
+ enum ContextMode {
+ RESTORE_CONTEXT,
+ CONTEXT_ADJUSTED
+ };
+
+ enum SafepointMode {
+ RECORD_SIMPLE_SAFEPOINT,
+ RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS
+ };
+
+ void CallCode(Handle<Code> code,
+ RelocInfo::Mode mode,
+ LInstruction* instr,
+ ContextMode context_mode);
+
+ void CallCodeGeneric(Handle<Code> code,
+ RelocInfo::Mode mode,
+ LInstruction* instr,
+ ContextMode context_mode,
+ SafepointMode safepoint_mode);
+
+ void CallRuntime(const Runtime::Function* fun,
+ int argc,
+ LInstruction* instr,
+ ContextMode context_mode);
+
+ void CallRuntime(Runtime::FunctionId id,
+ int argc,
+ LInstruction* instr,
+ ContextMode context_mode) {
const Runtime::Function* function = Runtime::FunctionForId(id);
- CallRuntime(function, argc, instr, adjusted);
+ CallRuntime(function, argc, instr, context_mode);
}
+ void CallRuntimeFromDeferred(Runtime::FunctionId id,
+ int argc,
+ LInstruction* instr);
+
// Generate a direct call to a known function. Expects the function
// to be in edi.
void CallKnownFunction(Handle<JSFunction> function,
@@ -182,7 +211,9 @@
void LoadHeapObject(Register result, Handle<HeapObject> object);
- void RegisterLazyDeoptimization(LInstruction* instr);
+ void RegisterLazyDeoptimization(LInstruction* instr,
+ SafepointMode safepoint_mode);
+
void RegisterEnvironmentForDeoptimization(LEnvironment* environment);
void DeoptimizeIf(Condition cc, LEnvironment* environment);
@@ -281,6 +312,27 @@
// Compiler from a set of parallel moves to a sequential list of moves.
LGapResolver resolver_;
+ Safepoint::Kind expected_safepoint_kind_;
+
+ class PushSafepointRegistersScope BASE_EMBEDDED {
+ public:
+ explicit PushSafepointRegistersScope(LCodeGen* codegen)
+ : codegen_(codegen) {
+ ASSERT(codegen_->expected_safepoint_kind_ == Safepoint::kSimple);
+ codegen_->masm_->PushSafepointRegisters();
+ codegen_->expected_safepoint_kind_ = Safepoint::kWithRegisters;
+ }
+
+ ~PushSafepointRegistersScope() {
+ ASSERT(codegen_->expected_safepoint_kind_ == Safepoint::kWithRegisters);
+ codegen_->masm_->PopSafepointRegisters();
+ codegen_->expected_safepoint_kind_ = Safepoint::kSimple;
+ }
+
+ private:
+ LCodeGen* codegen_;
+ };
+
friend class LDeferredCode;
friend class LEnvironment;
friend class SafepointGenerator;
diff --git a/src/ia32/lithium-gap-resolver-ia32.cc b/src/ia32/lithium-gap-resolver-ia32.cc
index eabfecc..3d1da40 100644
--- a/src/ia32/lithium-gap-resolver-ia32.cc
+++ b/src/ia32/lithium-gap-resolver-ia32.cc
@@ -27,6 +27,8 @@
#include "v8.h"
+#if defined(V8_TARGET_ARCH_IA32)
+
#include "ia32/lithium-gap-resolver-ia32.h"
#include "ia32/lithium-codegen-ia32.h"
@@ -460,3 +462,5 @@
#undef __
} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc
index 199a80a..aa91a83 100644
--- a/src/ia32/lithium-ia32.cc
+++ b/src/ia32/lithium-ia32.cc
@@ -71,22 +71,21 @@
#ifdef DEBUG
void LInstruction::VerifyCall() {
- // Call instructions can use only fixed registers as
- // temporaries and outputs because all registers
- // are blocked by the calling convention.
- // Inputs must use a fixed register.
+ // Call instructions can use only fixed registers as temporaries and
+ // outputs because all registers are blocked by the calling convention.
+ // Inputs operands must use a fixed register or use-at-start policy or
+ // a non-register policy.
ASSERT(Output() == NULL ||
LUnallocated::cast(Output())->HasFixedPolicy() ||
!LUnallocated::cast(Output())->HasRegisterPolicy());
for (UseIterator it(this); it.HasNext(); it.Advance()) {
- LOperand* operand = it.Next();
- ASSERT(LUnallocated::cast(operand)->HasFixedPolicy() ||
- !LUnallocated::cast(operand)->HasRegisterPolicy());
+ LUnallocated* operand = LUnallocated::cast(it.Next());
+ ASSERT(operand->HasFixedPolicy() ||
+ operand->IsUsedAtStart());
}
for (TempIterator it(this); it.HasNext(); it.Advance()) {
- LOperand* operand = it.Next();
- ASSERT(LUnallocated::cast(operand)->HasFixedPolicy() ||
- !LUnallocated::cast(operand)->HasRegisterPolicy());
+ LUnallocated* operand = LUnallocated::cast(it.Next());
+ ASSERT(operand->HasFixedPolicy() ||!operand->HasRegisterPolicy());
}
}
#endif
@@ -303,6 +302,15 @@
}
+void LInvokeFunction::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ InputAt(0)->PrintTo(stream);
+ stream->Add(" ");
+ InputAt(1)->PrintTo(stream);
+ stream->Add(" #%d / ", arity());
+}
+
+
void LCallKeyed::PrintDataTo(StringStream* stream) {
stream->Add("[ecx] #%d / ", arity());
}
@@ -1120,9 +1128,9 @@
return new LIsConstructCallAndBranch(TempRegister());
} else {
if (v->IsConstant()) {
- if (HConstant::cast(v)->handle()->IsTrue()) {
+ if (HConstant::cast(v)->ToBoolean()) {
return new LGoto(instr->FirstSuccessor()->block_id());
- } else if (HConstant::cast(v)->handle()->IsFalse()) {
+ } else {
return new LGoto(instr->SecondSuccessor()->block_id());
}
}
@@ -1187,7 +1195,7 @@
LInstruction* LChunkBuilder::DoPushArgument(HPushArgument* instr) {
++argument_count_;
- LOperand* argument = UseOrConstant(instr->argument());
+ LOperand* argument = UseAny(instr->argument());
return new LPushArgument(argument);
}
@@ -1222,9 +1230,24 @@
}
+LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
+ LOperand* context = UseFixed(instr->context(), esi);
+ LOperand* function = UseFixed(instr->function(), edi);
+ argument_count_ -= instr->argument_count();
+ LInvokeFunction* result = new LInvokeFunction(context, function);
+ return MarkAsCall(DefineFixed(result, eax), instr, CANNOT_DEOPTIMIZE_EAGERLY);
+}
+
+
LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
BuiltinFunctionId op = instr->op();
- if (op == kMathLog || op == kMathSin || op == kMathCos) {
+ if (op == kMathLog) {
+ ASSERT(instr->representation().IsDouble());
+ ASSERT(instr->value()->representation().IsDouble());
+ LOperand* input = UseRegisterAtStart(instr->value());
+ LUnaryMathOperation* result = new LUnaryMathOperation(input);
+ return DefineSameAsFirst(result);
+ } else if (op == kMathSin || op == kMathCos) {
LOperand* input = UseFixedDouble(instr->value(), xmm1);
LUnaryMathOperation* result = new LUnaryMathOperation(input);
return MarkAsCall(DefineFixedDouble(result, xmm1), instr);
@@ -1633,9 +1656,8 @@
LOperand* value = UseRegister(instr->value());
bool needs_check = !instr->value()->type().IsSmi();
if (needs_check) {
- CpuFeatures* cpu_features = Isolate::Current()->cpu_features();
LOperand* xmm_temp =
- (instr->CanTruncateToInt32() && cpu_features->IsSupported(SSE3))
+ (instr->CanTruncateToInt32() && CpuFeatures::IsSupported(SSE3))
? NULL
: FixedTemp(xmm1);
LTaggedToI* res = new LTaggedToI(value, xmm_temp);
@@ -1656,7 +1678,7 @@
} else {
ASSERT(to.IsInteger32());
bool needs_temp = instr->CanTruncateToInt32() &&
- !Isolate::Current()->cpu_features()->IsSupported(SSE3);
+ !CpuFeatures::IsSupported(SSE3);
LOperand* value = needs_temp ?
UseTempRegister(instr->value()) : UseRegister(instr->value());
LOperand* temp = needs_temp ? TempRegister() : NULL;
@@ -1746,20 +1768,39 @@
}
-LInstruction* LChunkBuilder::DoLoadGlobal(HLoadGlobal* instr) {
- LLoadGlobal* result = new LLoadGlobal;
+LInstruction* LChunkBuilder::DoLoadGlobalCell(HLoadGlobalCell* instr) {
+ LLoadGlobalCell* result = new LLoadGlobalCell;
return instr->check_hole_value()
? AssignEnvironment(DefineAsRegister(result))
: DefineAsRegister(result);
}
-LInstruction* LChunkBuilder::DoStoreGlobal(HStoreGlobal* instr) {
- LStoreGlobal* result = new LStoreGlobal(UseRegisterAtStart(instr->value()));
+LInstruction* LChunkBuilder::DoLoadGlobalGeneric(HLoadGlobalGeneric* instr) {
+ LOperand* context = UseFixed(instr->context(), esi);
+ LOperand* global_object = UseFixed(instr->global_object(), eax);
+ LLoadGlobalGeneric* result = new LLoadGlobalGeneric(context, global_object);
+ return MarkAsCall(DefineFixed(result, eax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoStoreGlobalCell(HStoreGlobalCell* instr) {
+ LStoreGlobalCell* result =
+ new LStoreGlobalCell(UseRegisterAtStart(instr->value()));
return instr->check_hole_value() ? AssignEnvironment(result) : result;
}
+LInstruction* LChunkBuilder::DoStoreGlobalGeneric(HStoreGlobalGeneric* instr) {
+ LOperand* context = UseFixed(instr->context(), esi);
+ LOperand* global_object = UseFixed(instr->global_object(), edx);
+ LOperand* value = UseFixed(instr->value(), eax);
+ LStoreGlobalGeneric* result =
+ new LStoreGlobalGeneric(context, global_object, value);
+ return MarkAsCall(result, instr);
+}
+
+
LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) {
LOperand* context = UseRegisterAtStart(instr->value());
return DefineAsRegister(new LLoadContextSlot(context));
@@ -1978,6 +2019,13 @@
}
+LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) {
+ LOperand* left = UseOrConstantAtStart(instr->left());
+ LOperand* right = UseOrConstantAtStart(instr->right());
+ return MarkAsCall(DefineFixed(new LStringAdd(left, right), eax), instr);
+}
+
+
LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
LOperand* string = UseRegister(instr->string());
LOperand* index = UseRegisterOrConstant(instr->index());
@@ -2022,7 +2070,8 @@
LInstruction* LChunkBuilder::DoDeleteProperty(HDeleteProperty* instr) {
LDeleteProperty* result =
- new LDeleteProperty(Use(instr->object()), UseOrConstant(instr->key()));
+ new LDeleteProperty(UseAtStart(instr->object()),
+ UseOrConstantAtStart(instr->key()));
return MarkAsCall(DefineFixed(result, eax), instr);
}
@@ -2110,7 +2159,6 @@
env->Push(value);
}
}
- ASSERT(env->length() == instr->environment_length());
// If there is an instruction pending deoptimization environment create a
// lazy bailout instruction to capture the environment.
diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h
index a9d769b..76c90be 100644
--- a/src/ia32/lithium-ia32.h
+++ b/src/ia32/lithium-ia32.h
@@ -39,6 +39,7 @@
// Forward declarations.
class LCodeGen;
+
#define LITHIUM_ALL_INSTRUCTION_LIST(V) \
V(ControlInstruction) \
V(Call) \
@@ -106,6 +107,7 @@
V(InstanceOfAndBranch) \
V(InstanceOfKnownGlobal) \
V(Integer32ToDouble) \
+ V(InvokeFunction) \
V(IsNull) \
V(IsNullAndBranch) \
V(IsObject) \
@@ -121,7 +123,8 @@
V(LoadElements) \
V(LoadExternalArrayPointer) \
V(LoadFunctionPrototype) \
- V(LoadGlobal) \
+ V(LoadGlobalCell) \
+ V(LoadGlobalGeneric) \
V(LoadKeyedFastElement) \
V(LoadKeyedGeneric) \
V(LoadKeyedSpecializedArrayElement) \
@@ -146,12 +149,14 @@
V(SmiUntag) \
V(StackCheck) \
V(StoreContextSlot) \
- V(StoreGlobal) \
+ V(StoreGlobalCell) \
+ V(StoreGlobalGeneric) \
V(StoreKeyedFastElement) \
V(StoreKeyedGeneric) \
V(StoreKeyedSpecializedArrayElement) \
V(StoreNamedField) \
V(StoreNamedGeneric) \
+ V(StringAdd) \
V(StringCharCodeAt) \
V(StringCharFromCode) \
V(StringLength) \
@@ -1292,21 +1297,59 @@
};
-class LLoadGlobal: public LTemplateInstruction<1, 0, 0> {
+class LLoadGlobalCell: public LTemplateInstruction<1, 0, 0> {
public:
- DECLARE_CONCRETE_INSTRUCTION(LoadGlobal, "load-global")
- DECLARE_HYDROGEN_ACCESSOR(LoadGlobal)
+ DECLARE_CONCRETE_INSTRUCTION(LoadGlobalCell, "load-global-cell")
+ DECLARE_HYDROGEN_ACCESSOR(LoadGlobalCell)
};
-class LStoreGlobal: public LTemplateInstruction<0, 1, 0> {
+class LLoadGlobalGeneric: public LTemplateInstruction<1, 2, 0> {
public:
- explicit LStoreGlobal(LOperand* value) {
+ LLoadGlobalGeneric(LOperand* context, LOperand* global_object) {
+ inputs_[0] = context;
+ inputs_[1] = global_object;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadGlobalGeneric, "load-global-generic")
+ DECLARE_HYDROGEN_ACCESSOR(LoadGlobalGeneric)
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* global_object() { return inputs_[1]; }
+ Handle<Object> name() const { return hydrogen()->name(); }
+ bool for_typeof() const { return hydrogen()->for_typeof(); }
+};
+
+
+class LStoreGlobalCell: public LTemplateInstruction<0, 1, 0> {
+ public:
+ explicit LStoreGlobalCell(LOperand* value) {
inputs_[0] = value;
}
- DECLARE_CONCRETE_INSTRUCTION(StoreGlobal, "store-global")
- DECLARE_HYDROGEN_ACCESSOR(StoreGlobal)
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalCell, "store-global-cell")
+ DECLARE_HYDROGEN_ACCESSOR(StoreGlobalCell)
+};
+
+
+class LStoreGlobalGeneric: public LTemplateInstruction<0, 3, 0> {
+ public:
+ explicit LStoreGlobalGeneric(LOperand* context,
+ LOperand* global_object,
+ LOperand* value) {
+ inputs_[0] = context;
+ inputs_[1] = global_object;
+ inputs_[2] = value;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalGeneric, "store-global-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreGlobalGeneric)
+
+ LOperand* context() { return InputAt(0); }
+ LOperand* global_object() { return InputAt(1); }
+ Handle<Object> name() const { return hydrogen()->name(); }
+ LOperand* value() { return InputAt(2); }
+ bool strict_mode() { return hydrogen()->strict_mode(); }
};
@@ -1410,6 +1453,25 @@
};
+class LInvokeFunction: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LInvokeFunction(LOperand* context, LOperand* function) {
+ inputs_[0] = context;
+ inputs_[1] = function;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(InvokeFunction, "invoke-function")
+ DECLARE_HYDROGEN_ACCESSOR(InvokeFunction)
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* function() { return inputs_[1]; }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
class LCallKeyed: public LTemplateInstruction<1, 2, 0> {
public:
LCallKeyed(LOperand* context, LOperand* key) {
@@ -1655,6 +1717,7 @@
LOperand* object() { return inputs_[1]; }
LOperand* value() { return inputs_[2]; }
Handle<Object> name() const { return hydrogen()->name(); }
+ bool strict_mode() { return hydrogen()->strict_mode(); }
};
@@ -1716,6 +1779,7 @@
}
DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store-keyed-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreKeyedGeneric)
virtual void PrintDataTo(StringStream* stream);
@@ -1723,6 +1787,22 @@
LOperand* object() { return inputs_[1]; }
LOperand* key() { return inputs_[2]; }
LOperand* value() { return inputs_[3]; }
+ bool strict_mode() { return hydrogen()->strict_mode(); }
+};
+
+
+class LStringAdd: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LStringAdd(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string-add")
+ DECLARE_HYDROGEN_ACCESSOR(StringAdd)
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
};
diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc
index ba30c49..ad567bc 100644
--- a/src/ia32/macro-assembler-ia32.cc
+++ b/src/ia32/macro-assembler-ia32.cc
@@ -30,7 +30,7 @@
#if defined(V8_TARGET_ARCH_IA32)
#include "bootstrapper.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "debug.h"
#include "runtime.h"
#include "serialize.h"
@@ -41,11 +41,14 @@
// -------------------------------------------------------------------------
// MacroAssembler implementation.
-MacroAssembler::MacroAssembler(void* buffer, int size)
- : Assembler(buffer, size),
+MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size)
+ : Assembler(arg_isolate, buffer, size),
generating_stub_(false),
- allow_stub_calls_(true),
- code_object_(isolate()->heap()->undefined_value()) {
+ allow_stub_calls_(true) {
+ if (isolate() != NULL) {
+ code_object_ = Handle<Object>(isolate()->heap()->undefined_value(),
+ isolate());
+ }
}
@@ -231,7 +234,7 @@
void MacroAssembler::FCmp() {
- if (Isolate::Current()->cpu_features()->IsSupported(CMOV)) {
+ if (CpuFeatures::IsSupported(CMOV)) {
fucomip();
ffree(0);
fincstp();
@@ -1027,19 +1030,6 @@
}
-void MacroAssembler::NegativeZeroTest(CodeGenerator* cgen,
- Register result,
- Register op,
- JumpTarget* then_target) {
- JumpTarget ok;
- test(result, Operand(result));
- ok.Branch(not_zero, taken);
- test(op, Operand(op));
- then_target->Branch(sign, not_taken);
- ok.Bind();
-}
-
-
void MacroAssembler::NegativeZeroTest(Register result,
Register op,
Label* then_label) {
@@ -1988,17 +1978,14 @@
void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) {
- // Reserve space for Isolate address which is always passed as last parameter
- num_arguments += 1;
-
- int frameAlignment = OS::ActivationFrameAlignment();
- if (frameAlignment != 0) {
+ int frame_alignment = OS::ActivationFrameAlignment();
+ if (frame_alignment != 0) {
// Make stack end at alignment and make room for num_arguments words
// and the original value of esp.
mov(scratch, esp);
sub(Operand(esp), Immediate((num_arguments + 1) * kPointerSize));
- ASSERT(IsPowerOf2(frameAlignment));
- and_(esp, -frameAlignment);
+ ASSERT(IsPowerOf2(frame_alignment));
+ and_(esp, -frame_alignment);
mov(Operand(esp, num_arguments * kPointerSize), scratch);
} else {
sub(Operand(esp), Immediate(num_arguments * kPointerSize));
@@ -2016,11 +2003,6 @@
void MacroAssembler::CallCFunction(Register function,
int num_arguments) {
- // Pass current isolate address as additional parameter.
- mov(Operand(esp, num_arguments * kPointerSize),
- Immediate(ExternalReference::isolate_address()));
- num_arguments += 1;
-
// Check stack alignment.
if (emit_debug_code()) {
CheckStackAlignment();
@@ -2030,13 +2012,15 @@
if (OS::ActivationFrameAlignment() != 0) {
mov(esp, Operand(esp, num_arguments * kPointerSize));
} else {
- add(Operand(esp), Immediate(num_arguments * sizeof(int32_t)));
+ add(Operand(esp), Immediate(num_arguments * kPointerSize));
}
}
CodePatcher::CodePatcher(byte* address, int size)
- : address_(address), size_(size), masm_(address, size + Assembler::kGap) {
+ : address_(address),
+ size_(size),
+ masm_(Isolate::Current(), address, size + Assembler::kGap) {
// Create a new macro assembler pointing to the address of the code to patch.
// The size is adjusted with kGap on order for the assembler to generate size
// bytes of instructions without failing with buffer size constraints.
diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h
index bafb175..6909272 100644
--- a/src/ia32/macro-assembler-ia32.h
+++ b/src/ia32/macro-assembler-ia32.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -50,13 +50,16 @@
typedef Operand MemOperand;
// Forward declaration.
-class JumpTarget;
class PostCallGenerator;
// MacroAssembler implements a collection of frequently used macros.
class MacroAssembler: public Assembler {
public:
- MacroAssembler(void* buffer, int size);
+ // The isolate parameter can be NULL if the macro assembler should
+ // not use isolate-dependent functionality. In this case, it's the
+ // responsibility of the caller to never invoke such function on the
+ // macro assembler.
+ MacroAssembler(Isolate* isolate, void* buffer, int size);
// ---------------------------------------------------------------------------
// GC Support
@@ -420,12 +423,6 @@
// Check if result is zero and op is negative.
void NegativeZeroTest(Register result, Register op, Label* then_label);
- // Check if result is zero and op is negative in code using jump targets.
- void NegativeZeroTest(CodeGenerator* cgen,
- Register result,
- Register op,
- JumpTarget* then_target);
-
// Check if result is zero and any of op1 and op2 are negative.
// Register scratch is destroyed, and it must be different from op2.
void NegativeZeroTest(Register result, Register op1, Register op2,
@@ -580,7 +577,10 @@
void Move(Register target, Handle<Object> value);
- Handle<Object> CodeObject() { return code_object_; }
+ Handle<Object> CodeObject() {
+ ASSERT(!code_object_.is_null());
+ return code_object_;
+ }
// ---------------------------------------------------------------------------
@@ -635,6 +635,10 @@
Register scratch2,
Label* on_not_flat_ascii_strings);
+ static int SafepointRegisterStackIndex(Register reg) {
+ return SafepointRegisterStackIndex(reg.code());
+ }
+
private:
bool generating_stub_;
bool allow_stub_calls_;
diff --git a/src/ia32/regexp-macro-assembler-ia32.cc b/src/ia32/regexp-macro-assembler-ia32.cc
index f1c773b..067f8c8 100644
--- a/src/ia32/regexp-macro-assembler-ia32.cc
+++ b/src/ia32/regexp-macro-assembler-ia32.cc
@@ -99,7 +99,7 @@
RegExpMacroAssemblerIA32::RegExpMacroAssemblerIA32(
Mode mode,
int registers_to_save)
- : masm_(new MacroAssembler(NULL, kRegExpCodeSize)),
+ : masm_(new MacroAssembler(Isolate::Current(), NULL, kRegExpCodeSize)),
mode_(mode),
num_registers_(registers_to_save),
num_saved_registers_(registers_to_save),
@@ -372,14 +372,18 @@
__ push(backtrack_stackpointer());
__ push(ebx);
- static const int argument_count = 3;
+ static const int argument_count = 4;
__ PrepareCallCFunction(argument_count, ecx);
// Put arguments into allocated stack area, last argument highest on stack.
// Parameters are
// Address byte_offset1 - Address captured substring's start.
// Address byte_offset2 - Address of current character position.
// size_t byte_length - length of capture in bytes(!)
+ // Isolate* isolate
+ // Set isolate.
+ __ mov(Operand(esp, 3 * kPointerSize),
+ Immediate(ExternalReference::isolate_address()));
// Set byte_length.
__ mov(Operand(esp, 2 * kPointerSize), ebx);
// Set byte_offset2.
@@ -838,8 +842,10 @@
__ push(edi);
// Call GrowStack(backtrack_stackpointer())
- static const int num_arguments = 2;
+ static const int num_arguments = 3;
__ PrepareCallCFunction(num_arguments, ebx);
+ __ mov(Operand(esp, 2 * kPointerSize),
+ Immediate(ExternalReference::isolate_address()));
__ lea(eax, Operand(ebp, kStackHighEnd));
__ mov(Operand(esp, 1 * kPointerSize), eax);
__ mov(Operand(esp, 0 * kPointerSize), backtrack_stackpointer());
diff --git a/src/ia32/register-allocator-ia32-inl.h b/src/ia32/register-allocator-ia32-inl.h
deleted file mode 100644
index 99ae6eb..0000000
--- a/src/ia32/register-allocator-ia32-inl.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_IA32_REGISTER_ALLOCATOR_IA32_INL_H_
-#define V8_IA32_REGISTER_ALLOCATOR_IA32_INL_H_
-
-#include "v8.h"
-
-namespace v8 {
-namespace internal {
-
-// -------------------------------------------------------------------------
-// RegisterAllocator implementation.
-
-bool RegisterAllocator::IsReserved(Register reg) {
- // The code for this test relies on the order of register codes.
- return reg.code() >= esp.code() && reg.code() <= esi.code();
-}
-
-
-// The register allocator uses small integers to represent the
-// non-reserved assembler registers. The mapping is:
-
-// eax <-> 0, ebx <-> 1, ecx <-> 2, edx <-> 3, edi <-> 4.
-
-int RegisterAllocator::ToNumber(Register reg) {
- ASSERT(reg.is_valid() && !IsReserved(reg));
- const int kNumbers[] = {
- 0, // eax
- 2, // ecx
- 3, // edx
- 1, // ebx
- -1, // esp
- -1, // ebp
- -1, // esi
- 4 // edi
- };
- return kNumbers[reg.code()];
-}
-
-
-Register RegisterAllocator::ToRegister(int num) {
- ASSERT(num >= 0 && num < kNumRegisters);
- const Register kRegisters[] = { eax, ebx, ecx, edx, edi };
- return kRegisters[num];
-}
-
-
-void RegisterAllocator::Initialize() {
- Reset();
- // The non-reserved edi register is live on JS function entry.
- Use(edi); // JS function.
-}
-
-
-} } // namespace v8::internal
-
-#endif // V8_IA32_REGISTER_ALLOCATOR_IA32_INL_H_
diff --git a/src/ia32/register-allocator-ia32.cc b/src/ia32/register-allocator-ia32.cc
deleted file mode 100644
index 6db13d4..0000000
--- a/src/ia32/register-allocator-ia32.cc
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "v8.h"
-
-#if defined(V8_TARGET_ARCH_IA32)
-
-#include "codegen-inl.h"
-#include "register-allocator-inl.h"
-#include "virtual-frame-inl.h"
-
-namespace v8 {
-namespace internal {
-
-// -------------------------------------------------------------------------
-// Result implementation.
-
-void Result::ToRegister() {
- ASSERT(is_valid());
- if (is_constant()) {
- CodeGenerator* code_generator =
- CodeGeneratorScope::Current(Isolate::Current());
- Result fresh = code_generator->allocator()->Allocate();
- ASSERT(fresh.is_valid());
- if (is_untagged_int32()) {
- fresh.set_untagged_int32(true);
- if (handle()->IsSmi()) {
- code_generator->masm()->Set(
- fresh.reg(),
- Immediate(Smi::cast(*handle())->value()));
- } else if (handle()->IsHeapNumber()) {
- double double_value = HeapNumber::cast(*handle())->value();
- int32_t value = DoubleToInt32(double_value);
- if (double_value == 0 && signbit(double_value)) {
- // Negative zero must not be converted to an int32 unless
- // the context allows it.
- code_generator->unsafe_bailout_->Branch(equal);
- code_generator->unsafe_bailout_->Branch(not_equal);
- } else if (double_value == value) {
- code_generator->masm()->Set(fresh.reg(), Immediate(value));
- } else {
- code_generator->unsafe_bailout_->Branch(equal);
- code_generator->unsafe_bailout_->Branch(not_equal);
- }
- } else {
- // Constant is not a number. This was not predicted by AST analysis.
- code_generator->unsafe_bailout_->Branch(equal);
- code_generator->unsafe_bailout_->Branch(not_equal);
- }
- } else if (code_generator->IsUnsafeSmi(handle())) {
- code_generator->MoveUnsafeSmi(fresh.reg(), handle());
- } else {
- code_generator->masm()->Set(fresh.reg(), Immediate(handle()));
- }
- // This result becomes a copy of the fresh one.
- fresh.set_type_info(type_info());
- *this = fresh;
- }
- ASSERT(is_register());
-}
-
-
-void Result::ToRegister(Register target) {
- CodeGenerator* code_generator =
- CodeGeneratorScope::Current(Isolate::Current());
- ASSERT(is_valid());
- if (!is_register() || !reg().is(target)) {
- Result fresh = code_generator->allocator()->Allocate(target);
- ASSERT(fresh.is_valid());
- if (is_register()) {
- code_generator->masm()->mov(fresh.reg(), reg());
- } else {
- ASSERT(is_constant());
- if (is_untagged_int32()) {
- if (handle()->IsSmi()) {
- code_generator->masm()->Set(
- fresh.reg(),
- Immediate(Smi::cast(*handle())->value()));
- } else {
- ASSERT(handle()->IsHeapNumber());
- double double_value = HeapNumber::cast(*handle())->value();
- int32_t value = DoubleToInt32(double_value);
- if (double_value == 0 && signbit(double_value)) {
- // Negative zero must not be converted to an int32 unless
- // the context allows it.
- code_generator->unsafe_bailout_->Branch(equal);
- code_generator->unsafe_bailout_->Branch(not_equal);
- } else if (double_value == value) {
- code_generator->masm()->Set(fresh.reg(), Immediate(value));
- } else {
- code_generator->unsafe_bailout_->Branch(equal);
- code_generator->unsafe_bailout_->Branch(not_equal);
- }
- }
- } else {
- if (code_generator->IsUnsafeSmi(handle())) {
- code_generator->MoveUnsafeSmi(fresh.reg(), handle());
- } else {
- code_generator->masm()->Set(fresh.reg(), Immediate(handle()));
- }
- }
- }
- fresh.set_type_info(type_info());
- fresh.set_untagged_int32(is_untagged_int32());
- *this = fresh;
- } else if (is_register() && reg().is(target)) {
- ASSERT(code_generator->has_valid_frame());
- code_generator->frame()->Spill(target);
- ASSERT(code_generator->allocator()->count(target) == 1);
- }
- ASSERT(is_register());
- ASSERT(reg().is(target));
-}
-
-
-// -------------------------------------------------------------------------
-// RegisterAllocator implementation.
-
-Result RegisterAllocator::AllocateByteRegisterWithoutSpilling() {
- Result result = AllocateWithoutSpilling();
- // Check that the register is a byte register. If not, unuse the
- // register if valid and return an invalid result.
- if (result.is_valid() && !result.reg().is_byte_register()) {
- result.Unuse();
- return Result();
- }
- return result;
-}
-
-
-} } // namespace v8::internal
-
-#endif // V8_TARGET_ARCH_IA32
diff --git a/src/ia32/register-allocator-ia32.h b/src/ia32/register-allocator-ia32.h
deleted file mode 100644
index e7ce91f..0000000
--- a/src/ia32/register-allocator-ia32.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_IA32_REGISTER_ALLOCATOR_IA32_H_
-#define V8_IA32_REGISTER_ALLOCATOR_IA32_H_
-
-namespace v8 {
-namespace internal {
-
-class RegisterAllocatorConstants : public AllStatic {
- public:
- static const int kNumRegisters = 5;
- static const int kInvalidRegister = -1;
-};
-
-
-} } // namespace v8::internal
-
-#endif // V8_IA32_REGISTER_ALLOCATOR_IA32_H_
diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc
index 7730ee3..27d2886 100644
--- a/src/ia32/stub-cache-ia32.cc
+++ b/src/ia32/stub-cache-ia32.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2009 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -30,7 +30,7 @@
#if defined(V8_TARGET_ARCH_IA32)
#include "ic-inl.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "stub-cache.h"
namespace v8 {
@@ -1921,7 +1921,7 @@
// -- esp[(argc + 1) * 4] : receiver
// -----------------------------------
- if (!isolate()->cpu_features()->IsSupported(SSE2)) {
+ if (!CpuFeatures::IsSupported(SSE2)) {
return isolate()->heap()->undefined_value();
}
@@ -3292,7 +3292,7 @@
int arg_number = shared->GetThisPropertyAssignmentArgument(i);
__ mov(ebx, edi);
__ cmp(eax, arg_number);
- if (isolate()->cpu_features()->IsSupported(CMOV)) {
+ if (CpuFeatures::IsSupported(CMOV)) {
CpuFeatures::Scope use_cmov(CMOV);
__ cmov(above, ebx, Operand(ecx, arg_number * -kPointerSize));
} else {
@@ -3611,10 +3611,10 @@
// processors that don't support SSE2. The code in IntegerConvert
// (code-stubs-ia32.cc) is roughly what is needed here though the
// conversion failure case does not need to be handled.
- if (isolate()->cpu_features()->IsSupported(SSE2)) {
+ if (CpuFeatures::IsSupported(SSE2)) {
if (array_type != kExternalIntArray &&
array_type != kExternalUnsignedIntArray) {
- ASSERT(isolate()->cpu_features()->IsSupported(SSE2));
+ ASSERT(CpuFeatures::IsSupported(SSE2));
CpuFeatures::Scope scope(SSE2);
__ cvttsd2si(ecx, FieldOperand(eax, HeapNumber::kValueOffset));
// ecx: untagged integer value
@@ -3629,6 +3629,7 @@
__ bind(&done);
}
__ mov_b(Operand(edi, ebx, times_1, 0), ecx);
+ break;
case kExternalByteArray:
case kExternalUnsignedByteArray:
__ mov_b(Operand(edi, ebx, times_1, 0), ecx);
@@ -3642,7 +3643,7 @@
break;
}
} else {
- if (isolate()->cpu_features()->IsSupported(SSE3)) {
+ if (CpuFeatures::IsSupported(SSE3)) {
CpuFeatures::Scope scope(SSE3);
// fisttp stores values as signed integers. To represent the
// entire range of int and unsigned int arrays, store as a
@@ -3655,7 +3656,7 @@
__ pop(ecx);
__ add(Operand(esp), Immediate(kPointerSize));
} else {
- ASSERT(isolate()->cpu_features()->IsSupported(SSE2));
+ ASSERT(CpuFeatures::IsSupported(SSE2));
CpuFeatures::Scope scope(SSE2);
// We can easily implement the correct rounding behavior for the
// range [0, 2^31-1]. For the time being, to keep this code simple,
diff --git a/src/ia32/virtual-frame-ia32.cc b/src/ia32/virtual-frame-ia32.cc
deleted file mode 100644
index 2613caf..0000000
--- a/src/ia32/virtual-frame-ia32.cc
+++ /dev/null
@@ -1,1366 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "v8.h"
-
-#if defined(V8_TARGET_ARCH_IA32)
-
-#include "codegen-inl.h"
-#include "register-allocator-inl.h"
-#include "scopes.h"
-#include "virtual-frame-inl.h"
-#include "stub-cache.h"
-
-namespace v8 {
-namespace internal {
-
-#define __ ACCESS_MASM(masm())
-
-void VirtualFrame::SyncElementBelowStackPointer(int index) {
- // Emit code to write elements below the stack pointer to their
- // (already allocated) stack address.
- ASSERT(index <= stack_pointer_);
- FrameElement element = elements_[index];
- ASSERT(!element.is_synced());
- switch (element.type()) {
- case FrameElement::INVALID:
- break;
-
- case FrameElement::MEMORY:
- // This function should not be called with synced elements.
- // (memory elements are always synced).
- UNREACHABLE();
- break;
-
- case FrameElement::REGISTER:
- __ mov(Operand(ebp, fp_relative(index)), element.reg());
- break;
-
- case FrameElement::CONSTANT:
- if (cgen()->IsUnsafeSmi(element.handle())) {
- cgen()->StoreUnsafeSmiToLocal(fp_relative(index), element.handle());
- } else {
- __ Set(Operand(ebp, fp_relative(index)),
- Immediate(element.handle()));
- }
- break;
-
- case FrameElement::COPY: {
- int backing_index = element.index();
- FrameElement backing_element = elements_[backing_index];
- if (backing_element.is_memory()) {
- Result temp = cgen()->allocator()->Allocate();
- ASSERT(temp.is_valid());
- __ mov(temp.reg(), Operand(ebp, fp_relative(backing_index)));
- __ mov(Operand(ebp, fp_relative(index)), temp.reg());
- } else {
- ASSERT(backing_element.is_register());
- __ mov(Operand(ebp, fp_relative(index)), backing_element.reg());
- }
- break;
- }
- }
- elements_[index].set_sync();
-}
-
-
-void VirtualFrame::SyncElementByPushing(int index) {
- // Sync an element of the frame that is just above the stack pointer
- // by pushing it.
- ASSERT(index == stack_pointer_ + 1);
- stack_pointer_++;
- FrameElement element = elements_[index];
-
- switch (element.type()) {
- case FrameElement::INVALID:
- __ push(Immediate(Smi::FromInt(0)));
- break;
-
- case FrameElement::MEMORY:
- // No memory elements exist above the stack pointer.
- UNREACHABLE();
- break;
-
- case FrameElement::REGISTER:
- __ push(element.reg());
- break;
-
- case FrameElement::CONSTANT:
- if (cgen()->IsUnsafeSmi(element.handle())) {
- cgen()->PushUnsafeSmi(element.handle());
- } else {
- __ push(Immediate(element.handle()));
- }
- break;
-
- case FrameElement::COPY: {
- int backing_index = element.index();
- FrameElement backing = elements_[backing_index];
- ASSERT(backing.is_memory() || backing.is_register());
- if (backing.is_memory()) {
- __ push(Operand(ebp, fp_relative(backing_index)));
- } else {
- __ push(backing.reg());
- }
- break;
- }
- }
- elements_[index].set_sync();
-}
-
-
-// Clear the dirty bits for the range of elements in
-// [min(stack_pointer_ + 1,begin), end].
-void VirtualFrame::SyncRange(int begin, int end) {
- ASSERT(begin >= 0);
- ASSERT(end < element_count());
- // Sync elements below the range if they have not been materialized
- // on the stack.
- int start = Min(begin, stack_pointer_ + 1);
-
- // Emit normal push instructions for elements above stack pointer
- // and use mov instructions if we are below stack pointer.
- for (int i = start; i <= end; i++) {
- if (!elements_[i].is_synced()) {
- if (i <= stack_pointer_) {
- SyncElementBelowStackPointer(i);
- } else {
- SyncElementByPushing(i);
- }
- }
- }
-}
-
-
-void VirtualFrame::MakeMergable() {
- for (int i = 0; i < element_count(); i++) {
- FrameElement element = elements_[i];
-
- // All number type information is reset to unknown for a mergable frame
- // because of incoming back edges.
- if (element.is_constant() || element.is_copy()) {
- if (element.is_synced()) {
- // Just spill.
- elements_[i] = FrameElement::MemoryElement(TypeInfo::Unknown());
- } else {
- // Allocate to a register.
- FrameElement backing_element; // Invalid if not a copy.
- if (element.is_copy()) {
- backing_element = elements_[element.index()];
- }
- Result fresh = cgen()->allocator()->Allocate();
- ASSERT(fresh.is_valid()); // A register was spilled if all were in use.
- elements_[i] =
- FrameElement::RegisterElement(fresh.reg(),
- FrameElement::NOT_SYNCED,
- TypeInfo::Unknown());
- Use(fresh.reg(), i);
-
- // Emit a move.
- if (element.is_constant()) {
- if (cgen()->IsUnsafeSmi(element.handle())) {
- cgen()->MoveUnsafeSmi(fresh.reg(), element.handle());
- } else {
- __ Set(fresh.reg(), Immediate(element.handle()));
- }
- } else {
- ASSERT(element.is_copy());
- // Copies are only backed by register or memory locations.
- if (backing_element.is_register()) {
- // The backing store may have been spilled by allocating,
- // but that's OK. If it was, the value is right where we
- // want it.
- if (!fresh.reg().is(backing_element.reg())) {
- __ mov(fresh.reg(), backing_element.reg());
- }
- } else {
- ASSERT(backing_element.is_memory());
- __ mov(fresh.reg(), Operand(ebp, fp_relative(element.index())));
- }
- }
- }
- // No need to set the copied flag --- there are no copies.
- } else {
- // Clear the copy flag of non-constant, non-copy elements.
- // They cannot be copied because copies are not allowed.
- // The copy flag is not relied on before the end of this loop,
- // including when registers are spilled.
- elements_[i].clear_copied();
- elements_[i].set_type_info(TypeInfo::Unknown());
- }
- }
-}
-
-
-void VirtualFrame::MergeTo(VirtualFrame* expected) {
- Comment cmnt(masm(), "[ Merge frame");
- // We should always be merging the code generator's current frame to an
- // expected frame.
- ASSERT(cgen()->frame() == this);
-
- // Adjust the stack pointer upward (toward the top of the virtual
- // frame) if necessary.
- if (stack_pointer_ < expected->stack_pointer_) {
- int difference = expected->stack_pointer_ - stack_pointer_;
- stack_pointer_ = expected->stack_pointer_;
- __ sub(Operand(esp), Immediate(difference * kPointerSize));
- }
-
- MergeMoveRegistersToMemory(expected);
- MergeMoveRegistersToRegisters(expected);
- MergeMoveMemoryToRegisters(expected);
-
- // Adjust the stack pointer downward if necessary.
- if (stack_pointer_ > expected->stack_pointer_) {
- int difference = stack_pointer_ - expected->stack_pointer_;
- stack_pointer_ = expected->stack_pointer_;
- __ add(Operand(esp), Immediate(difference * kPointerSize));
- }
-
- // At this point, the frames should be identical.
- ASSERT(Equals(expected));
-}
-
-
-void VirtualFrame::MergeMoveRegistersToMemory(VirtualFrame* expected) {
- ASSERT(stack_pointer_ >= expected->stack_pointer_);
-
- // Move registers, constants, and copies to memory. Perform moves
- // from the top downward in the frame in order to leave the backing
- // stores of copies in registers.
- //
- // Moving memory-backed copies to memory requires a spare register
- // for the memory-to-memory moves. Since we are performing a merge,
- // we use esi (which is already saved in the frame). We keep track
- // of the index of the frame element esi is caching or kIllegalIndex
- // if esi has not been disturbed.
- int esi_caches = kIllegalIndex;
- for (int i = element_count() - 1; i >= 0; i--) {
- FrameElement target = expected->elements_[i];
- if (target.is_register()) continue; // Handle registers later.
- if (target.is_memory()) {
- FrameElement source = elements_[i];
- switch (source.type()) {
- case FrameElement::INVALID:
- // Not a legal merge move.
- UNREACHABLE();
- break;
-
- case FrameElement::MEMORY:
- // Already in place.
- break;
-
- case FrameElement::REGISTER:
- Unuse(source.reg());
- if (!source.is_synced()) {
- __ mov(Operand(ebp, fp_relative(i)), source.reg());
- }
- break;
-
- case FrameElement::CONSTANT:
- if (!source.is_synced()) {
- if (cgen()->IsUnsafeSmi(source.handle())) {
- esi_caches = i;
- cgen()->MoveUnsafeSmi(esi, source.handle());
- __ mov(Operand(ebp, fp_relative(i)), esi);
- } else {
- __ Set(Operand(ebp, fp_relative(i)), Immediate(source.handle()));
- }
- }
- break;
-
- case FrameElement::COPY:
- if (!source.is_synced()) {
- int backing_index = source.index();
- FrameElement backing_element = elements_[backing_index];
- if (backing_element.is_memory()) {
- // If we have to spill a register, we spill esi.
- if (esi_caches != backing_index) {
- esi_caches = backing_index;
- __ mov(esi, Operand(ebp, fp_relative(backing_index)));
- }
- __ mov(Operand(ebp, fp_relative(i)), esi);
- } else {
- ASSERT(backing_element.is_register());
- __ mov(Operand(ebp, fp_relative(i)), backing_element.reg());
- }
- }
- break;
- }
- }
- elements_[i] = target;
- }
-
- if (esi_caches != kIllegalIndex) {
- __ mov(esi, Operand(ebp, fp_relative(context_index())));
- }
-}
-
-
-void VirtualFrame::MergeMoveRegistersToRegisters(VirtualFrame* expected) {
- // We have already done X-to-memory moves.
- ASSERT(stack_pointer_ >= expected->stack_pointer_);
-
- for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
- // Move the right value into register i if it is currently in a register.
- int index = expected->register_location(i);
- int use_index = register_location(i);
- // Skip if register i is unused in the target or else if source is
- // not a register (this is not a register-to-register move).
- if (index == kIllegalIndex || !elements_[index].is_register()) continue;
-
- Register target = RegisterAllocator::ToRegister(i);
- Register source = elements_[index].reg();
- if (index != use_index) {
- if (use_index == kIllegalIndex) { // Target is currently unused.
- // Copy contents of source from source to target.
- // Set frame element register to target.
- Use(target, index);
- Unuse(source);
- __ mov(target, source);
- } else {
- // Exchange contents of registers source and target.
- // Nothing except the register backing use_index has changed.
- elements_[use_index].set_reg(source);
- set_register_location(target, index);
- set_register_location(source, use_index);
- __ xchg(source, target);
- }
- }
-
- if (!elements_[index].is_synced() &&
- expected->elements_[index].is_synced()) {
- __ mov(Operand(ebp, fp_relative(index)), target);
- }
- elements_[index] = expected->elements_[index];
- }
-}
-
-
-void VirtualFrame::MergeMoveMemoryToRegisters(VirtualFrame* expected) {
- // Move memory, constants, and copies to registers. This is the
- // final step and since it is not done from the bottom up, but in
- // register code order, we have special code to ensure that the backing
- // elements of copies are in their correct locations when we
- // encounter the copies.
- for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
- int index = expected->register_location(i);
- if (index != kIllegalIndex) {
- FrameElement source = elements_[index];
- FrameElement target = expected->elements_[index];
- Register target_reg = RegisterAllocator::ToRegister(i);
- ASSERT(target.reg().is(target_reg));
- switch (source.type()) {
- case FrameElement::INVALID: // Fall through.
- UNREACHABLE();
- break;
- case FrameElement::REGISTER:
- ASSERT(source.Equals(target));
- // Go to next iteration. Skips Use(target_reg) and syncing
- // below. It is safe to skip syncing because a target
- // register frame element would only be synced if all source
- // elements were.
- continue;
- break;
- case FrameElement::MEMORY:
- ASSERT(index <= stack_pointer_);
- __ mov(target_reg, Operand(ebp, fp_relative(index)));
- break;
-
- case FrameElement::CONSTANT:
- if (cgen()->IsUnsafeSmi(source.handle())) {
- cgen()->MoveUnsafeSmi(target_reg, source.handle());
- } else {
- __ Set(target_reg, Immediate(source.handle()));
- }
- break;
-
- case FrameElement::COPY: {
- int backing_index = source.index();
- FrameElement backing = elements_[backing_index];
- ASSERT(backing.is_memory() || backing.is_register());
- if (backing.is_memory()) {
- ASSERT(backing_index <= stack_pointer_);
- // Code optimization if backing store should also move
- // to a register: move backing store to its register first.
- if (expected->elements_[backing_index].is_register()) {
- FrameElement new_backing = expected->elements_[backing_index];
- Register new_backing_reg = new_backing.reg();
- ASSERT(!is_used(new_backing_reg));
- elements_[backing_index] = new_backing;
- Use(new_backing_reg, backing_index);
- __ mov(new_backing_reg,
- Operand(ebp, fp_relative(backing_index)));
- __ mov(target_reg, new_backing_reg);
- } else {
- __ mov(target_reg, Operand(ebp, fp_relative(backing_index)));
- }
- } else {
- __ mov(target_reg, backing.reg());
- }
- }
- }
- // Ensure the proper sync state.
- if (target.is_synced() && !source.is_synced()) {
- __ mov(Operand(ebp, fp_relative(index)), target_reg);
- }
- Use(target_reg, index);
- elements_[index] = target;
- }
- }
-}
-
-
-void VirtualFrame::Enter() {
- // Registers live on entry: esp, ebp, esi, edi.
- Comment cmnt(masm(), "[ Enter JS frame");
-
-#ifdef DEBUG
- if (FLAG_debug_code) {
- // Verify that edi contains a JS function. The following code
- // relies on eax being available for use.
- __ test(edi, Immediate(kSmiTagMask));
- __ Check(not_zero,
- "VirtualFrame::Enter - edi is not a function (smi check).");
- __ CmpObjectType(edi, JS_FUNCTION_TYPE, eax);
- __ Check(equal,
- "VirtualFrame::Enter - edi is not a function (map check).");
- }
-#endif
-
- EmitPush(ebp);
-
- __ mov(ebp, Operand(esp));
-
- // Store the context in the frame. The context is kept in esi and a
- // copy is stored in the frame. The external reference to esi
- // remains.
- EmitPush(esi);
-
- // Store the function in the frame. The frame owns the register
- // reference now (ie, it can keep it in edi or spill it later).
- Push(edi);
- SyncElementAt(element_count() - 1);
- cgen()->allocator()->Unuse(edi);
-}
-
-
-void VirtualFrame::Exit() {
- Comment cmnt(masm(), "[ Exit JS frame");
- // Record the location of the JS exit code for patching when setting
- // break point.
- __ RecordJSReturn();
-
- // Avoid using the leave instruction here, because it is too
- // short. We need the return sequence to be a least the size of a
- // call instruction to support patching the exit code in the
- // debugger. See VisitReturnStatement for the full return sequence.
- __ mov(esp, Operand(ebp));
- stack_pointer_ = frame_pointer();
- for (int i = element_count() - 1; i > stack_pointer_; i--) {
- FrameElement last = elements_.RemoveLast();
- if (last.is_register()) {
- Unuse(last.reg());
- }
- }
-
- EmitPop(ebp);
-}
-
-
-void VirtualFrame::AllocateStackSlots() {
- int count = local_count();
- if (count > 0) {
- Comment cmnt(masm(), "[ Allocate space for locals");
- // The locals are initialized to a constant (the undefined value), but
- // we sync them with the actual frame to allocate space for spilling
- // them later. First sync everything above the stack pointer so we can
- // use pushes to allocate and initialize the locals.
- SyncRange(stack_pointer_ + 1, element_count() - 1);
- Handle<Object> undefined = FACTORY->undefined_value();
- FrameElement initial_value =
- FrameElement::ConstantElement(undefined, FrameElement::SYNCED);
- if (count == 1) {
- __ push(Immediate(undefined));
- } else if (count < kLocalVarBound) {
- // For less locals the unrolled loop is more compact.
- Result temp = cgen()->allocator()->Allocate();
- ASSERT(temp.is_valid());
- __ Set(temp.reg(), Immediate(undefined));
- for (int i = 0; i < count; i++) {
- __ push(temp.reg());
- }
- } else {
- // For more locals a loop in generated code is more compact.
- Label alloc_locals_loop;
- Result cnt = cgen()->allocator()->Allocate();
- Result tmp = cgen()->allocator()->Allocate();
- ASSERT(cnt.is_valid());
- ASSERT(tmp.is_valid());
- __ mov(cnt.reg(), Immediate(count));
- __ mov(tmp.reg(), Immediate(undefined));
- __ bind(&alloc_locals_loop);
- __ push(tmp.reg());
- __ dec(cnt.reg());
- __ j(not_zero, &alloc_locals_loop);
- }
- for (int i = 0; i < count; i++) {
- elements_.Add(initial_value);
- stack_pointer_++;
- }
- }
-}
-
-
-void VirtualFrame::SaveContextRegister() {
- ASSERT(elements_[context_index()].is_memory());
- __ mov(Operand(ebp, fp_relative(context_index())), esi);
-}
-
-
-void VirtualFrame::RestoreContextRegister() {
- ASSERT(elements_[context_index()].is_memory());
- __ mov(esi, Operand(ebp, fp_relative(context_index())));
-}
-
-
-void VirtualFrame::PushReceiverSlotAddress() {
- Result temp = cgen()->allocator()->Allocate();
- ASSERT(temp.is_valid());
- __ lea(temp.reg(), ParameterAt(-1));
- Push(&temp);
-}
-
-
-int VirtualFrame::InvalidateFrameSlotAt(int index) {
- FrameElement original = elements_[index];
-
- // Is this element the backing store of any copies?
- int new_backing_index = kIllegalIndex;
- if (original.is_copied()) {
- // Verify it is copied, and find first copy.
- for (int i = index + 1; i < element_count(); i++) {
- if (elements_[i].is_copy() && elements_[i].index() == index) {
- new_backing_index = i;
- break;
- }
- }
- }
-
- if (new_backing_index == kIllegalIndex) {
- // No copies found, return kIllegalIndex.
- if (original.is_register()) {
- Unuse(original.reg());
- }
- elements_[index] = FrameElement::InvalidElement();
- return kIllegalIndex;
- }
-
- // This is the backing store of copies.
- Register backing_reg;
- if (original.is_memory()) {
- Result fresh = cgen()->allocator()->Allocate();
- ASSERT(fresh.is_valid());
- Use(fresh.reg(), new_backing_index);
- backing_reg = fresh.reg();
- __ mov(backing_reg, Operand(ebp, fp_relative(index)));
- } else {
- // The original was in a register.
- backing_reg = original.reg();
- set_register_location(backing_reg, new_backing_index);
- }
- // Invalidate the element at index.
- elements_[index] = FrameElement::InvalidElement();
- // Set the new backing element.
- if (elements_[new_backing_index].is_synced()) {
- elements_[new_backing_index] =
- FrameElement::RegisterElement(backing_reg,
- FrameElement::SYNCED,
- original.type_info());
- } else {
- elements_[new_backing_index] =
- FrameElement::RegisterElement(backing_reg,
- FrameElement::NOT_SYNCED,
- original.type_info());
- }
- // Update the other copies.
- for (int i = new_backing_index + 1; i < element_count(); i++) {
- if (elements_[i].is_copy() && elements_[i].index() == index) {
- elements_[i].set_index(new_backing_index);
- elements_[new_backing_index].set_copied();
- }
- }
- return new_backing_index;
-}
-
-
-void VirtualFrame::TakeFrameSlotAt(int index) {
- ASSERT(index >= 0);
- ASSERT(index <= element_count());
- FrameElement original = elements_[index];
- int new_backing_store_index = InvalidateFrameSlotAt(index);
- if (new_backing_store_index != kIllegalIndex) {
- elements_.Add(CopyElementAt(new_backing_store_index));
- return;
- }
-
- switch (original.type()) {
- case FrameElement::MEMORY: {
- // Emit code to load the original element's data into a register.
- // Push that register as a FrameElement on top of the frame.
- Result fresh = cgen()->allocator()->Allocate();
- ASSERT(fresh.is_valid());
- FrameElement new_element =
- FrameElement::RegisterElement(fresh.reg(),
- FrameElement::NOT_SYNCED,
- original.type_info());
- Use(fresh.reg(), element_count());
- elements_.Add(new_element);
- __ mov(fresh.reg(), Operand(ebp, fp_relative(index)));
- break;
- }
- case FrameElement::REGISTER:
- Use(original.reg(), element_count());
- // Fall through.
- case FrameElement::CONSTANT:
- case FrameElement::COPY:
- original.clear_sync();
- elements_.Add(original);
- break;
- case FrameElement::INVALID:
- UNREACHABLE();
- break;
- }
-}
-
-
-void VirtualFrame::StoreToFrameSlotAt(int index) {
- // Store the value on top of the frame to the virtual frame slot at
- // a given index. The value on top of the frame is left in place.
- // This is a duplicating operation, so it can create copies.
- ASSERT(index >= 0);
- ASSERT(index < element_count());
-
- int top_index = element_count() - 1;
- FrameElement top = elements_[top_index];
- FrameElement original = elements_[index];
- if (top.is_copy() && top.index() == index) return;
- ASSERT(top.is_valid());
-
- InvalidateFrameSlotAt(index);
-
- // InvalidateFrameSlotAt can potentially change any frame element, due
- // to spilling registers to allocate temporaries in order to preserve
- // the copy-on-write semantics of aliased elements. Reload top from
- // the frame.
- top = elements_[top_index];
-
- if (top.is_copy()) {
- // There are two cases based on the relative positions of the
- // stored-to slot and the backing slot of the top element.
- int backing_index = top.index();
- ASSERT(backing_index != index);
- if (backing_index < index) {
- // 1. The top element is a copy of a slot below the stored-to
- // slot. The stored-to slot becomes an unsynced copy of that
- // same backing slot.
- elements_[index] = CopyElementAt(backing_index);
- } else {
- // 2. The top element is a copy of a slot above the stored-to
- // slot. The stored-to slot becomes the new (unsynced) backing
- // slot and both the top element and the element at the former
- // backing slot become copies of it. The sync state of the top
- // and former backing elements is preserved.
- FrameElement backing_element = elements_[backing_index];
- ASSERT(backing_element.is_memory() || backing_element.is_register());
- if (backing_element.is_memory()) {
- // Because sets of copies are canonicalized to be backed by
- // their lowest frame element, and because memory frame
- // elements are backed by the corresponding stack address, we
- // have to move the actual value down in the stack.
- //
- // TODO(209): considering allocating the stored-to slot to the
- // temp register. Alternatively, allow copies to appear in
- // any order in the frame and lazily move the value down to
- // the slot.
- Result temp = cgen()->allocator()->Allocate();
- ASSERT(temp.is_valid());
- __ mov(temp.reg(), Operand(ebp, fp_relative(backing_index)));
- __ mov(Operand(ebp, fp_relative(index)), temp.reg());
- } else {
- set_register_location(backing_element.reg(), index);
- if (backing_element.is_synced()) {
- // If the element is a register, we will not actually move
- // anything on the stack but only update the virtual frame
- // element.
- backing_element.clear_sync();
- }
- }
- elements_[index] = backing_element;
-
- // The old backing element becomes a copy of the new backing
- // element.
- FrameElement new_element = CopyElementAt(index);
- elements_[backing_index] = new_element;
- if (backing_element.is_synced()) {
- elements_[backing_index].set_sync();
- }
-
- // All the copies of the old backing element (including the top
- // element) become copies of the new backing element.
- for (int i = backing_index + 1; i < element_count(); i++) {
- if (elements_[i].is_copy() && elements_[i].index() == backing_index) {
- elements_[i].set_index(index);
- }
- }
- }
- return;
- }
-
- // Move the top element to the stored-to slot and replace it (the
- // top element) with a copy.
- elements_[index] = top;
- if (top.is_memory()) {
- // TODO(209): consider allocating the stored-to slot to the temp
- // register. Alternatively, allow copies to appear in any order
- // in the frame and lazily move the value down to the slot.
- FrameElement new_top = CopyElementAt(index);
- new_top.set_sync();
- elements_[top_index] = new_top;
-
- // The sync state of the former top element is correct (synced).
- // Emit code to move the value down in the frame.
- Result temp = cgen()->allocator()->Allocate();
- ASSERT(temp.is_valid());
- __ mov(temp.reg(), Operand(esp, 0));
- __ mov(Operand(ebp, fp_relative(index)), temp.reg());
- } else if (top.is_register()) {
- set_register_location(top.reg(), index);
- // The stored-to slot has the (unsynced) register reference and
- // the top element becomes a copy. The sync state of the top is
- // preserved.
- FrameElement new_top = CopyElementAt(index);
- if (top.is_synced()) {
- new_top.set_sync();
- elements_[index].clear_sync();
- }
- elements_[top_index] = new_top;
- } else {
- // The stored-to slot holds the same value as the top but
- // unsynced. (We do not have copies of constants yet.)
- ASSERT(top.is_constant());
- elements_[index].clear_sync();
- }
-}
-
-
-void VirtualFrame::UntaggedPushFrameSlotAt(int index) {
- ASSERT(index >= 0);
- ASSERT(index <= element_count());
- FrameElement original = elements_[index];
- if (original.is_copy()) {
- original = elements_[original.index()];
- index = original.index();
- }
-
- switch (original.type()) {
- case FrameElement::MEMORY:
- case FrameElement::REGISTER: {
- Label done;
- // Emit code to load the original element's data into a register.
- // Push that register as a FrameElement on top of the frame.
- Result fresh = cgen()->allocator()->Allocate();
- ASSERT(fresh.is_valid());
- Register fresh_reg = fresh.reg();
- FrameElement new_element =
- FrameElement::RegisterElement(fresh_reg,
- FrameElement::NOT_SYNCED,
- original.type_info());
- new_element.set_untagged_int32(true);
- Use(fresh_reg, element_count());
- fresh.Unuse(); // BreakTarget does not handle a live Result well.
- elements_.Add(new_element);
- if (original.is_register()) {
- __ mov(fresh_reg, original.reg());
- } else {
- ASSERT(original.is_memory());
- __ mov(fresh_reg, Operand(ebp, fp_relative(index)));
- }
- // Now convert the value to int32, or bail out.
- if (original.type_info().IsSmi()) {
- __ SmiUntag(fresh_reg);
- // Pushing the element is completely done.
- } else {
- __ test(fresh_reg, Immediate(kSmiTagMask));
- Label not_smi;
- __ j(not_zero, ¬_smi);
- __ SmiUntag(fresh_reg);
- __ jmp(&done);
-
- __ bind(¬_smi);
- if (!original.type_info().IsNumber()) {
- __ cmp(FieldOperand(fresh_reg, HeapObject::kMapOffset),
- FACTORY->heap_number_map());
- cgen()->unsafe_bailout_->Branch(not_equal);
- }
-
- if (!Isolate::Current()->cpu_features()->IsSupported(SSE2)) {
- UNREACHABLE();
- } else {
- CpuFeatures::Scope use_sse2(SSE2);
- __ movdbl(xmm0, FieldOperand(fresh_reg, HeapNumber::kValueOffset));
- __ cvttsd2si(fresh_reg, Operand(xmm0));
- __ cvtsi2sd(xmm1, Operand(fresh_reg));
- __ ucomisd(xmm0, xmm1);
- cgen()->unsafe_bailout_->Branch(not_equal);
- cgen()->unsafe_bailout_->Branch(parity_even); // NaN.
- // Test for negative zero.
- __ test(fresh_reg, Operand(fresh_reg));
- __ j(not_zero, &done);
- __ movmskpd(fresh_reg, xmm0);
- __ and_(fresh_reg, 0x1);
- cgen()->unsafe_bailout_->Branch(not_equal);
- }
- __ bind(&done);
- }
- break;
- }
- case FrameElement::CONSTANT:
- elements_.Add(CopyElementAt(index));
- elements_[element_count() - 1].set_untagged_int32(true);
- break;
- case FrameElement::COPY:
- case FrameElement::INVALID:
- UNREACHABLE();
- break;
- }
-}
-
-
-void VirtualFrame::PushTryHandler(HandlerType type) {
- ASSERT(cgen()->HasValidEntryRegisters());
- // Grow the expression stack by handler size less one (the return
- // address is already pushed by a call instruction).
- Adjust(kHandlerSize - 1);
- __ PushTryHandler(IN_JAVASCRIPT, type);
-}
-
-
-Result VirtualFrame::RawCallStub(CodeStub* stub) {
- ASSERT(cgen()->HasValidEntryRegisters());
- __ CallStub(stub);
- Result result = cgen()->allocator()->Allocate(eax);
- ASSERT(result.is_valid());
- return result;
-}
-
-
-Result VirtualFrame::CallStub(CodeStub* stub, Result* arg) {
- PrepareForCall(0, 0);
- arg->ToRegister(eax);
- arg->Unuse();
- return RawCallStub(stub);
-}
-
-
-Result VirtualFrame::CallStub(CodeStub* stub, Result* arg0, Result* arg1) {
- PrepareForCall(0, 0);
-
- if (arg0->is_register() && arg0->reg().is(eax)) {
- if (arg1->is_register() && arg1->reg().is(edx)) {
- // Wrong registers.
- __ xchg(eax, edx);
- } else {
- // Register edx is free for arg0, which frees eax for arg1.
- arg0->ToRegister(edx);
- arg1->ToRegister(eax);
- }
- } else {
- // Register eax is free for arg1, which guarantees edx is free for
- // arg0.
- arg1->ToRegister(eax);
- arg0->ToRegister(edx);
- }
-
- arg0->Unuse();
- arg1->Unuse();
- return RawCallStub(stub);
-}
-
-
-Result VirtualFrame::CallJSFunction(int arg_count) {
- Result function = Pop();
-
- // InvokeFunction requires function in edi. Move it in there.
- function.ToRegister(edi);
- function.Unuse();
-
- // +1 for receiver.
- PrepareForCall(arg_count + 1, arg_count + 1);
- ASSERT(cgen()->HasValidEntryRegisters());
- ParameterCount count(arg_count);
- __ InvokeFunction(edi, count, CALL_FUNCTION);
- RestoreContextRegister();
- Result result = cgen()->allocator()->Allocate(eax);
- ASSERT(result.is_valid());
- return result;
-}
-
-
-Result VirtualFrame::CallRuntime(const Runtime::Function* f, int arg_count) {
- PrepareForCall(arg_count, arg_count);
- ASSERT(cgen()->HasValidEntryRegisters());
- __ CallRuntime(f, arg_count);
- Result result = cgen()->allocator()->Allocate(eax);
- ASSERT(result.is_valid());
- return result;
-}
-
-
-Result VirtualFrame::CallRuntime(Runtime::FunctionId id, int arg_count) {
- PrepareForCall(arg_count, arg_count);
- ASSERT(cgen()->HasValidEntryRegisters());
- __ CallRuntime(id, arg_count);
- Result result = cgen()->allocator()->Allocate(eax);
- ASSERT(result.is_valid());
- return result;
-}
-
-
-#ifdef ENABLE_DEBUGGER_SUPPORT
-void VirtualFrame::DebugBreak() {
- PrepareForCall(0, 0);
- ASSERT(cgen()->HasValidEntryRegisters());
- __ DebugBreak();
- Result result = cgen()->allocator()->Allocate(eax);
- ASSERT(result.is_valid());
-}
-#endif
-
-
-Result VirtualFrame::InvokeBuiltin(Builtins::JavaScript id,
- InvokeFlag flag,
- int arg_count) {
- PrepareForCall(arg_count, arg_count);
- ASSERT(cgen()->HasValidEntryRegisters());
- __ InvokeBuiltin(id, flag);
- Result result = cgen()->allocator()->Allocate(eax);
- ASSERT(result.is_valid());
- return result;
-}
-
-
-Result VirtualFrame::RawCallCodeObject(Handle<Code> code,
- RelocInfo::Mode rmode) {
- ASSERT(cgen()->HasValidEntryRegisters());
- __ call(code, rmode);
- Result result = cgen()->allocator()->Allocate(eax);
- ASSERT(result.is_valid());
- return result;
-}
-
-
-// This function assumes that the only results that could be in a_reg or b_reg
-// are a and b. Other results can be live, but must not be in a_reg or b_reg.
-void VirtualFrame::MoveResultsToRegisters(Result* a,
- Result* b,
- Register a_reg,
- Register b_reg) {
- if (a->is_register() && a->reg().is(a_reg)) {
- b->ToRegister(b_reg);
- } else if (!cgen()->allocator()->is_used(a_reg)) {
- a->ToRegister(a_reg);
- b->ToRegister(b_reg);
- } else if (cgen()->allocator()->is_used(b_reg)) {
- // a must be in b_reg, b in a_reg.
- __ xchg(a_reg, b_reg);
- // Results a and b will be invalidated, so it is ok if they are switched.
- } else {
- b->ToRegister(b_reg);
- a->ToRegister(a_reg);
- }
- a->Unuse();
- b->Unuse();
-}
-
-
-Result VirtualFrame::CallLoadIC(RelocInfo::Mode mode) {
- // Name and receiver are on the top of the frame. The IC expects
- // name in ecx and receiver in eax.
- Result name = Pop();
- Result receiver = Pop();
- PrepareForCall(0, 0); // No stack arguments.
- MoveResultsToRegisters(&name, &receiver, ecx, eax);
-
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- Builtins::kLoadIC_Initialize));
- return RawCallCodeObject(ic, mode);
-}
-
-
-Result VirtualFrame::CallKeyedLoadIC(RelocInfo::Mode mode) {
- // Key and receiver are on top of the frame. Put them in eax and edx.
- Result key = Pop();
- Result receiver = Pop();
- PrepareForCall(0, 0);
- MoveResultsToRegisters(&key, &receiver, eax, edx);
-
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- Builtins::kKeyedLoadIC_Initialize));
- return RawCallCodeObject(ic, mode);
-}
-
-
-Result VirtualFrame::CallStoreIC(Handle<String> name,
- bool is_contextual,
- StrictModeFlag strict_mode) {
- // Value and (if not contextual) receiver are on top of the frame.
- // The IC expects name in ecx, value in eax, and receiver in edx.
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- (strict_mode == kStrictMode) ? Builtins::kStoreIC_Initialize_Strict
- : Builtins::kStoreIC_Initialize));
-
- Result value = Pop();
- RelocInfo::Mode mode;
- if (is_contextual) {
- PrepareForCall(0, 0);
- value.ToRegister(eax);
- __ mov(edx, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
- value.Unuse();
- mode = RelocInfo::CODE_TARGET_CONTEXT;
- } else {
- Result receiver = Pop();
- PrepareForCall(0, 0);
- MoveResultsToRegisters(&value, &receiver, eax, edx);
- mode = RelocInfo::CODE_TARGET;
- }
- __ mov(ecx, name);
- return RawCallCodeObject(ic, mode);
-}
-
-
-Result VirtualFrame::CallKeyedStoreIC(StrictModeFlag strict_mode) {
- // Value, key, and receiver are on the top of the frame. The IC
- // expects value in eax, key in ecx, and receiver in edx.
- Result value = Pop();
- Result key = Pop();
- Result receiver = Pop();
- PrepareForCall(0, 0);
- if (!cgen()->allocator()->is_used(eax) ||
- (value.is_register() && value.reg().is(eax))) {
- if (!cgen()->allocator()->is_used(eax)) {
- value.ToRegister(eax);
- }
- MoveResultsToRegisters(&key, &receiver, ecx, edx);
- value.Unuse();
- } else if (!cgen()->allocator()->is_used(ecx) ||
- (key.is_register() && key.reg().is(ecx))) {
- if (!cgen()->allocator()->is_used(ecx)) {
- key.ToRegister(ecx);
- }
- MoveResultsToRegisters(&value, &receiver, eax, edx);
- key.Unuse();
- } else if (!cgen()->allocator()->is_used(edx) ||
- (receiver.is_register() && receiver.reg().is(edx))) {
- if (!cgen()->allocator()->is_used(edx)) {
- receiver.ToRegister(edx);
- }
- MoveResultsToRegisters(&key, &value, ecx, eax);
- receiver.Unuse();
- } else {
- // All three registers are used, and no value is in the correct place.
- // We have one of the two circular permutations of eax, ecx, edx.
- ASSERT(value.is_register());
- if (value.reg().is(ecx)) {
- __ xchg(eax, edx);
- __ xchg(eax, ecx);
- } else {
- __ xchg(eax, ecx);
- __ xchg(eax, edx);
- }
- value.Unuse();
- key.Unuse();
- receiver.Unuse();
- }
-
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- (strict_mode == kStrictMode) ? Builtins::kKeyedStoreIC_Initialize_Strict
- : Builtins::kKeyedStoreIC_Initialize));
- return RawCallCodeObject(ic, RelocInfo::CODE_TARGET);
-}
-
-
-Result VirtualFrame::CallCallIC(RelocInfo::Mode mode,
- int arg_count,
- int loop_nesting) {
- // Function name, arguments, and receiver are on top of the frame.
- // The IC expects the name in ecx and the rest on the stack and
- // drops them all.
- InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP;
- Handle<Code> ic = Isolate::Current()->stub_cache()->ComputeCallInitialize(
- arg_count, in_loop);
- // Spill args, receiver, and function. The call will drop args and
- // receiver.
- Result name = Pop();
- PrepareForCall(arg_count + 1, arg_count + 1); // Arguments + receiver.
- name.ToRegister(ecx);
- name.Unuse();
- return RawCallCodeObject(ic, mode);
-}
-
-
-Result VirtualFrame::CallKeyedCallIC(RelocInfo::Mode mode,
- int arg_count,
- int loop_nesting) {
- // Function name, arguments, and receiver are on top of the frame.
- // The IC expects the name in ecx and the rest on the stack and
- // drops them all.
- InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP;
- Handle<Code> ic =
- Isolate::Current()->stub_cache()->ComputeKeyedCallInitialize(arg_count,
- in_loop);
- // Spill args, receiver, and function. The call will drop args and
- // receiver.
- Result name = Pop();
- PrepareForCall(arg_count + 1, arg_count + 1); // Arguments + receiver.
- name.ToRegister(ecx);
- name.Unuse();
- return RawCallCodeObject(ic, mode);
-}
-
-
-Result VirtualFrame::CallConstructor(int arg_count) {
- // Arguments, receiver, and function are on top of the frame. The
- // IC expects arg count in eax, function in edi, and the arguments
- // and receiver on the stack.
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- Builtins::kJSConstructCall));
- // Duplicate the function before preparing the frame.
- PushElementAt(arg_count);
- Result function = Pop();
- PrepareForCall(arg_count + 1, arg_count + 1); // Spill function and args.
- function.ToRegister(edi);
-
- // Constructors are called with the number of arguments in register
- // eax for now. Another option would be to have separate construct
- // call trampolines per different arguments counts encountered.
- Result num_args = cgen()->allocator()->Allocate(eax);
- ASSERT(num_args.is_valid());
- __ Set(num_args.reg(), Immediate(arg_count));
-
- function.Unuse();
- num_args.Unuse();
- return RawCallCodeObject(ic, RelocInfo::CONSTRUCT_CALL);
-}
-
-
-void VirtualFrame::Drop(int count) {
- ASSERT(count >= 0);
- ASSERT(height() >= count);
- int num_virtual_elements = (element_count() - 1) - stack_pointer_;
-
- // Emit code to lower the stack pointer if necessary.
- if (num_virtual_elements < count) {
- int num_dropped = count - num_virtual_elements;
- stack_pointer_ -= num_dropped;
- __ add(Operand(esp), Immediate(num_dropped * kPointerSize));
- }
-
- // Discard elements from the virtual frame and free any registers.
- for (int i = 0; i < count; i++) {
- FrameElement dropped = elements_.RemoveLast();
- if (dropped.is_register()) {
- Unuse(dropped.reg());
- }
- }
-}
-
-
-Result VirtualFrame::Pop() {
- FrameElement element = elements_.RemoveLast();
- int index = element_count();
- ASSERT(element.is_valid());
- ASSERT(element.is_untagged_int32() == cgen()->in_safe_int32_mode());
-
- // Get number type information of the result.
- TypeInfo info;
- if (!element.is_copy()) {
- info = element.type_info();
- } else {
- info = elements_[element.index()].type_info();
- }
-
- bool pop_needed = (stack_pointer_ == index);
- if (pop_needed) {
- stack_pointer_--;
- if (element.is_memory()) {
- Result temp = cgen()->allocator()->Allocate();
- ASSERT(temp.is_valid());
- __ pop(temp.reg());
- temp.set_type_info(info);
- temp.set_untagged_int32(element.is_untagged_int32());
- return temp;
- }
-
- __ add(Operand(esp), Immediate(kPointerSize));
- }
- ASSERT(!element.is_memory());
-
- // The top element is a register, constant, or a copy. Unuse
- // registers and follow copies to their backing store.
- if (element.is_register()) {
- Unuse(element.reg());
- } else if (element.is_copy()) {
- ASSERT(!element.is_untagged_int32());
- ASSERT(element.index() < index);
- index = element.index();
- element = elements_[index];
- }
- ASSERT(!element.is_copy());
-
- // The element is memory, a register, or a constant.
- if (element.is_memory()) {
- // Memory elements could only be the backing store of a copy.
- // Allocate the original to a register.
- ASSERT(index <= stack_pointer_);
- ASSERT(!element.is_untagged_int32());
- Result temp = cgen()->allocator()->Allocate();
- ASSERT(temp.is_valid());
- Use(temp.reg(), index);
- FrameElement new_element =
- FrameElement::RegisterElement(temp.reg(),
- FrameElement::SYNCED,
- element.type_info());
- // Preserve the copy flag on the element.
- if (element.is_copied()) new_element.set_copied();
- elements_[index] = new_element;
- __ mov(temp.reg(), Operand(ebp, fp_relative(index)));
- return Result(temp.reg(), info);
- } else if (element.is_register()) {
- Result return_value(element.reg(), info);
- return_value.set_untagged_int32(element.is_untagged_int32());
- return return_value;
- } else {
- ASSERT(element.is_constant());
- Result return_value(element.handle());
- return_value.set_untagged_int32(element.is_untagged_int32());
- return return_value;
- }
-}
-
-
-void VirtualFrame::EmitPop(Register reg) {
- ASSERT(stack_pointer_ == element_count() - 1);
- stack_pointer_--;
- elements_.RemoveLast();
- __ pop(reg);
-}
-
-
-void VirtualFrame::EmitPop(Operand operand) {
- ASSERT(stack_pointer_ == element_count() - 1);
- stack_pointer_--;
- elements_.RemoveLast();
- __ pop(operand);
-}
-
-
-void VirtualFrame::EmitPush(Register reg, TypeInfo info) {
- ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement(info));
- stack_pointer_++;
- __ push(reg);
-}
-
-
-void VirtualFrame::EmitPush(Operand operand, TypeInfo info) {
- ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement(info));
- stack_pointer_++;
- __ push(operand);
-}
-
-
-void VirtualFrame::EmitPush(Immediate immediate, TypeInfo info) {
- ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement(info));
- stack_pointer_++;
- __ push(immediate);
-}
-
-
-void VirtualFrame::PushUntaggedElement(Handle<Object> value) {
- ASSERT(!ConstantPoolOverflowed());
- elements_.Add(FrameElement::ConstantElement(value, FrameElement::NOT_SYNCED));
- elements_[element_count() - 1].set_untagged_int32(true);
-}
-
-
-void VirtualFrame::Push(Expression* expr) {
- ASSERT(expr->IsTrivial());
-
- Literal* lit = expr->AsLiteral();
- if (lit != NULL) {
- Push(lit->handle());
- return;
- }
-
- VariableProxy* proxy = expr->AsVariableProxy();
- if (proxy != NULL) {
- Slot* slot = proxy->var()->AsSlot();
- if (slot->type() == Slot::LOCAL) {
- PushLocalAt(slot->index());
- return;
- }
- if (slot->type() == Slot::PARAMETER) {
- PushParameterAt(slot->index());
- return;
- }
- }
- UNREACHABLE();
-}
-
-
-void VirtualFrame::Push(Handle<Object> value) {
- if (ConstantPoolOverflowed()) {
- Result temp = cgen()->allocator()->Allocate();
- ASSERT(temp.is_valid());
- __ Set(temp.reg(), Immediate(value));
- Push(&temp);
- } else {
- FrameElement element =
- FrameElement::ConstantElement(value, FrameElement::NOT_SYNCED);
- elements_.Add(element);
- }
-}
-
-
-#undef __
-
-} } // namespace v8::internal
-
-#endif // V8_TARGET_ARCH_IA32
diff --git a/src/ia32/virtual-frame-ia32.h b/src/ia32/virtual-frame-ia32.h
deleted file mode 100644
index 504a8fc..0000000
--- a/src/ia32/virtual-frame-ia32.h
+++ /dev/null
@@ -1,650 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_IA32_VIRTUAL_FRAME_IA32_H_
-#define V8_IA32_VIRTUAL_FRAME_IA32_H_
-
-#include "codegen.h"
-#include "register-allocator.h"
-#include "scopes.h"
-#include "type-info.h"
-
-namespace v8 {
-namespace internal {
-
-// -------------------------------------------------------------------------
-// Virtual frames
-//
-// The virtual frame is an abstraction of the physical stack frame. It
-// encapsulates the parameters, frame-allocated locals, and the expression
-// stack. It supports push/pop operations on the expression stack, as well
-// as random access to the expression stack elements, locals, and
-// parameters.
-
-class VirtualFrame: public ZoneObject {
- public:
- // A utility class to introduce a scope where the virtual frame is
- // expected to remain spilled. The constructor spills the code
- // generator's current frame, but no attempt is made to require it
- // to stay spilled. It is intended as documentation while the code
- // generator is being transformed.
- class SpilledScope BASE_EMBEDDED {
- public:
- SpilledScope() : previous_state_(cgen()->in_spilled_code()) {
- ASSERT(cgen()->has_valid_frame());
- cgen()->frame()->SpillAll();
- cgen()->set_in_spilled_code(true);
- }
-
- ~SpilledScope() {
- cgen()->set_in_spilled_code(previous_state_);
- }
-
- private:
- bool previous_state_;
-
- CodeGenerator* cgen() {
- return CodeGeneratorScope::Current(Isolate::Current());
- }
- };
-
- // An illegal index into the virtual frame.
- static const int kIllegalIndex = -1;
-
- // Construct an initial virtual frame on entry to a JS function.
- inline VirtualFrame();
-
- // Construct a virtual frame as a clone of an existing one.
- explicit inline VirtualFrame(VirtualFrame* original);
-
- CodeGenerator* cgen() {
- return CodeGeneratorScope::Current(Isolate::Current());
- }
-
- MacroAssembler* masm() { return cgen()->masm(); }
-
- // Create a duplicate of an existing valid frame element.
- FrameElement CopyElementAt(int index,
- TypeInfo info = TypeInfo::Uninitialized());
-
- // The number of elements on the virtual frame.
- int element_count() { return elements_.length(); }
-
- // The height of the virtual expression stack.
- int height() { return element_count() - expression_base_index(); }
-
- int register_location(int num) {
- ASSERT(num >= 0 && num < RegisterAllocator::kNumRegisters);
- return register_locations_[num];
- }
-
- inline int register_location(Register reg);
-
- inline void set_register_location(Register reg, int index);
-
- bool is_used(int num) {
- ASSERT(num >= 0 && num < RegisterAllocator::kNumRegisters);
- return register_locations_[num] != kIllegalIndex;
- }
-
- inline bool is_used(Register reg);
-
- // Add extra in-memory elements to the top of the frame to match an actual
- // frame (eg, the frame after an exception handler is pushed). No code is
- // emitted.
- void Adjust(int count);
-
- // Forget count elements from the top of the frame all in-memory
- // (including synced) and adjust the stack pointer downward, to
- // match an external frame effect (examples include a call removing
- // its arguments, and exiting a try/catch removing an exception
- // handler). No code will be emitted.
- void Forget(int count) {
- ASSERT(count >= 0);
- ASSERT(stack_pointer_ == element_count() - 1);
- stack_pointer_ -= count;
- ForgetElements(count);
- }
-
- // Forget count elements from the top of the frame without adjusting
- // the stack pointer downward. This is used, for example, before
- // merging frames at break, continue, and return targets.
- void ForgetElements(int count);
-
- // Spill all values from the frame to memory.
- inline void SpillAll();
-
- // Spill all occurrences of a specific register from the frame.
- void Spill(Register reg) {
- if (is_used(reg)) SpillElementAt(register_location(reg));
- }
-
- // Make the two registers distinct and spill them. Returns the second
- // register. If the registers were not distinct then it returns the new
- // second register.
- Result MakeDistinctAndSpilled(Result* left, Result* right) {
- Spill(left->reg());
- Spill(right->reg());
- if (left->reg().is(right->reg())) {
- RegisterAllocator* allocator = cgen()->allocator();
- Result fresh = allocator->Allocate();
- ASSERT(fresh.is_valid());
- masm()->mov(fresh.reg(), right->reg());
- return fresh;
- }
- return *right;
- }
-
- // Spill all occurrences of an arbitrary register if possible. Return the
- // register spilled or no_reg if it was not possible to free any register
- // (ie, they all have frame-external references).
- Register SpillAnyRegister();
-
- // Spill the top element of the frame.
- void SpillTop() { SpillElementAt(element_count() - 1); }
-
- // Sync the range of elements in [begin, end] with memory.
- void SyncRange(int begin, int end);
-
- // Make this frame so that an arbitrary frame of the same height can
- // be merged to it. Copies and constants are removed from the frame.
- void MakeMergable();
-
- // Prepare this virtual frame for merging to an expected frame by
- // performing some state changes that do not require generating
- // code. It is guaranteed that no code will be generated.
- void PrepareMergeTo(VirtualFrame* expected);
-
- // Make this virtual frame have a state identical to an expected virtual
- // frame. As a side effect, code may be emitted to make this frame match
- // the expected one.
- void MergeTo(VirtualFrame* expected);
-
- // Detach a frame from its code generator, perhaps temporarily. This
- // tells the register allocator that it is free to use frame-internal
- // registers. Used when the code generator's frame is switched from this
- // one to NULL by an unconditional jump.
- void DetachFromCodeGenerator() {
- RegisterAllocator* cgen_allocator = cgen()->allocator();
- for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
- if (is_used(i)) cgen_allocator->Unuse(i);
- }
- }
-
- // (Re)attach a frame to its code generator. This informs the register
- // allocator that the frame-internal register references are active again.
- // Used when a code generator's frame is switched from NULL to this one by
- // binding a label.
- void AttachToCodeGenerator() {
- RegisterAllocator* cgen_allocator = cgen()->allocator();
- for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
- if (is_used(i)) cgen_allocator->Use(i);
- }
- }
-
- // Emit code for the physical JS entry and exit frame sequences. After
- // calling Enter, the virtual frame is ready for use; and after calling
- // Exit it should not be used. Note that Enter does not allocate space in
- // the physical frame for storing frame-allocated locals.
- void Enter();
- void Exit();
-
- // Prepare for returning from the frame by spilling locals. This
- // avoids generating unnecessary merge code when jumping to the
- // shared return site. Emits code for spills.
- inline void PrepareForReturn();
-
- // Number of local variables after when we use a loop for allocating.
- static const int kLocalVarBound = 10;
-
- // Allocate and initialize the frame-allocated locals.
- void AllocateStackSlots();
-
- // An element of the expression stack as an assembly operand.
- Operand ElementAt(int index) const {
- return Operand(esp, index * kPointerSize);
- }
-
- // Random-access store to a frame-top relative frame element. The result
- // becomes owned by the frame and is invalidated.
- void SetElementAt(int index, Result* value);
-
- // Set a frame element to a constant. The index is frame-top relative.
- inline void SetElementAt(int index, Handle<Object> value);
-
- void PushElementAt(int index) {
- PushFrameSlotAt(element_count() - index - 1);
- }
-
- void StoreToElementAt(int index) {
- StoreToFrameSlotAt(element_count() - index - 1);
- }
-
- // A frame-allocated local as an assembly operand.
- Operand LocalAt(int index) {
- ASSERT(0 <= index);
- ASSERT(index < local_count());
- return Operand(ebp, kLocal0Offset - index * kPointerSize);
- }
-
- // Push a copy of the value of a local frame slot on top of the frame.
- void PushLocalAt(int index) {
- PushFrameSlotAt(local0_index() + index);
- }
-
- // Push a copy of the value of a local frame slot on top of the frame.
- void UntaggedPushLocalAt(int index) {
- UntaggedPushFrameSlotAt(local0_index() + index);
- }
-
- // Push the value of a local frame slot on top of the frame and invalidate
- // the local slot. The slot should be written to before trying to read
- // from it again.
- void TakeLocalAt(int index) {
- TakeFrameSlotAt(local0_index() + index);
- }
-
- // Store the top value on the virtual frame into a local frame slot. The
- // value is left in place on top of the frame.
- void StoreToLocalAt(int index) {
- StoreToFrameSlotAt(local0_index() + index);
- }
-
- // Push the address of the receiver slot on the frame.
- void PushReceiverSlotAddress();
-
- // Push the function on top of the frame.
- void PushFunction() {
- PushFrameSlotAt(function_index());
- }
-
- // Save the value of the esi register to the context frame slot.
- void SaveContextRegister();
-
- // Restore the esi register from the value of the context frame
- // slot.
- void RestoreContextRegister();
-
- // A parameter as an assembly operand.
- Operand ParameterAt(int index) {
- ASSERT(-1 <= index); // -1 is the receiver.
- ASSERT(index < parameter_count());
- return Operand(ebp, (1 + parameter_count() - index) * kPointerSize);
- }
-
- // Push a copy of the value of a parameter frame slot on top of the frame.
- void PushParameterAt(int index) {
- PushFrameSlotAt(param0_index() + index);
- }
-
- // Push a copy of the value of a parameter frame slot on top of the frame.
- void UntaggedPushParameterAt(int index) {
- UntaggedPushFrameSlotAt(param0_index() + index);
- }
-
- // Push the value of a paramter frame slot on top of the frame and
- // invalidate the parameter slot. The slot should be written to before
- // trying to read from it again.
- void TakeParameterAt(int index) {
- TakeFrameSlotAt(param0_index() + index);
- }
-
- // Store the top value on the virtual frame into a parameter frame slot.
- // The value is left in place on top of the frame.
- void StoreToParameterAt(int index) {
- StoreToFrameSlotAt(param0_index() + index);
- }
-
- // The receiver frame slot.
- Operand Receiver() {
- return ParameterAt(-1);
- }
-
- // Push a try-catch or try-finally handler on top of the virtual frame.
- void PushTryHandler(HandlerType type);
-
- // Call stub given the number of arguments it expects on (and
- // removes from) the stack.
- inline Result CallStub(CodeStub* stub, int arg_count);
-
- // Call stub that takes a single argument passed in eax. The
- // argument is given as a result which does not have to be eax or
- // even a register. The argument is consumed by the call.
- Result CallStub(CodeStub* stub, Result* arg);
-
- // Call stub that takes a pair of arguments passed in edx (arg0) and
- // eax (arg1). The arguments are given as results which do not have
- // to be in the proper registers or even in registers. The
- // arguments are consumed by the call.
- Result CallStub(CodeStub* stub, Result* arg0, Result* arg1);
-
- // Call JS function from top of the stack with arguments
- // taken from the stack.
- Result CallJSFunction(int arg_count);
-
- // Call runtime given the number of arguments expected on (and
- // removed from) the stack.
- Result CallRuntime(const Runtime::Function* f, int arg_count);
- Result CallRuntime(Runtime::FunctionId id, int arg_count);
-
-#ifdef ENABLE_DEBUGGER_SUPPORT
- void DebugBreak();
-#endif
-
- // Invoke builtin given the number of arguments it expects on (and
- // removes from) the stack.
- Result InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag, int arg_count);
-
- // Call load IC. Name and receiver are found on top of the frame.
- // Both are dropped.
- Result CallLoadIC(RelocInfo::Mode mode);
-
- // Call keyed load IC. Key and receiver are found on top of the
- // frame. Both are dropped.
- Result CallKeyedLoadIC(RelocInfo::Mode mode);
-
- // Call store IC. If the load is contextual, value is found on top of the
- // frame. If not, value and receiver are on the frame. Both are dropped.
- Result CallStoreIC(Handle<String> name, bool is_contextual,
- StrictModeFlag strict_mode);
-
- // Call keyed store IC. Value, key, and receiver are found on top
- // of the frame. All three are dropped.
- Result CallKeyedStoreIC(StrictModeFlag strict_mode);
-
- // Call call IC. Function name, arguments, and receiver are found on top
- // of the frame and dropped by the call. The argument count does not
- // include the receiver.
- Result CallCallIC(RelocInfo::Mode mode, int arg_count, int loop_nesting);
-
- // Call keyed call IC. Same calling convention as CallCallIC.
- Result CallKeyedCallIC(RelocInfo::Mode mode, int arg_count, int loop_nesting);
-
- // Allocate and call JS function as constructor. Arguments,
- // receiver (global object), and function are found on top of the
- // frame. Function is not dropped. The argument count does not
- // include the receiver.
- Result CallConstructor(int arg_count);
-
- // Drop a number of elements from the top of the expression stack. May
- // emit code to affect the physical frame. Does not clobber any registers
- // excepting possibly the stack pointer.
- void Drop(int count);
-
- // Drop one element.
- void Drop() {
- Drop(1);
- }
-
- // Duplicate the top element of the frame.
- void Dup() {
- PushFrameSlotAt(element_count() - 1);
- }
-
- // Pop an element from the top of the expression stack. Returns a
- // Result, which may be a constant or a register.
- Result Pop();
-
- // Pop and save an element from the top of the expression stack and
- // emit a corresponding pop instruction.
- void EmitPop(Register reg);
- void EmitPop(Operand operand);
-
- // Push an element on top of the expression stack and emit a
- // corresponding push instruction.
- void EmitPush(Register reg,
- TypeInfo info = TypeInfo::Unknown());
- void EmitPush(Operand operand,
- TypeInfo info = TypeInfo::Unknown());
- void EmitPush(Immediate immediate,
- TypeInfo info = TypeInfo::Unknown());
-
- inline bool ConstantPoolOverflowed();
-
- // Push an element on the virtual frame.
- void Push(Handle<Object> value);
- inline void Push(Register reg, TypeInfo info = TypeInfo::Unknown());
- inline void Push(Smi* value);
-
- void PushUntaggedElement(Handle<Object> value);
-
- // Pushing a result invalidates it (its contents become owned by the
- // frame).
- void Push(Result* result) {
- // This assert will trigger if you try to push the same value twice.
- ASSERT(result->is_valid());
- if (result->is_register()) {
- Push(result->reg(), result->type_info());
- } else {
- ASSERT(result->is_constant());
- Push(result->handle());
- }
- if (cgen()->in_safe_int32_mode()) {
- ASSERT(result->is_untagged_int32());
- elements_[element_count() - 1].set_untagged_int32(true);
- }
- result->Unuse();
- }
-
- // Pushing an expression expects that the expression is trivial (according
- // to Expression::IsTrivial).
- void Push(Expression* expr);
-
- // Nip removes zero or more elements from immediately below the top
- // of the frame, leaving the previous top-of-frame value on top of
- // the frame. Nip(k) is equivalent to x = Pop(), Drop(k), Push(x).
- inline void Nip(int num_dropped);
-
- // Check that the frame has no elements containing untagged int32 elements.
- bool HasNoUntaggedInt32Elements() {
- for (int i = 0; i < element_count(); ++i) {
- if (elements_[i].is_untagged_int32()) return false;
- }
- return true;
- }
-
- // Update the type information of a variable frame element directly.
- inline void SetTypeForLocalAt(int index, TypeInfo info);
- inline void SetTypeForParamAt(int index, TypeInfo info);
-
- private:
- static const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset;
- static const int kFunctionOffset = JavaScriptFrameConstants::kFunctionOffset;
- static const int kContextOffset = StandardFrameConstants::kContextOffset;
-
- static const int kHandlerSize = StackHandlerConstants::kSize / kPointerSize;
- static const int kPreallocatedElements = 5 + 8; // 8 expression stack slots.
-
- ZoneList<FrameElement> elements_;
-
- // The index of the element that is at the processor's stack pointer
- // (the esp register).
- int stack_pointer_;
-
- // The index of the register frame element using each register, or
- // kIllegalIndex if a register is not on the frame.
- int register_locations_[RegisterAllocator::kNumRegisters];
-
- // The number of frame-allocated locals and parameters respectively.
- inline int parameter_count();
-
- inline int local_count();
-
- // The index of the element that is at the processor's frame pointer
- // (the ebp register). The parameters, receiver, and return address
- // are below the frame pointer.
- int frame_pointer() {
- return parameter_count() + 2;
- }
-
- // The index of the first parameter. The receiver lies below the first
- // parameter.
- int param0_index() {
- return 1;
- }
-
- // The index of the context slot in the frame. It is immediately
- // above the frame pointer.
- int context_index() {
- return frame_pointer() + 1;
- }
-
- // The index of the function slot in the frame. It is above the frame
- // pointer and the context slot.
- int function_index() {
- return frame_pointer() + 2;
- }
-
- // The index of the first local. Between the frame pointer and the
- // locals lie the context and the function.
- int local0_index() {
- return frame_pointer() + 3;
- }
-
- // The index of the base of the expression stack.
- int expression_base_index() {
- return local0_index() + local_count();
- }
-
- // Convert a frame index into a frame pointer relative offset into the
- // actual stack.
- int fp_relative(int index) {
- ASSERT(index < element_count());
- ASSERT(frame_pointer() < element_count()); // FP is on the frame.
- return (frame_pointer() - index) * kPointerSize;
- }
-
- // Record an occurrence of a register in the virtual frame. This has the
- // effect of incrementing the register's external reference count and
- // of updating the index of the register's location in the frame.
- void Use(Register reg, int index) {
- ASSERT(!is_used(reg));
- set_register_location(reg, index);
- cgen()->allocator()->Use(reg);
- }
-
- // Record that a register reference has been dropped from the frame. This
- // decrements the register's external reference count and invalidates the
- // index of the register's location in the frame.
- void Unuse(Register reg) {
- ASSERT(is_used(reg));
- set_register_location(reg, kIllegalIndex);
- cgen()->allocator()->Unuse(reg);
- }
-
- // Spill the element at a particular index---write it to memory if
- // necessary, free any associated register, and forget its value if
- // constant.
- void SpillElementAt(int index);
-
- // Sync the element at a particular index. If it is a register or
- // constant that disagrees with the value on the stack, write it to memory.
- // Keep the element type as register or constant, and clear the dirty bit.
- void SyncElementAt(int index);
-
- // Sync a single unsynced element that lies beneath or at the stack pointer.
- void SyncElementBelowStackPointer(int index);
-
- // Sync a single unsynced element that lies just above the stack pointer.
- void SyncElementByPushing(int index);
-
- // Push a copy of a frame slot (typically a local or parameter) on top of
- // the frame.
- inline void PushFrameSlotAt(int index);
-
- // Push a copy of a frame slot (typically a local or parameter) on top of
- // the frame, at an untagged int32 value. Bails out if the value is not
- // an int32.
- void UntaggedPushFrameSlotAt(int index);
-
- // Push a the value of a frame slot (typically a local or parameter) on
- // top of the frame and invalidate the slot.
- void TakeFrameSlotAt(int index);
-
- // Store the value on top of the frame to a frame slot (typically a local
- // or parameter).
- void StoreToFrameSlotAt(int index);
-
- // Spill all elements in registers. Spill the top spilled_args elements
- // on the frame. Sync all other frame elements.
- // Then drop dropped_args elements from the virtual frame, to match
- // the effect of an upcoming call that will drop them from the stack.
- void PrepareForCall(int spilled_args, int dropped_args);
-
- // Move frame elements currently in registers or constants, that
- // should be in memory in the expected frame, to memory.
- void MergeMoveRegistersToMemory(VirtualFrame* expected);
-
- // Make the register-to-register moves necessary to
- // merge this frame with the expected frame.
- // Register to memory moves must already have been made,
- // and memory to register moves must follow this call.
- // This is because some new memory-to-register moves are
- // created in order to break cycles of register moves.
- // Used in the implementation of MergeTo().
- void MergeMoveRegistersToRegisters(VirtualFrame* expected);
-
- // Make the memory-to-register and constant-to-register moves
- // needed to make this frame equal the expected frame.
- // Called after all register-to-memory and register-to-register
- // moves have been made. After this function returns, the frames
- // should be equal.
- void MergeMoveMemoryToRegisters(VirtualFrame* expected);
-
- // Invalidates a frame slot (puts an invalid frame element in it).
- // Copies on the frame are correctly handled, and if this slot was
- // the backing store of copies, the index of the new backing store
- // is returned. Otherwise, returns kIllegalIndex.
- // Register counts are correctly updated.
- int InvalidateFrameSlotAt(int index);
-
- // This function assumes that a and b are the only results that could be in
- // the registers a_reg or b_reg. Other results can be live, but must not
- // be in the registers a_reg or b_reg. The results a and b are invalidated.
- void MoveResultsToRegisters(Result* a,
- Result* b,
- Register a_reg,
- Register b_reg);
-
- // Call a code stub that has already been prepared for calling (via
- // PrepareForCall).
- Result RawCallStub(CodeStub* stub);
-
- // Calls a code object which has already been prepared for calling
- // (via PrepareForCall).
- Result RawCallCodeObject(Handle<Code> code, RelocInfo::Mode rmode);
-
- inline bool Equals(VirtualFrame* other);
-
- // Classes that need raw access to the elements_ array.
- friend class FrameRegisterState;
- friend class JumpTarget;
-};
-
-} } // namespace v8::internal
-
-#endif // V8_IA32_VIRTUAL_FRAME_IA32_H_
diff --git a/src/ic.cc b/src/ic.cc
index 382b438..2299922 100644
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -282,7 +282,6 @@
return KeyedStoreIC::Clear(address, target);
case Code::CALL_IC: return CallIC::Clear(address, target);
case Code::KEYED_CALL_IC: return KeyedCallIC::Clear(address, target);
- case Code::BINARY_OP_IC:
case Code::TYPE_RECORDING_BINARY_OP_IC:
case Code::COMPARE_IC:
// Clearing these is tricky and does not
@@ -305,54 +304,23 @@
}
-void KeyedLoadIC::ClearInlinedVersion(Address address) {
- // Insert null as the map to check for to make sure the map check fails
- // sending control flow to the IC instead of the inlined version.
- PatchInlinedLoad(address, HEAP->null_value());
-}
-
-
void KeyedLoadIC::Clear(Address address, Code* target) {
if (target->ic_state() == UNINITIALIZED) return;
// Make sure to also clear the map used in inline fast cases. If we
// do not clear these maps, cached code can keep objects alive
// through the embedded maps.
- ClearInlinedVersion(address);
SetTargetAtAddress(address, initialize_stub());
}
-void LoadIC::ClearInlinedVersion(Address address) {
- // Reset the map check of the inlined inobject property load (if
- // present) to guarantee failure by holding an invalid map (the null
- // value). The offset can be patched to anything.
- Heap* heap = HEAP;
- PatchInlinedLoad(address, heap->null_value(), 0);
- PatchInlinedContextualLoad(address,
- heap->null_value(),
- heap->null_value(),
- true);
-}
-
-
void LoadIC::Clear(Address address, Code* target) {
if (target->ic_state() == UNINITIALIZED) return;
- ClearInlinedVersion(address);
SetTargetAtAddress(address, initialize_stub());
}
-void StoreIC::ClearInlinedVersion(Address address) {
- // Reset the map check of the inlined inobject property store (if
- // present) to guarantee failure by holding an invalid map (the null
- // value). The offset can be patched to anything.
- PatchInlinedStore(address, HEAP->null_value(), 0);
-}
-
-
void StoreIC::Clear(Address address, Code* target) {
if (target->ic_state() == UNINITIALIZED) return;
- ClearInlinedVersion(address);
SetTargetAtAddress(address,
(target->extra_ic_state() == kStrictMode)
? initialize_stub_strict()
@@ -360,21 +328,6 @@
}
-void KeyedStoreIC::ClearInlinedVersion(Address address) {
- // Insert null as the elements map to check for. This will make
- // sure that the elements fast-case map check fails so that control
- // flows to the IC instead of the inlined version.
- PatchInlinedStore(address, HEAP->null_value());
-}
-
-
-void KeyedStoreIC::RestoreInlinedVersion(Address address) {
- // Restore the fast-case elements map check so that the inlined
- // version can be used again.
- PatchInlinedStore(address, HEAP->fixed_array_map());
-}
-
-
void KeyedStoreIC::Clear(Address address, Code* target) {
if (target->ic_state() == UNINITIALIZED) return;
SetTargetAtAddress(address,
@@ -874,9 +827,6 @@
#endif
if (state == PREMONOMORPHIC) {
if (object->IsString()) {
- Map* map = HeapObject::cast(*object)->map();
- const int offset = String::kLengthOffset;
- PatchInlinedLoad(address(), map, offset);
set_target(isolate()->builtins()->builtin(
Builtins::kLoadIC_StringLength));
} else {
@@ -904,9 +854,6 @@
if (FLAG_trace_ic) PrintF("[LoadIC : +#length /array]\n");
#endif
if (state == PREMONOMORPHIC) {
- Map* map = HeapObject::cast(*object)->map();
- const int offset = JSArray::kLengthOffset;
- PatchInlinedLoad(address(), map, offset);
set_target(isolate()->builtins()->builtin(
Builtins::kLoadIC_ArrayLength));
} else {
@@ -949,63 +896,6 @@
LOG(isolate(), SuspectReadEvent(*name, *object));
}
- bool can_be_inlined_precheck =
- FLAG_use_ic &&
- lookup.IsProperty() &&
- lookup.IsCacheable() &&
- lookup.holder() == *object &&
- !object->IsAccessCheckNeeded();
-
- bool can_be_inlined =
- can_be_inlined_precheck &&
- state == PREMONOMORPHIC &&
- lookup.type() == FIELD;
-
- bool can_be_inlined_contextual =
- can_be_inlined_precheck &&
- state == UNINITIALIZED &&
- lookup.holder()->IsGlobalObject() &&
- lookup.type() == NORMAL;
-
- if (can_be_inlined) {
- Map* map = lookup.holder()->map();
- // Property's index in the properties array. If negative we have
- // an inobject property.
- int index = lookup.GetFieldIndex() - map->inobject_properties();
- if (index < 0) {
- // Index is an offset from the end of the object.
- int offset = map->instance_size() + (index * kPointerSize);
- if (PatchInlinedLoad(address(), map, offset)) {
- set_target(megamorphic_stub());
- TRACE_IC_NAMED("[LoadIC : inline patch %s]\n", name);
- return lookup.holder()->FastPropertyAt(lookup.GetFieldIndex());
- } else {
- TRACE_IC_NAMED("[LoadIC : no inline patch %s (patching failed)]\n",
- name);
- }
- } else {
- TRACE_IC_NAMED("[LoadIC : no inline patch %s (not inobject)]\n", name);
- }
- } else if (can_be_inlined_contextual) {
- Map* map = lookup.holder()->map();
- JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(
- lookup.holder()->property_dictionary()->ValueAt(
- lookup.GetDictionaryEntry()));
- if (PatchInlinedContextualLoad(address(),
- map,
- cell,
- lookup.IsDontDelete())) {
- set_target(megamorphic_stub());
- TRACE_IC_NAMED("[LoadIC : inline contextual patch %s]\n", name);
- ASSERT(cell->value() != isolate()->heap()->the_hole_value());
- return cell->value();
- }
- } else {
- if (FLAG_use_ic && state == PREMONOMORPHIC) {
- TRACE_IC_NAMED("[LoadIC : no inline patch %s (not inlinable)]\n", name);
- }
- }
-
// Update inline cache and stub cache.
if (FLAG_use_ic) {
UpdateCaches(&lookup, state, object, name);
@@ -1143,6 +1033,16 @@
MaybeObject* KeyedLoadIC::Load(State state,
Handle<Object> object,
Handle<Object> key) {
+ // Check for values that can be converted into a symbol.
+ // TODO(1295): Remove this code.
+ HandleScope scope(isolate());
+ if (key->IsHeapNumber() &&
+ isnan(HeapNumber::cast(*key)->value())) {
+ key = isolate()->factory()->nan_symbol();
+ } else if (key->IsUndefined()) {
+ key = isolate()->factory()->undefined_symbol();
+ }
+
if (key->IsSymbol()) {
Handle<String> name = Handle<String>::cast(key);
@@ -1285,18 +1185,6 @@
#ifdef DEBUG
TraceIC("KeyedLoadIC", key, state, target());
#endif // DEBUG
-
- // For JSObjects with fast elements that are not value wrappers
- // and that do not have indexed interceptors, we initialize the
- // inlined fast case (if present) by patching the inlined map
- // check.
- if (object->IsJSObject() &&
- !object->IsJSValue() &&
- !JSObject::cast(*object)->HasIndexedInterceptor() &&
- JSObject::cast(*object)->HasFastElements()) {
- Map* map = JSObject::cast(*object)->map();
- PatchInlinedLoad(address(), map);
- }
}
// Get the property.
@@ -1462,57 +1350,7 @@
LookupResult lookup;
if (LookupForWrite(*receiver, *name, &lookup)) {
- bool can_be_inlined =
- state == UNINITIALIZED &&
- lookup.IsProperty() &&
- lookup.holder() == *receiver &&
- lookup.type() == FIELD &&
- !receiver->IsAccessCheckNeeded();
-
- if (can_be_inlined) {
- Map* map = lookup.holder()->map();
- // Property's index in the properties array. If negative we have
- // an inobject property.
- int index = lookup.GetFieldIndex() - map->inobject_properties();
- if (index < 0) {
- // Index is an offset from the end of the object.
- int offset = map->instance_size() + (index * kPointerSize);
- if (PatchInlinedStore(address(), map, offset)) {
- set_target((strict_mode == kStrictMode)
- ? megamorphic_stub_strict()
- : megamorphic_stub());
-#ifdef DEBUG
- if (FLAG_trace_ic) {
- PrintF("[StoreIC : inline patch %s]\n", *name->ToCString());
- }
-#endif
- return receiver->SetProperty(*name, *value, NONE, strict_mode);
-#ifdef DEBUG
-
- } else {
- if (FLAG_trace_ic) {
- PrintF("[StoreIC : no inline patch %s (patching failed)]\n",
- *name->ToCString());
- }
- }
- } else {
- if (FLAG_trace_ic) {
- PrintF("[StoreIC : no inline patch %s (not inobject)]\n",
- *name->ToCString());
- }
- }
- } else {
- if (state == PREMONOMORPHIC) {
- if (FLAG_trace_ic) {
- PrintF("[StoreIC : no inline patch %s (not inlinable)]\n",
- *name->ToCString());
-#endif
- }
- }
- }
-
- // If no inlined store ic was patched, generate a stub for this
- // store.
+ // Generate a stub for this store.
UpdateCaches(&lookup, state, strict_mode, receiver, name, value);
} else {
// Strict mode doesn't allow setting non-existent global property
@@ -1815,8 +1653,7 @@
// Used from ic-<arch>.cc.
-MUST_USE_RESULT MaybeObject* CallIC_Miss(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, CallIC_Miss) {
NoHandleAllocation na;
ASSERT(args.length() == 2);
CallIC ic(isolate);
@@ -1846,8 +1683,7 @@
// Used from ic-<arch>.cc.
-MUST_USE_RESULT MaybeObject* KeyedCallIC_Miss(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, KeyedCallIC_Miss) {
NoHandleAllocation na;
ASSERT(args.length() == 2);
KeyedCallIC ic(isolate);
@@ -1868,8 +1704,7 @@
// Used from ic-<arch>.cc.
-MUST_USE_RESULT MaybeObject* LoadIC_Miss(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, LoadIC_Miss) {
NoHandleAllocation na;
ASSERT(args.length() == 2);
LoadIC ic(isolate);
@@ -1879,8 +1714,7 @@
// Used from ic-<arch>.cc
-MUST_USE_RESULT MaybeObject* KeyedLoadIC_Miss(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, KeyedLoadIC_Miss) {
NoHandleAllocation na;
ASSERT(args.length() == 2);
KeyedLoadIC ic(isolate);
@@ -1890,8 +1724,7 @@
// Used from ic-<arch>.cc.
-MUST_USE_RESULT MaybeObject* StoreIC_Miss(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, StoreIC_Miss) {
NoHandleAllocation na;
ASSERT(args.length() == 3);
StoreIC ic(isolate);
@@ -1905,8 +1738,7 @@
}
-MUST_USE_RESULT MaybeObject* StoreIC_ArrayLength(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, StoreIC_ArrayLength) {
NoHandleAllocation nha;
ASSERT(args.length() == 2);
@@ -1927,9 +1759,7 @@
// Extend storage is called in a store inline cache when
// it is necessary to extend the properties array of a
// JSObject.
-MUST_USE_RESULT MaybeObject* SharedStoreIC_ExtendStorage(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, SharedStoreIC_ExtendStorage) {
NoHandleAllocation na;
ASSERT(args.length() == 3);
@@ -1963,8 +1793,7 @@
// Used from ic-<arch>.cc.
-MUST_USE_RESULT MaybeObject* KeyedStoreIC_Miss(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_Miss) {
NoHandleAllocation na;
ASSERT(args.length() == 3);
KeyedStoreIC ic(isolate);
@@ -1978,148 +1807,6 @@
}
-void BinaryOpIC::patch(Code* code) {
- set_target(code);
-}
-
-
-const char* BinaryOpIC::GetName(TypeInfo type_info) {
- switch (type_info) {
- case UNINIT_OR_SMI: return "UninitOrSmi";
- case DEFAULT: return "Default";
- case GENERIC: return "Generic";
- case HEAP_NUMBERS: return "HeapNumbers";
- case STRINGS: return "Strings";
- default: return "Invalid";
- }
-}
-
-
-BinaryOpIC::State BinaryOpIC::ToState(TypeInfo type_info) {
- switch (type_info) {
- case UNINIT_OR_SMI:
- return UNINITIALIZED;
- case DEFAULT:
- case HEAP_NUMBERS:
- case STRINGS:
- return MONOMORPHIC;
- case GENERIC:
- return MEGAMORPHIC;
- }
- UNREACHABLE();
- return UNINITIALIZED;
-}
-
-
-BinaryOpIC::TypeInfo BinaryOpIC::GetTypeInfo(Object* left,
- Object* right) {
- if (left->IsSmi() && right->IsSmi()) {
- // If we have two smi inputs we can reach here because
- // of an overflow. Enter default state.
- return DEFAULT;
- }
-
- if (left->IsNumber() && right->IsNumber()) {
- return HEAP_NUMBERS;
- }
-
- if (left->IsString() || right->IsString()) {
- // Patching for fast string ADD makes sense even if only one of the
- // arguments is a string.
- return STRINGS;
- }
-
- return GENERIC;
-}
-
-
-// defined in code-stubs-<arch>.cc
-Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info);
-
-
-MUST_USE_RESULT MaybeObject* BinaryOp_Patch(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
- ASSERT(args.length() == 5);
-
- HandleScope scope(isolate);
- Handle<Object> left = args.at<Object>(0);
- Handle<Object> right = args.at<Object>(1);
- int key = Smi::cast(args[2])->value();
- Token::Value op = static_cast<Token::Value>(Smi::cast(args[3])->value());
- BinaryOpIC::TypeInfo previous_type =
- static_cast<BinaryOpIC::TypeInfo>(Smi::cast(args[4])->value());
-
- BinaryOpIC::TypeInfo type = BinaryOpIC::GetTypeInfo(*left, *right);
- Handle<Code> code = GetBinaryOpStub(key, type);
- if (!code.is_null()) {
- BinaryOpIC ic(isolate);
- ic.patch(*code);
- if (FLAG_trace_ic) {
- PrintF("[BinaryOpIC (%s->%s)#%s]\n",
- BinaryOpIC::GetName(previous_type),
- BinaryOpIC::GetName(type),
- Token::Name(op));
- }
- }
-
- Handle<JSBuiltinsObject> builtins = Handle<JSBuiltinsObject>(
- isolate->thread_local_top()->context_->builtins(), isolate);
- Object* builtin = NULL; // Initialization calms down the compiler.
- switch (op) {
- case Token::ADD:
- builtin = builtins->javascript_builtin(Builtins::ADD);
- break;
- case Token::SUB:
- builtin = builtins->javascript_builtin(Builtins::SUB);
- break;
- case Token::MUL:
- builtin = builtins->javascript_builtin(Builtins::MUL);
- break;
- case Token::DIV:
- builtin = builtins->javascript_builtin(Builtins::DIV);
- break;
- case Token::MOD:
- builtin = builtins->javascript_builtin(Builtins::MOD);
- break;
- case Token::BIT_AND:
- builtin = builtins->javascript_builtin(Builtins::BIT_AND);
- break;
- case Token::BIT_OR:
- builtin = builtins->javascript_builtin(Builtins::BIT_OR);
- break;
- case Token::BIT_XOR:
- builtin = builtins->javascript_builtin(Builtins::BIT_XOR);
- break;
- case Token::SHR:
- builtin = builtins->javascript_builtin(Builtins::SHR);
- break;
- case Token::SAR:
- builtin = builtins->javascript_builtin(Builtins::SAR);
- break;
- case Token::SHL:
- builtin = builtins->javascript_builtin(Builtins::SHL);
- break;
- default:
- UNREACHABLE();
- }
-
- Handle<JSFunction> builtin_function(JSFunction::cast(builtin),
- isolate);
-
- bool caught_exception;
- Object** builtin_args[] = { right.location() };
- Handle<Object> result = Execution::Call(builtin_function,
- left,
- ARRAY_SIZE(builtin_args),
- builtin_args,
- &caught_exception);
- if (caught_exception) {
- return Failure::Exception();
- }
- return *result;
-}
-
-
void TRBinaryOpIC::patch(Code* code) {
set_target(code);
}
@@ -2132,6 +1819,7 @@
case INT32: return "Int32s";
case HEAP_NUMBER: return "HeapNumbers";
case ODDBALL: return "Oddball";
+ case BOTH_STRING: return "BothStrings";
case STRING: return "Strings";
case GENERIC: return "Generic";
default: return "Invalid";
@@ -2147,6 +1835,7 @@
case INT32:
case HEAP_NUMBER:
case ODDBALL:
+ case BOTH_STRING:
case STRING:
return MONOMORPHIC;
case GENERIC:
@@ -2161,12 +1850,17 @@
TRBinaryOpIC::TypeInfo y) {
if (x == UNINITIALIZED) return y;
if (y == UNINITIALIZED) return x;
- if (x == STRING && y == STRING) return STRING;
- if (x == STRING || y == STRING) return GENERIC;
- if (x >= y) return x;
+ if (x == y) return x;
+ if (x == BOTH_STRING && y == STRING) return STRING;
+ if (x == STRING && y == BOTH_STRING) return STRING;
+ if (x == STRING || x == BOTH_STRING || y == STRING || y == BOTH_STRING) {
+ return GENERIC;
+ }
+ if (x > y) return x;
return y;
}
+
TRBinaryOpIC::TypeInfo TRBinaryOpIC::GetTypeInfo(Handle<Object> left,
Handle<Object> right) {
::v8::internal::TypeInfo left_type =
@@ -2188,9 +1882,11 @@
return HEAP_NUMBER;
}
- if (left_type.IsString() || right_type.IsString()) {
- // Patching for fast string ADD makes sense even if only one of the
- // arguments is a string.
+ // Patching for fast string ADD makes sense even if only one of the
+ // arguments is a string.
+ if (left_type.IsString()) {
+ return right_type.IsString() ? BOTH_STRING : STRING;
+ } else if (right_type.IsString()) {
return STRING;
}
@@ -2209,8 +1905,7 @@
TRBinaryOpIC::TypeInfo result_type);
-MaybeObject* TypeRecordingBinaryOp_Patch(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, TypeRecordingBinaryOp_Patch) {
ASSERT(args.length() == 5);
HandleScope scope(isolate);
@@ -2224,11 +1919,11 @@
TRBinaryOpIC::TypeInfo type = TRBinaryOpIC::GetTypeInfo(left, right);
type = TRBinaryOpIC::JoinTypes(type, previous_type);
TRBinaryOpIC::TypeInfo result_type = TRBinaryOpIC::UNINITIALIZED;
- if (type == TRBinaryOpIC::STRING && op != Token::ADD) {
+ if ((type == TRBinaryOpIC::STRING || type == TRBinaryOpIC::BOTH_STRING) &&
+ op != Token::ADD) {
type = TRBinaryOpIC::GENERIC;
}
- if (type == TRBinaryOpIC::SMI &&
- previous_type == TRBinaryOpIC::SMI) {
+ if (type == TRBinaryOpIC::SMI && previous_type == TRBinaryOpIC::SMI) {
if (op == Token::DIV || op == Token::MUL || kSmiValueSize == 32) {
// Arithmetic on two Smi inputs has yielded a heap number.
// That is the only way to get here from the Smi stub.
@@ -2240,8 +1935,7 @@
result_type = TRBinaryOpIC::INT32;
}
}
- if (type == TRBinaryOpIC::INT32 &&
- previous_type == TRBinaryOpIC::INT32) {
+ if (type == TRBinaryOpIC::INT32 && previous_type == TRBinaryOpIC::INT32) {
// We must be here because an operation on two INT32 types overflowed.
result_type = TRBinaryOpIC::HEAP_NUMBER;
}
@@ -2365,8 +2059,7 @@
// Used from ic_<arch>.cc.
-Code* CompareIC_Miss(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(Code*, CompareIC_Miss) {
NoHandleAllocation na;
ASSERT(args.length() == 3);
CompareIC ic(isolate, static_cast<Token::Value>(Smi::cast(args[2])->value()));
diff --git a/src/ic.h b/src/ic.h
index bb8a981..7b7ab43 100644
--- a/src/ic.h
+++ b/src/ic.h
@@ -53,7 +53,6 @@
ICU(LoadPropertyWithInterceptorForCall) \
ICU(KeyedLoadPropertyWithInterceptor) \
ICU(StoreInterceptorProperty) \
- ICU(BinaryOp_Patch) \
ICU(TypeRecordingBinaryOp_Patch) \
ICU(CompareIC_Miss)
//
@@ -297,14 +296,6 @@
bool support_wrappers);
static void GenerateFunctionPrototype(MacroAssembler* masm);
- // Clear the use of the inlined version.
- static void ClearInlinedVersion(Address address);
-
- // The offset from the inlined patch site to the start of the
- // inlined load instruction. It is architecture-dependent, and not
- // used on ARM.
- static const int kOffsetToLoadInstruction;
-
private:
// Update the inline cache and the global stub cache based on the
// lookup result.
@@ -329,13 +320,6 @@
static void Clear(Address address, Code* target);
- static bool PatchInlinedLoad(Address address, Object* map, int index);
-
- static bool PatchInlinedContextualLoad(Address address,
- Object* map,
- Object* cell,
- bool is_dont_delete);
-
friend class IC;
};
@@ -362,9 +346,6 @@
static void GenerateIndexedInterceptor(MacroAssembler* masm);
- // Clear the use of the inlined version.
- static void ClearInlinedVersion(Address address);
-
// Bit mask to be tested against bit field for the cases when
// generic stub should go into slow case.
// Access check is necessary explicitly since generic stub does not perform
@@ -408,10 +389,6 @@
static void Clear(Address address, Code* target);
- // Support for patching the map that is checked in an inlined
- // version of keyed load.
- static bool PatchInlinedLoad(Address address, Object* map);
-
friend class IC;
};
@@ -438,13 +415,6 @@
static void GenerateGlobalProxy(MacroAssembler* masm,
StrictModeFlag strict_mode);
- // Clear the use of an inlined version.
- static void ClearInlinedVersion(Address address);
-
- // The offset from the inlined patch site to the start of the
- // inlined store instruction.
- static const int kOffsetToStoreInstruction;
-
private:
// Update the inline cache and the global stub cache based on the
// lookup result.
@@ -490,10 +460,6 @@
static void Clear(Address address, Code* target);
- // Support for patching the index and the map that is checked in an
- // inlined version of the named store.
- static bool PatchInlinedStore(Address address, Object* map, int index);
-
friend class IC;
};
@@ -515,12 +481,6 @@
StrictModeFlag strict_mode);
static void GenerateGeneric(MacroAssembler* masm, StrictModeFlag strict_mode);
- // Clear the inlined version so the IC is always hit.
- static void ClearInlinedVersion(Address address);
-
- // Restore the inlined version so the fast case can get hit.
- static void RestoreInlinedVersion(Address address);
-
private:
// Update the inline cache.
void UpdateCaches(LookupResult* lookup,
@@ -565,42 +525,10 @@
static void Clear(Address address, Code* target);
- // Support for patching the map that is checked in an inlined
- // version of keyed store.
- // The address is the patch point for the IC call
- // (Assembler::kCallTargetAddressOffset before the end of
- // the call/return address).
- // The map is the new map that the inlined code should check against.
- static bool PatchInlinedStore(Address address, Object* map);
-
friend class IC;
};
-class BinaryOpIC: public IC {
- public:
-
- enum TypeInfo {
- UNINIT_OR_SMI,
- DEFAULT, // Initial state. When first executed, patches to one
- // of the following states depending on the operands types.
- HEAP_NUMBERS, // Both arguments are HeapNumbers.
- STRINGS, // At least one of the arguments is String.
- GENERIC // Non-specialized case (processes any type combination).
- };
-
- explicit BinaryOpIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) { }
-
- void patch(Code* code);
-
- static const char* GetName(TypeInfo type_info);
-
- static State ToState(TypeInfo type_info);
-
- static TypeInfo GetTypeInfo(Object* left, Object* right);
-};
-
-
// Type Recording BinaryOpIC, that records the types of the inputs and outputs.
class TRBinaryOpIC: public IC {
public:
@@ -611,6 +539,7 @@
INT32,
HEAP_NUMBER,
ODDBALL,
+ BOTH_STRING, // Only used for addition operation.
STRING, // Only used for addition operation. At least one string operand.
GENERIC
};
diff --git a/src/isolate.cc b/src/isolate.cc
index a163532..e42d78e 100644
--- a/src/isolate.cc
+++ b/src/isolate.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -54,6 +54,21 @@
namespace v8 {
namespace internal {
+Atomic32 ThreadId::highest_thread_id_ = 0;
+
+int ThreadId::AllocateThreadId() {
+ int new_id = NoBarrier_AtomicIncrement(&highest_thread_id_, 1);
+ return new_id;
+}
+
+int ThreadId::GetCurrentThreadId() {
+ int thread_id = Thread::GetThreadLocalInt(Isolate::thread_id_key_);
+ if (thread_id == 0) {
+ thread_id = AllocateThreadId();
+ Thread::SetThreadLocalInt(Isolate::thread_id_key_, thread_id);
+ }
+ return thread_id;
+}
// Create a dummy thread that will wait forever on a semaphore. The only
// purpose for this thread is to have some stack area to save essential data
@@ -245,7 +260,6 @@
Thread::LocalStorageKey Isolate::per_isolate_thread_data_key_;
Mutex* Isolate::process_wide_mutex_ = OS::CreateMutex();
Isolate::ThreadDataTable* Isolate::thread_data_table_ = NULL;
-Isolate::ThreadId Isolate::highest_thread_id_ = 0;
class IsolateInitializer {
@@ -265,20 +279,12 @@
static IsolateInitializer* static_initializer = EnsureDefaultIsolateAllocated();
-Isolate::ThreadId Isolate::AllocateThreadId() {
- ThreadId new_id;
- {
- ScopedLock lock(process_wide_mutex_);
- new_id = ++highest_thread_id_;
- }
- return new_id;
-}
+
Isolate::PerIsolateThreadData* Isolate::AllocatePerIsolateThreadData(
ThreadId thread_id) {
- ASSERT(thread_id != 0);
- ASSERT(Thread::GetThreadLocalInt(thread_id_key_) == thread_id);
+ ASSERT(!thread_id.Equals(ThreadId::Invalid()));
PerIsolateThreadData* per_thread = new PerIsolateThreadData(this, thread_id);
{
ScopedLock lock(process_wide_mutex_);
@@ -292,11 +298,7 @@
Isolate::PerIsolateThreadData*
Isolate::FindOrAllocatePerThreadDataForThisThread() {
- ThreadId thread_id = Thread::GetThreadLocalInt(thread_id_key_);
- if (thread_id == 0) {
- thread_id = AllocateThreadId();
- Thread::SetThreadLocalInt(thread_id_key_, thread_id);
- }
+ ThreadId thread_id = ThreadId::Current();
PerIsolateThreadData* per_thread = NULL;
{
ScopedLock lock(process_wide_mutex_);
@@ -361,7 +363,8 @@
Isolate::PerIsolateThreadData*
- Isolate::ThreadDataTable::Lookup(Isolate* isolate, ThreadId thread_id) {
+ Isolate::ThreadDataTable::Lookup(Isolate* isolate,
+ ThreadId thread_id) {
for (PerIsolateThreadData* data = list_; data != NULL; data = data->next_) {
if (data->Matches(isolate, thread_id)) return data;
}
@@ -383,7 +386,8 @@
}
-void Isolate::ThreadDataTable::Remove(Isolate* isolate, ThreadId thread_id) {
+void Isolate::ThreadDataTable::Remove(Isolate* isolate,
+ ThreadId thread_id) {
PerIsolateThreadData* data = Lookup(isolate, thread_id);
if (data != NULL) {
Remove(data);
@@ -414,7 +418,6 @@
runtime_profiler_(NULL),
compilation_cache_(NULL),
counters_(new Counters()),
- cpu_features_(NULL),
code_range_(NULL),
break_access_(OS::CreateMutex()),
logger_(new Logger()),
@@ -430,7 +433,7 @@
context_slot_cache_(NULL),
descriptor_lookup_cache_(NULL),
handle_scope_implementer_(NULL),
- scanner_constants_(NULL),
+ unicode_cache_(NULL),
in_use_list_(0),
free_list_(0),
preallocated_storage_preallocated_(false),
@@ -565,8 +568,8 @@
producer_heap_profile_ = NULL;
#endif
- delete scanner_constants_;
- scanner_constants_ = NULL;
+ delete unicode_cache_;
+ unicode_cache_ = NULL;
delete regexp_stack_;
regexp_stack_ = NULL;
@@ -593,8 +596,6 @@
delete counters_;
counters_ = NULL;
- delete cpu_features_;
- cpu_features_ = NULL;
delete handle_scope_implementer_;
handle_scope_implementer_ = NULL;
@@ -675,12 +676,11 @@
keyed_lookup_cache_ = new KeyedLookupCache();
context_slot_cache_ = new ContextSlotCache();
descriptor_lookup_cache_ = new DescriptorLookupCache();
- scanner_constants_ = new ScannerConstants();
+ unicode_cache_ = new UnicodeCache();
pc_to_code_cache_ = new PcToCodeCache(this);
write_input_buffer_ = new StringInputBuffer();
global_handles_ = new GlobalHandles(this);
bootstrapper_ = new Bootstrapper();
- cpu_features_ = new CpuFeatures();
handle_scope_implementer_ = new HandleScopeImplementer();
stub_cache_ = new StubCache(this);
ast_sentinels_ = new AstSentinels();
@@ -705,6 +705,33 @@
}
+void Isolate::PropagatePendingExceptionToExternalTryCatch() {
+ ASSERT(has_pending_exception());
+
+ bool external_caught = IsExternallyCaught();
+ thread_local_top_.external_caught_exception_ = external_caught;
+
+ if (!external_caught) return;
+
+ if (thread_local_top_.pending_exception_ == Failure::OutOfMemoryException()) {
+ // Do not propagate OOM exception: we should kill VM asap.
+ } else if (thread_local_top_.pending_exception_ ==
+ heap()->termination_exception()) {
+ try_catch_handler()->can_continue_ = false;
+ try_catch_handler()->exception_ = heap()->null_value();
+ } else {
+ // At this point all non-object (failure) exceptions have
+ // been dealt with so this shouldn't fail.
+ ASSERT(!pending_exception()->IsFailure());
+ try_catch_handler()->can_continue_ = true;
+ try_catch_handler()->exception_ = pending_exception();
+ if (!thread_local_top_.pending_message_obj_->IsTheHole()) {
+ try_catch_handler()->message_ = thread_local_top_.pending_message_obj_;
+ }
+ }
+}
+
+
bool Isolate::Init(Deserializer* des) {
ASSERT(state_ != INITIALIZED);
@@ -725,9 +752,6 @@
CpuProfiler::Setup();
HeapProfiler::Setup();
- // Setup the platform OS support.
- OS::Setup();
-
// Initialize other runtime facilities
#if defined(USE_SIMULATOR)
#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS)
@@ -786,11 +810,6 @@
// stack guard.
heap_.SetStackLimits();
- // Setup the CPU support. Must be done after heap setup and after
- // any deserialization because we have to have the initial heap
- // objects in place for creating the code object used for probing.
- CPU::Setup();
-
deoptimizer_data_ = new DeoptimizerData;
runtime_profiler_ = new RuntimeProfiler(this);
runtime_profiler_->Setup();
@@ -818,8 +837,8 @@
ASSERT(Current() == this);
ASSERT(entry_stack_ != NULL);
ASSERT(entry_stack_->previous_thread_data == NULL ||
- entry_stack_->previous_thread_data->thread_id() ==
- Thread::GetThreadLocalInt(thread_id_key_));
+ entry_stack_->previous_thread_data->thread_id().Equals(
+ ThreadId::Current()));
// Same thread re-enters the isolate, no need to re-init anything.
entry_stack_->entry_count++;
return;
@@ -857,8 +876,8 @@
void Isolate::Exit() {
ASSERT(entry_stack_ != NULL);
ASSERT(entry_stack_->previous_thread_data == NULL ||
- entry_stack_->previous_thread_data->thread_id() ==
- Thread::GetThreadLocalInt(thread_id_key_));
+ entry_stack_->previous_thread_data->thread_id().Equals(
+ ThreadId::Current()));
if (--entry_stack_->entry_count > 0) return;
diff --git a/src/isolate.h b/src/isolate.h
index 03a4866..35ffcb4 100644
--- a/src/isolate.h
+++ b/src/isolate.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -72,7 +72,7 @@
class ProducerHeapProfile;
class RegExpStack;
class SaveContext;
-class ScannerConstants;
+class UnicodeCache;
class StringInputBuffer;
class StringTracker;
class StubCache;
@@ -136,8 +136,59 @@
#endif
+// Platform-independent, reliable thread identifier.
+class ThreadId {
+ public:
+ // Creates an invalid ThreadId.
+ ThreadId() : id_(kInvalidId) {}
+
+ // Returns ThreadId for current thread.
+ static ThreadId Current() { return ThreadId(GetCurrentThreadId()); }
+
+ // Returns invalid ThreadId (guaranteed not to be equal to any thread).
+ static ThreadId Invalid() { return ThreadId(kInvalidId); }
+
+ // Compares ThreadIds for equality.
+ INLINE(bool Equals(const ThreadId& other) const) {
+ return id_ == other.id_;
+ }
+
+ // Checks whether this ThreadId refers to any thread.
+ INLINE(bool IsValid() const) {
+ return id_ != kInvalidId;
+ }
+
+ // Converts ThreadId to an integer representation
+ // (required for public API: V8::V8::GetCurrentThreadId).
+ int ToInteger() const { return id_; }
+
+ // Converts ThreadId to an integer representation
+ // (required for public API: V8::V8::TerminateExecution).
+ static ThreadId FromInteger(int id) { return ThreadId(id); }
+
+ private:
+ static const int kInvalidId = -1;
+
+ explicit ThreadId(int id) : id_(id) {}
+
+ static int AllocateThreadId();
+
+ static int GetCurrentThreadId();
+
+ int id_;
+
+ static Atomic32 highest_thread_id_;
+
+ friend class Isolate;
+};
+
+
class ThreadLocalTop BASE_EMBEDDED {
public:
+ // Does early low-level initialization that does not depend on the
+ // isolate being present.
+ ThreadLocalTop();
+
// Initialize the thread data.
void Initialize();
@@ -176,10 +227,9 @@
// The context where the current execution method is created and for variable
// lookups.
Context* context_;
- int thread_id_;
+ ThreadId thread_id_;
MaybeObject* pending_exception_;
bool has_pending_message_;
- const char* pending_message_;
Object* pending_message_obj_;
Script* pending_message_script_;
int pending_message_start_pos_;
@@ -218,6 +268,8 @@
v8::FailedAccessCheckCallback failed_access_check_callback_;
private:
+ void InitializeInternal();
+
Address try_catch_handler_address_;
};
@@ -242,6 +294,7 @@
#ifdef ENABLE_DEBUGGER_SUPPORT
#define ISOLATE_DEBUGGER_INIT_LIST(V) \
+ V(uint64_t, enabled_cpu_features, 0) \
V(v8::Debug::EventCallback, debug_event_callback, NULL) \
V(DebuggerAgent*, debugger_agent_instance, NULL)
#else
@@ -315,6 +368,8 @@
/* AstNode state. */ \
V(unsigned, ast_node_id, 0) \
V(unsigned, ast_node_count, 0) \
+ /* SafeStackFrameIterator activations count. */ \
+ V(int, safe_stack_iterator_counter, 0) \
ISOLATE_PLATFORM_INIT_LIST(V) \
ISOLATE_LOGGING_INIT_LIST(V) \
ISOLATE_DEBUGGER_INIT_LIST(V)
@@ -327,8 +382,6 @@
public:
~Isolate();
- typedef int ThreadId;
-
// A thread has a PerIsolateThreadData instance for each isolate that it has
// entered. That instance is allocated when the isolate is initially entered
// and reused on subsequent entries.
@@ -361,7 +414,7 @@
#endif
bool Matches(Isolate* isolate, ThreadId thread_id) const {
- return isolate_ == isolate && thread_id_ == thread_id;
+ return isolate_ == isolate && thread_id_.Equals(thread_id);
}
private:
@@ -453,9 +506,6 @@
return thread_id_key_;
}
- // Atomically allocates a new thread ID.
- static ThreadId AllocateThreadId();
-
// If a client attempts to create a Locker without specifying an isolate,
// we assume that the client is using legacy behavior. Set up the current
// thread to be inside the implicit isolate (or fail a check if we have
@@ -481,8 +531,8 @@
}
// Access to current thread id.
- int thread_id() { return thread_local_top_.thread_id_; }
- void set_thread_id(int id) { thread_local_top_.thread_id_ = id; }
+ ThreadId thread_id() { return thread_local_top_.thread_id_; }
+ void set_thread_id(ThreadId id) { thread_local_top_.thread_id_ = id; }
// Interface to pending exception.
MaybeObject* pending_exception() {
@@ -492,6 +542,9 @@
bool external_caught_exception() {
return thread_local_top_.external_caught_exception_;
}
+ void set_external_caught_exception(bool value) {
+ thread_local_top_.external_caught_exception_ = value;
+ }
void set_pending_exception(MaybeObject* exception) {
thread_local_top_.pending_exception_ = exception;
}
@@ -506,7 +559,6 @@
}
void clear_pending_message() {
thread_local_top_.has_pending_message_ = false;
- thread_local_top_.pending_message_ = NULL;
thread_local_top_.pending_message_obj_ = heap_.the_hole_value();
thread_local_top_.pending_message_script_ = NULL;
}
@@ -519,6 +571,12 @@
bool* external_caught_exception_address() {
return &thread_local_top_.external_caught_exception_;
}
+ v8::TryCatch* catcher() {
+ return thread_local_top_.catcher_;
+ }
+ void set_catcher(v8::TryCatch* catcher) {
+ thread_local_top_.catcher_ = catcher;
+ }
MaybeObject** scheduled_exception_address() {
return &thread_local_top_.scheduled_exception_;
@@ -589,6 +647,27 @@
// JavaScript code. If an exception is scheduled true is returned.
bool OptionalRescheduleException(bool is_bottom_call);
+ class ExceptionScope {
+ public:
+ explicit ExceptionScope(Isolate* isolate) :
+ // Scope currently can only be used for regular exceptions, not
+ // failures like OOM or termination exception.
+ isolate_(isolate),
+ pending_exception_(isolate_->pending_exception()->ToObjectUnchecked()),
+ catcher_(isolate_->catcher())
+ { }
+
+ ~ExceptionScope() {
+ isolate_->set_catcher(catcher_);
+ isolate_->set_pending_exception(*pending_exception_);
+ }
+
+ private:
+ Isolate* isolate_;
+ Handle<Object> pending_exception_;
+ v8::TryCatch* catcher_;
+ };
+
void SetCaptureStackTraceForUncaughtExceptions(
bool capture,
int frame_limit,
@@ -633,9 +712,7 @@
// Promote a scheduled exception to pending. Asserts has_scheduled_exception.
Failure* PromoteScheduledException();
- void DoThrow(MaybeObject* exception,
- MessageLocation* location,
- const char* message);
+ void DoThrow(MaybeObject* exception, MessageLocation* location);
// Checks if exception should be reported and finds out if it's
// caught externally.
bool ShouldReportException(bool* can_be_caught_externally,
@@ -708,10 +785,6 @@
Bootstrapper* bootstrapper() { return bootstrapper_; }
Counters* counters() { return counters_; }
- // TODO(isolates): Having CPU features per isolate is probably too
- // flexible. We only really need to have the set of currently
- // enabled features for asserts in DEBUG builds.
- CpuFeatures* cpu_features() { return cpu_features_; }
CodeRange* code_range() { return code_range_; }
RuntimeProfiler* runtime_profiler() { return runtime_profiler_; }
CompilationCache* compilation_cache() { return compilation_cache_; }
@@ -752,8 +825,8 @@
}
Zone* zone() { return &zone_; }
- ScannerConstants* scanner_constants() {
- return scanner_constants_;
+ UnicodeCache* unicode_cache() {
+ return unicode_cache_;
}
PcToCodeCache* pc_to_code_cache() { return pc_to_code_cache_; }
@@ -898,13 +971,19 @@
void SetCurrentVMState(StateTag state) {
if (RuntimeProfiler::IsEnabled()) {
- if (state == JS) {
- // JS or non-JS -> JS transition.
+ StateTag current_state = thread_local_top_.current_vm_state_;
+ if (current_state != JS && state == JS) {
+ // Non-JS -> JS transition.
RuntimeProfiler::IsolateEnteredJS(this);
- } else if (thread_local_top_.current_vm_state_ == JS) {
+ } else if (current_state == JS && state != JS) {
// JS -> non-JS transition.
ASSERT(RuntimeProfiler::IsSomeIsolateInJS());
RuntimeProfiler::IsolateExitedJS(this);
+ } else {
+ // Other types of state transitions are not interesting to the
+ // runtime profiler, because they don't affect whether we're
+ // in JS or not.
+ ASSERT((current_state == JS) == (state == JS));
}
}
thread_local_top_.current_vm_state_ = state;
@@ -965,7 +1044,6 @@
static Thread::LocalStorageKey thread_id_key_;
static Isolate* default_isolate_;
static ThreadDataTable* thread_data_table_;
- static ThreadId highest_thread_id_;
bool PreInit();
@@ -1018,6 +1096,8 @@
void FillCache();
+ void PropagatePendingExceptionToExternalTryCatch();
+
int stack_trace_nesting_level_;
StringStream* incomplete_message_;
// The preallocated memory thread singleton.
@@ -1029,7 +1109,6 @@
RuntimeProfiler* runtime_profiler_;
CompilationCache* compilation_cache_;
Counters* counters_;
- CpuFeatures* cpu_features_;
CodeRange* code_range_;
Mutex* break_access_;
Heap heap_;
@@ -1049,7 +1128,7 @@
DescriptorLookupCache* descriptor_lookup_cache_;
v8::ImplementationUtilities::HandleScopeData handle_scope_data_;
HandleScopeImplementer* handle_scope_implementer_;
- ScannerConstants* scanner_constants_;
+ UnicodeCache* unicode_cache_;
Zone zone_;
PreallocatedStorage in_use_list_;
PreallocatedStorage free_list_;
@@ -1124,6 +1203,7 @@
friend class ExecutionAccess;
friend class IsolateInitializer;
+ friend class ThreadId;
friend class v8::Isolate;
friend class v8::Locker;
@@ -1146,7 +1226,7 @@
isolate->set_save_context(this);
// If there is no JS frame under the current C frame, use the value 0.
- JavaScriptFrameIterator it;
+ JavaScriptFrameIterator it(isolate);
js_sp_ = it.done() ? 0 : it.frame()->sp();
}
diff --git a/src/jsregexp.h b/src/jsregexp.h
index 3ed5a7e..b9b2f60 100644
--- a/src/jsregexp.h
+++ b/src/jsregexp.h
@@ -1447,7 +1447,7 @@
class OffsetsVector {
public:
- inline OffsetsVector(int num_registers)
+ explicit inline OffsetsVector(int num_registers)
: offsets_vector_length_(num_registers) {
if (offsets_vector_length_ > Isolate::kJSRegexpStaticOffsetsVectorSize) {
vector_ = NewArray<int>(offsets_vector_length_);
diff --git a/src/jump-target-heavy-inl.h b/src/jump-target-heavy-inl.h
deleted file mode 100644
index 0a2a569..0000000
--- a/src/jump-target-heavy-inl.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_JUMP_TARGET_HEAVY_INL_H_
-#define V8_JUMP_TARGET_HEAVY_INL_H_
-
-#include "virtual-frame-inl.h"
-
-namespace v8 {
-namespace internal {
-
-void JumpTarget::InitializeEntryElement(int index, FrameElement* target) {
- FrameElement* element = &entry_frame_->elements_[index];
- element->clear_copied();
- if (target->is_register()) {
- entry_frame_->set_register_location(target->reg(), index);
- } else if (target->is_copy()) {
- entry_frame_->elements_[target->index()].set_copied();
- }
- if (direction_ == BIDIRECTIONAL && !target->is_copy()) {
- element->set_type_info(TypeInfo::Unknown());
- }
-}
-
-} } // namespace v8::internal
-
-#endif // V8_JUMP_TARGET_HEAVY_INL_H_
diff --git a/src/jump-target-heavy.cc b/src/jump-target-heavy.cc
deleted file mode 100644
index f73e027..0000000
--- a/src/jump-target-heavy.cc
+++ /dev/null
@@ -1,427 +0,0 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "v8.h"
-
-#include "codegen-inl.h"
-#include "jump-target-inl.h"
-#include "register-allocator-inl.h"
-
-namespace v8 {
-namespace internal {
-
-
-void JumpTarget::Jump(Result* arg) {
- ASSERT(cgen()->has_valid_frame());
-
- cgen()->frame()->Push(arg);
- DoJump();
-}
-
-
-void JumpTarget::Branch(Condition cc, Result* arg, Hint hint) {
- ASSERT(cgen()->has_valid_frame());
-
- // We want to check that non-frame registers at the call site stay in
- // the same registers on the fall-through branch.
-#ifdef DEBUG
- Result::Type arg_type = arg->type();
- Register arg_reg = arg->is_register() ? arg->reg() : no_reg;
-#endif
-
- cgen()->frame()->Push(arg);
- DoBranch(cc, hint);
- *arg = cgen()->frame()->Pop();
-
- ASSERT(arg->type() == arg_type);
- ASSERT(!arg->is_register() || arg->reg().is(arg_reg));
-}
-
-
-void JumpTarget::Branch(Condition cc, Result* arg0, Result* arg1, Hint hint) {
- ASSERT(cgen()->has_valid_frame());
-
- // We want to check that non-frame registers at the call site stay in
- // the same registers on the fall-through branch.
-#ifdef DEBUG
- Result::Type arg0_type = arg0->type();
- Register arg0_reg = arg0->is_register() ? arg0->reg() : no_reg;
- Result::Type arg1_type = arg1->type();
- Register arg1_reg = arg1->is_register() ? arg1->reg() : no_reg;
-#endif
-
- cgen()->frame()->Push(arg0);
- cgen()->frame()->Push(arg1);
- DoBranch(cc, hint);
- *arg1 = cgen()->frame()->Pop();
- *arg0 = cgen()->frame()->Pop();
-
- ASSERT(arg0->type() == arg0_type);
- ASSERT(!arg0->is_register() || arg0->reg().is(arg0_reg));
- ASSERT(arg1->type() == arg1_type);
- ASSERT(!arg1->is_register() || arg1->reg().is(arg1_reg));
-}
-
-
-void BreakTarget::Branch(Condition cc, Result* arg, Hint hint) {
- ASSERT(cgen()->has_valid_frame());
-
- int count = cgen()->frame()->height() - expected_height_;
- if (count > 0) {
- // We negate and branch here rather than using DoBranch's negate
- // and branch. This gives us a hook to remove statement state
- // from the frame.
- JumpTarget fall_through;
- // Branch to fall through will not negate, because it is a
- // forward-only target.
- fall_through.Branch(NegateCondition(cc), NegateHint(hint));
- Jump(arg); // May emit merge code here.
- fall_through.Bind();
- } else {
-#ifdef DEBUG
- Result::Type arg_type = arg->type();
- Register arg_reg = arg->is_register() ? arg->reg() : no_reg;
-#endif
- cgen()->frame()->Push(arg);
- DoBranch(cc, hint);
- *arg = cgen()->frame()->Pop();
- ASSERT(arg->type() == arg_type);
- ASSERT(!arg->is_register() || arg->reg().is(arg_reg));
- }
-}
-
-
-void JumpTarget::Bind(Result* arg) {
- if (cgen()->has_valid_frame()) {
- cgen()->frame()->Push(arg);
- }
- DoBind();
- *arg = cgen()->frame()->Pop();
-}
-
-
-void JumpTarget::Bind(Result* arg0, Result* arg1) {
- if (cgen()->has_valid_frame()) {
- cgen()->frame()->Push(arg0);
- cgen()->frame()->Push(arg1);
- }
- DoBind();
- *arg1 = cgen()->frame()->Pop();
- *arg0 = cgen()->frame()->Pop();
-}
-
-
-void JumpTarget::ComputeEntryFrame() {
- // Given: a collection of frames reaching by forward CFG edges and
- // the directionality of the block. Compute: an entry frame for the
- // block.
-
- Isolate::Current()->counters()->compute_entry_frame()->Increment();
-#ifdef DEBUG
- if (Isolate::Current()->jump_target_compiling_deferred_code()) {
- ASSERT(reaching_frames_.length() > 1);
- VirtualFrame* frame = reaching_frames_[0];
- bool all_identical = true;
- for (int i = 1; i < reaching_frames_.length(); i++) {
- if (!frame->Equals(reaching_frames_[i])) {
- all_identical = false;
- break;
- }
- }
- ASSERT(!all_identical || all_identical);
- }
-#endif
-
- // Choose an initial frame.
- VirtualFrame* initial_frame = reaching_frames_[0];
-
- // A list of pointers to frame elements in the entry frame. NULL
- // indicates that the element has not yet been determined.
- int length = initial_frame->element_count();
- ZoneList<FrameElement*> elements(length);
-
- // Initially populate the list of elements based on the initial
- // frame.
- for (int i = 0; i < length; i++) {
- FrameElement element = initial_frame->elements_[i];
- // We do not allow copies or constants in bidirectional frames.
- if (direction_ == BIDIRECTIONAL) {
- if (element.is_constant() || element.is_copy()) {
- elements.Add(NULL);
- continue;
- }
- }
- elements.Add(&initial_frame->elements_[i]);
- }
-
- // Compute elements based on the other reaching frames.
- if (reaching_frames_.length() > 1) {
- for (int i = 0; i < length; i++) {
- FrameElement* element = elements[i];
- for (int j = 1; j < reaching_frames_.length(); j++) {
- // Element computation is monotonic: new information will not
- // change our decision about undetermined or invalid elements.
- if (element == NULL || !element->is_valid()) break;
-
- FrameElement* other = &reaching_frames_[j]->elements_[i];
- element = element->Combine(other);
- if (element != NULL && !element->is_copy()) {
- ASSERT(other != NULL);
- // We overwrite the number information of one of the incoming frames.
- // This is safe because we only use the frame for emitting merge code.
- // The number information of incoming frames is not used anymore.
- element->set_type_info(TypeInfo::Combine(element->type_info(),
- other->type_info()));
- }
- }
- elements[i] = element;
- }
- }
-
- // Build the new frame. A freshly allocated frame has memory elements
- // for the parameters and some platform-dependent elements (e.g.,
- // return address). Replace those first.
- entry_frame_ = new VirtualFrame();
- int index = 0;
- for (; index < entry_frame_->element_count(); index++) {
- FrameElement* target = elements[index];
- // If the element is determined, set it now. Count registers. Mark
- // elements as copied exactly when they have a copy. Undetermined
- // elements are initially recorded as if in memory.
- if (target != NULL) {
- entry_frame_->elements_[index] = *target;
- InitializeEntryElement(index, target);
- }
- }
- // Then fill in the rest of the frame with new elements.
- for (; index < length; index++) {
- FrameElement* target = elements[index];
- if (target == NULL) {
- entry_frame_->elements_.Add(
- FrameElement::MemoryElement(TypeInfo::Uninitialized()));
- } else {
- entry_frame_->elements_.Add(*target);
- InitializeEntryElement(index, target);
- }
- }
-
- // Allocate any still-undetermined frame elements to registers or
- // memory, from the top down.
- for (int i = length - 1; i >= 0; i--) {
- if (elements[i] == NULL) {
- // Loop over all the reaching frames to check whether the element
- // is synced on all frames and to count the registers it occupies.
- bool is_synced = true;
- RegisterFile candidate_registers;
- int best_count = kMinInt;
- int best_reg_num = RegisterAllocator::kInvalidRegister;
- TypeInfo info = TypeInfo::Uninitialized();
-
- for (int j = 0; j < reaching_frames_.length(); j++) {
- FrameElement element = reaching_frames_[j]->elements_[i];
- if (direction_ == BIDIRECTIONAL) {
- info = TypeInfo::Unknown();
- } else if (!element.is_copy()) {
- info = TypeInfo::Combine(info, element.type_info());
- } else {
- // New elements will not be copies, so get number information from
- // backing element in the reaching frame.
- info = TypeInfo::Combine(info,
- reaching_frames_[j]->elements_[element.index()].type_info());
- }
- is_synced = is_synced && element.is_synced();
- if (element.is_register() && !entry_frame_->is_used(element.reg())) {
- // Count the register occurrence and remember it if better
- // than the previous best.
- int num = RegisterAllocator::ToNumber(element.reg());
- candidate_registers.Use(num);
- if (candidate_registers.count(num) > best_count) {
- best_count = candidate_registers.count(num);
- best_reg_num = num;
- }
- }
- }
-
- // We must have a number type information now (not for copied elements).
- ASSERT(entry_frame_->elements_[i].is_copy()
- || !info.IsUninitialized());
-
- // If the value is synced on all frames, put it in memory. This
- // costs nothing at the merge code but will incur a
- // memory-to-register move when the value is needed later.
- if (is_synced) {
- // Already recorded as a memory element.
- // Set combined number info.
- entry_frame_->elements_[i].set_type_info(info);
- continue;
- }
-
- // Try to put it in a register. If there was no best choice
- // consider any free register.
- if (best_reg_num == RegisterAllocator::kInvalidRegister) {
- for (int j = 0; j < RegisterAllocator::kNumRegisters; j++) {
- if (!entry_frame_->is_used(j)) {
- best_reg_num = j;
- break;
- }
- }
- }
-
- if (best_reg_num != RegisterAllocator::kInvalidRegister) {
- // If there was a register choice, use it. Preserve the copied
- // flag on the element.
- bool is_copied = entry_frame_->elements_[i].is_copied();
- Register reg = RegisterAllocator::ToRegister(best_reg_num);
- entry_frame_->elements_[i] =
- FrameElement::RegisterElement(reg, FrameElement::NOT_SYNCED,
- TypeInfo::Uninitialized());
- if (is_copied) entry_frame_->elements_[i].set_copied();
- entry_frame_->set_register_location(reg, i);
- }
- // Set combined number info.
- entry_frame_->elements_[i].set_type_info(info);
- }
- }
-
- // If we have incoming backward edges assert we forget all number information.
-#ifdef DEBUG
- if (direction_ == BIDIRECTIONAL) {
- for (int i = 0; i < length; ++i) {
- if (!entry_frame_->elements_[i].is_copy()) {
- ASSERT(entry_frame_->elements_[i].type_info().IsUnknown());
- }
- }
- }
-#endif
-
- // The stack pointer is at the highest synced element or the base of
- // the expression stack.
- int stack_pointer = length - 1;
- while (stack_pointer >= entry_frame_->expression_base_index() &&
- !entry_frame_->elements_[stack_pointer].is_synced()) {
- stack_pointer--;
- }
- entry_frame_->stack_pointer_ = stack_pointer;
-}
-
-
-FrameRegisterState::FrameRegisterState(VirtualFrame* frame) {
- // Copy the register locations from the code generator's frame.
- // These are the registers that will be spilled on entry to the
- // deferred code and restored on exit.
- int sp_offset = frame->fp_relative(frame->stack_pointer_);
- for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
- int loc = frame->register_location(i);
- if (loc == VirtualFrame::kIllegalIndex) {
- registers_[i] = kIgnore;
- } else if (frame->elements_[loc].is_synced()) {
- // Needs to be restored on exit but not saved on entry.
- registers_[i] = frame->fp_relative(loc) | kSyncedFlag;
- } else {
- int offset = frame->fp_relative(loc);
- registers_[i] = (offset < sp_offset) ? kPush : offset;
- }
- }
-}
-
-
-void JumpTarget::Unuse() {
- reaching_frames_.Clear();
- merge_labels_.Clear();
- entry_frame_ = NULL;
- entry_label_.Unuse();
-}
-
-
-void JumpTarget::AddReachingFrame(VirtualFrame* frame) {
- ASSERT(reaching_frames_.length() == merge_labels_.length());
- ASSERT(entry_frame_ == NULL);
- Label fresh;
- merge_labels_.Add(fresh);
- reaching_frames_.Add(frame);
-}
-
-
-// -------------------------------------------------------------------------
-// BreakTarget implementation.
-
-void BreakTarget::set_direction(Directionality direction) {
- JumpTarget::set_direction(direction);
- ASSERT(cgen()->has_valid_frame());
- expected_height_ = cgen()->frame()->height();
-}
-
-
-void BreakTarget::CopyTo(BreakTarget* destination) {
- ASSERT(destination != NULL);
- destination->direction_ = direction_;
- destination->reaching_frames_.Rewind(0);
- destination->reaching_frames_.AddAll(reaching_frames_);
- destination->merge_labels_.Rewind(0);
- destination->merge_labels_.AddAll(merge_labels_);
- destination->entry_frame_ = entry_frame_;
- destination->entry_label_ = entry_label_;
- destination->expected_height_ = expected_height_;
-}
-
-
-void BreakTarget::Branch(Condition cc, Hint hint) {
- ASSERT(cgen()->has_valid_frame());
-
- int count = cgen()->frame()->height() - expected_height_;
- if (count > 0) {
- // We negate and branch here rather than using DoBranch's negate
- // and branch. This gives us a hook to remove statement state
- // from the frame.
- JumpTarget fall_through;
- // Branch to fall through will not negate, because it is a
- // forward-only target.
- fall_through.Branch(NegateCondition(cc), NegateHint(hint));
- Jump(); // May emit merge code here.
- fall_through.Bind();
- } else {
- DoBranch(cc, hint);
- }
-}
-
-
-DeferredCode::DeferredCode()
- : masm_(CodeGeneratorScope::Current(Isolate::Current())->masm()),
- statement_position_(masm_->positions_recorder()->
- current_statement_position()),
- position_(masm_->positions_recorder()->current_position()),
- frame_state_(CodeGeneratorScope::Current(Isolate::Current())->frame()) {
- ASSERT(statement_position_ != RelocInfo::kNoPosition);
- ASSERT(position_ != RelocInfo::kNoPosition);
-
- CodeGeneratorScope::Current(Isolate::Current())->AddDeferred(this);
-#ifdef DEBUG
- comment_ = "";
-#endif
-}
-
-} } // namespace v8::internal
diff --git a/src/jump-target-heavy.h b/src/jump-target-heavy.h
deleted file mode 100644
index bf97756..0000000
--- a/src/jump-target-heavy.h
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_JUMP_TARGET_HEAVY_H_
-#define V8_JUMP_TARGET_HEAVY_H_
-
-#include "macro-assembler.h"
-#include "zone-inl.h"
-
-namespace v8 {
-namespace internal {
-
-// Forward declarations.
-class FrameElement;
-class Result;
-class VirtualFrame;
-
-// -------------------------------------------------------------------------
-// Jump targets
-//
-// A jump target is an abstraction of a basic-block entry in generated
-// code. It collects all the virtual frames reaching the block by
-// forward jumps and pairs them with labels for the merge code along
-// all forward-reaching paths. When bound, an expected frame for the
-// block is determined and code is generated to merge to the expected
-// frame. For backward jumps, the merge code is generated at the edge
-// leaving the predecessor block.
-//
-// A jump target must have been reached via control flow (either by
-// jumping, branching, or falling through) at the time it is bound.
-// In particular, this means that at least one of the control-flow
-// graph edges reaching the target must be a forward edge.
-
-class JumpTarget : public ZoneObject { // Shadows are dynamically allocated.
- public:
- // Forward-only jump targets can only be reached by forward CFG edges.
- enum Directionality { FORWARD_ONLY, BIDIRECTIONAL };
-
- // Construct a jump target used to generate code and to provide
- // access to a current frame.
- explicit JumpTarget(Directionality direction)
- : direction_(direction),
- reaching_frames_(0),
- merge_labels_(0),
- entry_frame_(NULL) {
- }
-
- // Construct a jump target.
- JumpTarget()
- : direction_(FORWARD_ONLY),
- reaching_frames_(0),
- merge_labels_(0),
- entry_frame_(NULL) {
- }
-
- virtual ~JumpTarget() {}
-
- // Set the direction of the jump target.
- virtual void set_direction(Directionality direction) {
- direction_ = direction;
- }
-
- // Treat the jump target as a fresh one. The state is reset.
- void Unuse();
-
- inline CodeGenerator* cgen();
-
- Label* entry_label() { return &entry_label_; }
-
- VirtualFrame* entry_frame() const { return entry_frame_; }
- void set_entry_frame(VirtualFrame* frame) {
- entry_frame_ = frame;
- }
-
- // Predicates testing the state of the encapsulated label.
- bool is_bound() const { return entry_label_.is_bound(); }
- bool is_linked() const {
- return !is_bound() && !reaching_frames_.is_empty();
- }
- bool is_unused() const {
- // This is !is_bound() && !is_linked().
- return !is_bound() && reaching_frames_.is_empty();
- }
-
- // Emit a jump to the target. There must be a current frame at the
- // jump and there will be no current frame after the jump.
- virtual void Jump();
- virtual void Jump(Result* arg);
-
- // Emit a conditional branch to the target. There must be a current
- // frame at the branch. The current frame will fall through to the
- // code after the branch. The arg is a result that is live both at
- // the target and the fall-through.
- virtual void Branch(Condition cc, Hint hint = no_hint);
- virtual void Branch(Condition cc, Result* arg, Hint hint = no_hint);
- void Branch(Condition cc,
- Result* arg0,
- Result* arg1,
- Hint hint = no_hint);
-
- // Bind a jump target. If there is no current frame at the binding
- // site, there must be at least one frame reaching via a forward
- // jump.
- virtual void Bind();
- virtual void Bind(Result* arg);
- void Bind(Result* arg0, Result* arg1);
-
- // Emit a call to a jump target. There must be a current frame at
- // the call. The frame at the target is the same as the current
- // frame except for an extra return address on top of it. The frame
- // after the call is the same as the frame before the call.
- void Call();
-
- protected:
- // Directionality flag set at initialization time.
- Directionality direction_;
-
- // A list of frames reaching this block via forward jumps.
- ZoneList<VirtualFrame*> reaching_frames_;
-
- // A parallel list of labels for merge code.
- ZoneList<Label> merge_labels_;
-
- // The frame used on entry to the block and expected at backward
- // jumps to the block. Set when the jump target is bound, but may
- // or may not be set for forward-only blocks.
- VirtualFrame* entry_frame_;
-
- // The actual entry label of the block.
- Label entry_label_;
-
- // Implementations of Jump, Branch, and Bind with all arguments and
- // return values using the virtual frame.
- void DoJump();
- void DoBranch(Condition cc, Hint hint);
- void DoBind();
-
- private:
- // Add a virtual frame reaching this labeled block via a forward jump,
- // and a corresponding merge code label.
- void AddReachingFrame(VirtualFrame* frame);
-
- // Perform initialization required during entry frame computation
- // after setting the virtual frame element at index in frame to be
- // target.
- inline void InitializeEntryElement(int index, FrameElement* target);
-
- // Compute a frame to use for entry to this block.
- void ComputeEntryFrame();
-
- DISALLOW_COPY_AND_ASSIGN(JumpTarget);
-};
-
-
-// -------------------------------------------------------------------------
-// Break targets
-//
-// A break target is a jump target that can be used to break out of a
-// statement that keeps extra state on the stack (eg, for/in or
-// try/finally). They know the expected stack height at the target
-// and will drop state from nested statements as part of merging.
-//
-// Break targets are used for return, break, and continue targets.
-
-class BreakTarget : public JumpTarget {
- public:
- // Construct a break target.
- BreakTarget() {}
- explicit BreakTarget(JumpTarget::Directionality direction)
- : JumpTarget(direction) { }
-
- virtual ~BreakTarget() {}
-
- // Set the direction of the break target.
- virtual void set_direction(Directionality direction);
-
- // Copy the state of this break target to the destination. The
- // lists of forward-reaching frames and merge-point labels are
- // copied. All virtual frame pointers are copied, not the
- // pointed-to frames. The previous state of the destination is
- // overwritten, without deallocating pointed-to virtual frames.
- void CopyTo(BreakTarget* destination);
-
- // Emit a jump to the target. There must be a current frame at the
- // jump and there will be no current frame after the jump.
- virtual void Jump();
- virtual void Jump(Result* arg);
-
- // Emit a conditional branch to the target. There must be a current
- // frame at the branch. The current frame will fall through to the
- // code after the branch.
- virtual void Branch(Condition cc, Hint hint = no_hint);
- virtual void Branch(Condition cc, Result* arg, Hint hint = no_hint);
-
- // Bind a break target. If there is no current frame at the binding
- // site, there must be at least one frame reaching via a forward
- // jump.
- virtual void Bind();
- virtual void Bind(Result* arg);
-
- // Setter for expected height.
- void set_expected_height(int expected) { expected_height_ = expected; }
-
- private:
- // The expected height of the expression stack where the target will
- // be bound, statically known at initialization time.
- int expected_height_;
-
- DISALLOW_COPY_AND_ASSIGN(BreakTarget);
-};
-
-} } // namespace v8::internal
-
-#endif // V8_JUMP_TARGET_HEAVY_H_
diff --git a/src/jump-target-inl.h b/src/jump-target-inl.h
deleted file mode 100644
index 545328c..0000000
--- a/src/jump-target-inl.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_JUMP_TARGET_INL_H_
-#define V8_JUMP_TARGET_INL_H_
-
-#include "virtual-frame-inl.h"
-
-#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64
-#include "jump-target-heavy-inl.h"
-#else
-#include "jump-target-light-inl.h"
-#endif
-
-namespace v8 {
-namespace internal {
-
-CodeGenerator* JumpTarget::cgen() {
- return CodeGeneratorScope::Current(Isolate::Current());
-}
-
-} } // namespace v8::internal
-
-#endif // V8_JUMP_TARGET_INL_H_
diff --git a/src/jump-target-light-inl.h b/src/jump-target-light-inl.h
deleted file mode 100644
index e8f1a5f..0000000
--- a/src/jump-target-light-inl.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_JUMP_TARGET_LIGHT_INL_H_
-#define V8_JUMP_TARGET_LIGHT_INL_H_
-
-#include "virtual-frame-inl.h"
-
-namespace v8 {
-namespace internal {
-
-// Construct a jump target.
-JumpTarget::JumpTarget(Directionality direction)
- : entry_frame_set_(false),
- direction_(direction),
- entry_frame_(kInvalidVirtualFrameInitializer) {
-}
-
-JumpTarget::JumpTarget()
- : entry_frame_set_(false),
- direction_(FORWARD_ONLY),
- entry_frame_(kInvalidVirtualFrameInitializer) {
-}
-
-
-BreakTarget::BreakTarget() { }
-BreakTarget::BreakTarget(JumpTarget::Directionality direction)
- : JumpTarget(direction) { }
-
-} } // namespace v8::internal
-
-#endif // V8_JUMP_TARGET_LIGHT_INL_H_
diff --git a/src/jump-target-light.cc b/src/jump-target-light.cc
deleted file mode 100644
index 1d89474..0000000
--- a/src/jump-target-light.cc
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "v8.h"
-
-#include "codegen-inl.h"
-#include "jump-target-inl.h"
-
-namespace v8 {
-namespace internal {
-
-
-DeferredCode::DeferredCode()
- : masm_(CodeGeneratorScope::Current(Isolate::Current())->masm()),
- statement_position_(masm_->positions_recorder()->
- current_statement_position()),
- position_(masm_->positions_recorder()->current_position()),
- frame_state_(*CodeGeneratorScope::Current(Isolate::Current())->frame()) {
- ASSERT(statement_position_ != RelocInfo::kNoPosition);
- ASSERT(position_ != RelocInfo::kNoPosition);
-
- CodeGeneratorScope::Current(Isolate::Current())->AddDeferred(this);
-
-#ifdef DEBUG
- comment_ = "";
-#endif
-}
-
-
-// -------------------------------------------------------------------------
-// BreakTarget implementation.
-
-
-void BreakTarget::SetExpectedHeight() {
- expected_height_ = cgen()->frame()->height();
-}
-
-
-void BreakTarget::Jump() {
- ASSERT(cgen()->has_valid_frame());
-
- int count = cgen()->frame()->height() - expected_height_;
- if (count > 0) {
- cgen()->frame()->Drop(count);
- }
- DoJump();
-}
-
-
-void BreakTarget::Branch(Condition cc, Hint hint) {
- if (cc == al) {
- Jump();
- return;
- }
-
- ASSERT(cgen()->has_valid_frame());
-
- int count = cgen()->frame()->height() - expected_height_;
- if (count > 0) {
- // We negate and branch here rather than using DoBranch's negate
- // and branch. This gives us a hook to remove statement state
- // from the frame.
- JumpTarget fall_through;
- // Branch to fall through will not negate, because it is a
- // forward-only target.
- fall_through.Branch(NegateCondition(cc), NegateHint(hint));
- // Emit merge code.
- cgen()->frame()->Drop(count);
- DoJump();
- fall_through.Bind();
- } else {
- DoBranch(cc, hint);
- }
-}
-
-
-void BreakTarget::Bind() {
- if (cgen()->has_valid_frame()) {
- int count = cgen()->frame()->height() - expected_height_;
- if (count > 0) {
- cgen()->frame()->Drop(count);
- }
- }
- DoBind();
-}
-
-} } // namespace v8::internal
diff --git a/src/jump-target-light.h b/src/jump-target-light.h
deleted file mode 100644
index 0d65306..0000000
--- a/src/jump-target-light.h
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_JUMP_TARGET_LIGHT_H_
-#define V8_JUMP_TARGET_LIGHT_H_
-
-#include "macro-assembler.h"
-#include "zone-inl.h"
-#include "virtual-frame.h"
-
-namespace v8 {
-namespace internal {
-
-// Forward declarations.
-class FrameElement;
-class Result;
-
-// -------------------------------------------------------------------------
-// Jump targets
-//
-// A jump target is an abstraction of a basic-block entry in generated
-// code. It collects all the virtual frames reaching the block by
-// forward jumps and pairs them with labels for the merge code along
-// all forward-reaching paths. When bound, an expected frame for the
-// block is determined and code is generated to merge to the expected
-// frame. For backward jumps, the merge code is generated at the edge
-// leaving the predecessor block.
-//
-// A jump target must have been reached via control flow (either by
-// jumping, branching, or falling through) at the time it is bound.
-// In particular, this means that at least one of the control-flow
-// graph edges reaching the target must be a forward edge.
-
-class JumpTarget : public ZoneObject { // Shadows are dynamically allocated.
- public:
- // Forward-only jump targets can only be reached by forward CFG edges.
- enum Directionality { FORWARD_ONLY, BIDIRECTIONAL };
-
- // Construct a jump target.
- explicit inline JumpTarget(Directionality direction);
-
- inline JumpTarget();
-
- virtual ~JumpTarget() {}
-
- void Unuse() {
- entry_frame_set_ = false;
- entry_label_.Unuse();
- }
-
- inline CodeGenerator* cgen();
-
- Label* entry_label() { return &entry_label_; }
-
- const VirtualFrame* entry_frame() const {
- return entry_frame_set_ ? &entry_frame_ : NULL;
- }
-
- void set_entry_frame(VirtualFrame* frame) {
- entry_frame_ = *frame;
- entry_frame_set_ = true;
- }
-
- // Predicates testing the state of the encapsulated label.
- bool is_bound() const { return entry_label_.is_bound(); }
- bool is_linked() const { return entry_label_.is_linked(); }
- bool is_unused() const { return entry_label_.is_unused(); }
-
- // Copy the state of this jump target to the destination.
- inline void CopyTo(JumpTarget* destination) {
- *destination = *this;
- }
-
- // Emit a jump to the target. There must be a current frame at the
- // jump and there will be no current frame after the jump.
- virtual void Jump();
-
- // Emit a conditional branch to the target. There must be a current
- // frame at the branch. The current frame will fall through to the
- // code after the branch.
- virtual void Branch(Condition cc, Hint hint = no_hint);
-
- // Bind a jump target. If there is no current frame at the binding
- // site, there must be at least one frame reaching via a forward
- // jump.
- virtual void Bind();
-
- // Emit a call to a jump target. There must be a current frame at
- // the call. The frame at the target is the same as the current
- // frame except for an extra return address on top of it. The frame
- // after the call is the same as the frame before the call.
- void Call();
-
- protected:
- // Has an entry frame been found?
- bool entry_frame_set_;
-
- // Can we branch backwards to this label?
- Directionality direction_;
-
- // The frame used on entry to the block and expected at backward
- // jumps to the block. Set the first time something branches to this
- // jump target.
- VirtualFrame entry_frame_;
-
- // The actual entry label of the block.
- Label entry_label_;
-
- // Implementations of Jump, Branch, and Bind with all arguments and
- // return values using the virtual frame.
- void DoJump();
- void DoBranch(Condition cc, Hint hint);
- void DoBind();
-};
-
-
-// -------------------------------------------------------------------------
-// Break targets
-//
-// A break target is a jump target that can be used to break out of a
-// statement that keeps extra state on the stack (eg, for/in or
-// try/finally). They know the expected stack height at the target
-// and will drop state from nested statements as part of merging.
-//
-// Break targets are used for return, break, and continue targets.
-
-class BreakTarget : public JumpTarget {
- public:
- // Construct a break target.
- inline BreakTarget();
-
- inline BreakTarget(JumpTarget::Directionality direction);
-
- virtual ~BreakTarget() {}
-
- // Copy the state of this jump target to the destination.
- inline void CopyTo(BreakTarget* destination) {
- *destination = *this;
- }
-
- // Emit a jump to the target. There must be a current frame at the
- // jump and there will be no current frame after the jump.
- virtual void Jump();
-
- // Emit a conditional branch to the target. There must be a current
- // frame at the branch. The current frame will fall through to the
- // code after the branch.
- virtual void Branch(Condition cc, Hint hint = no_hint);
-
- // Bind a break target. If there is no current frame at the binding
- // site, there must be at least one frame reaching via a forward
- // jump.
- virtual void Bind();
-
- // Setter for expected height.
- void set_expected_height(int expected) { expected_height_ = expected; }
-
- // Uses the current frame to set the expected height.
- void SetExpectedHeight();
-
- private:
- // The expected height of the expression stack where the target will
- // be bound, statically known at initialization time.
- int expected_height_;
-};
-
-} } // namespace v8::internal
-
-#endif // V8_JUMP_TARGET_LIGHT_H_
diff --git a/src/jump-target.cc b/src/jump-target.cc
deleted file mode 100644
index 72aada8..0000000
--- a/src/jump-target.cc
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "v8.h"
-
-#include "codegen-inl.h"
-#include "jump-target-inl.h"
-#include "register-allocator-inl.h"
-
-namespace v8 {
-namespace internal {
-
-// -------------------------------------------------------------------------
-// JumpTarget implementation.
-
-void JumpTarget::Jump() {
- DoJump();
-}
-
-
-void JumpTarget::Branch(Condition cc, Hint hint) {
- DoBranch(cc, hint);
-}
-
-
-void JumpTarget::Bind() {
- DoBind();
-}
-
-
-// -------------------------------------------------------------------------
-// ShadowTarget implementation.
-
-ShadowTarget::ShadowTarget(BreakTarget* shadowed) {
- ASSERT(shadowed != NULL);
- other_target_ = shadowed;
-
-#ifdef DEBUG
- is_shadowing_ = true;
-#endif
- // While shadowing this shadow target saves the state of the original.
- shadowed->CopyTo(this);
-
- // The original's state is reset.
- shadowed->Unuse();
- ASSERT(cgen()->has_valid_frame());
- shadowed->set_expected_height(cgen()->frame()->height());
-}
-
-
-void ShadowTarget::StopShadowing() {
- ASSERT(is_shadowing_);
-
- // The states of this target, which was shadowed, and the original
- // target, which was shadowing, are swapped.
- BreakTarget temp;
- other_target_->CopyTo(&temp);
- CopyTo(other_target_);
- temp.CopyTo(this);
- temp.Unuse();
-
-#ifdef DEBUG
- is_shadowing_ = false;
-#endif
-}
-
-} } // namespace v8::internal
diff --git a/src/jump-target.h b/src/jump-target.h
deleted file mode 100644
index a0d2686..0000000
--- a/src/jump-target.h
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_JUMP_TARGET_H_
-#define V8_JUMP_TARGET_H_
-
-#if V8_TARGET_ARCH_IA32
-#include "jump-target-heavy.h"
-#elif V8_TARGET_ARCH_X64
-#include "jump-target-heavy.h"
-#elif V8_TARGET_ARCH_ARM
-#include "jump-target-light.h"
-#elif V8_TARGET_ARCH_MIPS
-#include "jump-target-light.h"
-#else
-#error Unsupported target architecture.
-#endif
-
-namespace v8 {
-namespace internal {
-
-// -------------------------------------------------------------------------
-// Shadow break targets
-//
-// A shadow break target represents a break target that is temporarily
-// shadowed by another one (represented by the original during
-// shadowing). They are used to catch jumps to labels in certain
-// contexts, e.g. try blocks. After shadowing ends, the formerly
-// shadowed target is again represented by the original and the
-// ShadowTarget can be used as a jump target in its own right,
-// representing the formerly shadowing target.
-
-class ShadowTarget : public BreakTarget {
- public:
- // Construct a shadow jump target. After construction the shadow
- // target object holds the state of the original target, and the
- // original target is actually a fresh one that intercepts control
- // flow intended for the shadowed one.
- explicit ShadowTarget(BreakTarget* shadowed);
-
- virtual ~ShadowTarget() {}
-
- // End shadowing. After shadowing ends, the original jump target
- // again gives access to the formerly shadowed target and the shadow
- // target object gives access to the formerly shadowing target.
- void StopShadowing();
-
- // During shadowing, the currently shadowing target. After
- // shadowing, the target that was shadowed.
- BreakTarget* other_target() const { return other_target_; }
-
- private:
- // During shadowing, the currently shadowing target. After
- // shadowing, the target that was shadowed.
- BreakTarget* other_target_;
-
-#ifdef DEBUG
- bool is_shadowing_;
-#endif
-
- DISALLOW_COPY_AND_ASSIGN(ShadowTarget);
-};
-
-} } // namespace v8::internal
-
-#endif // V8_JUMP_TARGET_H_
diff --git a/src/liveedit.cc b/src/liveedit.cc
index dbcf5ef..1466766 100644
--- a/src/liveedit.cc
+++ b/src/liveedit.cc
@@ -1013,8 +1013,8 @@
Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
if (IsJSFunctionCode(shared_info->code())) {
- ReplaceCodeObject(shared_info->code(),
- *(compile_info_wrapper.GetFunctionCode()));
+ Handle<Code> code = compile_info_wrapper.GetFunctionCode();
+ ReplaceCodeObject(shared_info->code(), *code);
Handle<Object> code_scope_info = compile_info_wrapper.GetCodeScopeInfo();
if (code_scope_info->IsFixedArray()) {
shared_info->set_scope_info(SerializedScopeInfo::cast(*code_scope_info));
@@ -1028,8 +1028,10 @@
debug_info->set_original_code(*new_original_code);
}
- shared_info->set_start_position(compile_info_wrapper.GetStartPosition());
- shared_info->set_end_position(compile_info_wrapper.GetEndPosition());
+ int start_position = compile_info_wrapper.GetStartPosition();
+ int end_position = compile_info_wrapper.GetEndPosition();
+ shared_info->set_start_position(start_position);
+ shared_info->set_end_position(end_position);
shared_info->set_construct_stub(
Isolate::Current()->builtins()->builtin(
@@ -1233,13 +1235,14 @@
int old_function_start = info->start_position();
int new_function_start = TranslatePosition(old_function_start,
position_change_array);
- info->set_start_position(new_function_start);
- info->set_end_position(TranslatePosition(info->end_position(),
- position_change_array));
+ int new_function_end = TranslatePosition(info->end_position(),
+ position_change_array);
+ int new_function_token_pos =
+ TranslatePosition(info->function_token_position(), position_change_array);
- info->set_function_token_position(
- TranslatePosition(info->function_token_position(),
- position_change_array));
+ info->set_start_position(new_function_start);
+ info->set_end_position(new_function_end);
+ info->set_function_token_position(new_function_token_pos);
if (IsJSFunctionCode(info->code())) {
// Patch relocation info section of the code.
@@ -1393,17 +1396,18 @@
ASSERT(bottom_js_frame->is_java_script());
// Check the nature of the top frame.
- Code* pre_top_frame_code = pre_top_frame->LookupCode(Isolate::Current());
+ Isolate* isolate = Isolate::Current();
+ Code* pre_top_frame_code = pre_top_frame->LookupCode();
if (pre_top_frame_code->is_inline_cache_stub() &&
pre_top_frame_code->ic_state() == DEBUG_BREAK) {
// OK, we can drop inline cache calls.
*mode = Debug::FRAME_DROPPED_IN_IC_CALL;
} else if (pre_top_frame_code ==
- Isolate::Current()->debug()->debug_break_slot()) {
+ isolate->debug()->debug_break_slot()) {
// OK, we can drop debug break slot.
*mode = Debug::FRAME_DROPPED_IN_DEBUG_SLOT_CALL;
} else if (pre_top_frame_code ==
- Isolate::Current()->builtins()->builtin(
+ isolate->builtins()->builtin(
Builtins::kFrameDropper_LiveEdit)) {
// OK, we can drop our own code.
*mode = Debug::FRAME_DROPPED_IN_DIRECT_CALL;
@@ -1567,8 +1571,8 @@
: shared_info_array_(shared_info_array), result_(result),
has_blocked_functions_(false) {
}
- void VisitThread(ThreadLocalTop* top) {
- for (StackFrameIterator it(top); !it.done(); it.Advance()) {
+ void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
+ for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) {
has_blocked_functions_ |= CheckActivation(
shared_info_array_, result_, it.frame(),
LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK);
diff --git a/src/log.cc b/src/log.cc
index 6a601c6..6d95094 100644
--- a/src/log.cc
+++ b/src/log.cc
@@ -150,6 +150,7 @@
sample->tos = NULL;
sample->frames_count = 0;
+ sample->has_external_callback = false;
// Avoid collecting traces while doing GC.
if (sample->state == GC) return;
@@ -190,7 +191,7 @@
//
class Ticker: public Sampler {
public:
- explicit Ticker(Isolate* isolate, int interval):
+ Ticker(Isolate* isolate, int interval):
Sampler(isolate, interval),
window_(NULL),
profiler_(NULL) {}
@@ -1315,7 +1316,6 @@
case Code::FUNCTION:
case Code::OPTIMIZED_FUNCTION:
return; // We log this later using LogCompiledFunctions.
- case Code::BINARY_OP_IC: // fall through
case Code::TYPE_RECORDING_BINARY_OP_IC: // fall through
case Code::COMPARE_IC: // fall through
case Code::STUB:
diff --git a/src/mark-compact.cc b/src/mark-compact.cc
index 1f73388..bd36459 100644
--- a/src/mark-compact.cc
+++ b/src/mark-compact.cc
@@ -86,15 +86,15 @@
GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_COMPACT);
EncodeForwardingAddresses();
- heap_->MarkMapPointersAsEncoded(true);
+ heap()->MarkMapPointersAsEncoded(true);
UpdatePointers();
- heap_->MarkMapPointersAsEncoded(false);
- heap_->isolate()->pc_to_code_cache()->Flush();
+ heap()->MarkMapPointersAsEncoded(false);
+ heap()->isolate()->pc_to_code_cache()->Flush();
RelocateObjects();
} else {
SweepSpaces();
- heap_->isolate()->pc_to_code_cache()->Flush();
+ heap()->isolate()->pc_to_code_cache()->Flush();
}
Finish();
@@ -123,7 +123,7 @@
compact_on_next_gc_ = false;
if (FLAG_never_compact) compacting_collection_ = false;
- if (!HEAP->map_space()->MapPointersEncodable())
+ if (!heap()->map_space()->MapPointersEncodable())
compacting_collection_ = false;
if (FLAG_collect_maps) CreateBackPointers();
#ifdef ENABLE_GDB_JIT_INTERFACE
@@ -161,9 +161,9 @@
// force lazy re-initialization of it. This must be done after the
// GC, because it relies on the new address of certain old space
// objects (empty string, illegal builtin).
- Isolate::Current()->stub_cache()->Clear();
+ heap()->isolate()->stub_cache()->Clear();
- heap_->external_string_table_.CleanUp();
+ heap()->external_string_table_.CleanUp();
// If we've just compacted old space there's no reason to check the
// fragmentation limit. Just return.
@@ -456,7 +456,7 @@
for (Object** p = start; p < end; p++) MarkObjectByPointer(heap, p);
}
- static inline void VisitCodeTarget(RelocInfo* rinfo) {
+ static inline void VisitCodeTarget(Heap* heap, RelocInfo* rinfo) {
ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
Code* code = Code::GetCodeFromTargetAddress(rinfo->target_address());
if (FLAG_cleanup_ics_at_gc && code->is_inline_cache_stub()) {
@@ -464,48 +464,50 @@
// Please note targets for cleared inline cached do not have to be
// marked since they are contained in HEAP->non_monomorphic_cache().
} else {
- HEAP->mark_compact_collector()->MarkObject(code);
+ heap->mark_compact_collector()->MarkObject(code);
}
}
- static void VisitGlobalPropertyCell(RelocInfo* rinfo) {
+ static void VisitGlobalPropertyCell(Heap* heap, RelocInfo* rinfo) {
ASSERT(rinfo->rmode() == RelocInfo::GLOBAL_PROPERTY_CELL);
Object* cell = rinfo->target_cell();
Object* old_cell = cell;
- VisitPointer(HEAP, &cell);
+ VisitPointer(heap, &cell);
if (cell != old_cell) {
rinfo->set_target_cell(reinterpret_cast<JSGlobalPropertyCell*>(cell));
}
}
- static inline void VisitDebugTarget(RelocInfo* rinfo) {
+ static inline void VisitDebugTarget(Heap* heap, RelocInfo* rinfo) {
ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
rinfo->IsPatchedReturnSequence()) ||
(RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
rinfo->IsPatchedDebugBreakSlotSequence()));
HeapObject* code = Code::GetCodeFromTargetAddress(rinfo->call_address());
- HEAP->mark_compact_collector()->MarkObject(code);
+ heap->mark_compact_collector()->MarkObject(code);
}
// Mark object pointed to by p.
INLINE(static void MarkObjectByPointer(Heap* heap, Object** p)) {
if (!(*p)->IsHeapObject()) return;
HeapObject* object = ShortCircuitConsString(p);
- heap->mark_compact_collector()->MarkObject(object);
+ if (!object->IsMarked()) {
+ heap->mark_compact_collector()->MarkUnmarkedObject(object);
+ }
}
// Visit an unmarked object.
- static inline void VisitUnmarkedObject(HeapObject* obj) {
+ INLINE(static void VisitUnmarkedObject(MarkCompactCollector* collector,
+ HeapObject* obj)) {
#ifdef DEBUG
- ASSERT(HEAP->Contains(obj));
+ ASSERT(Isolate::Current()->heap()->Contains(obj));
ASSERT(!obj->IsMarked());
#endif
Map* map = obj->map();
- MarkCompactCollector* collector = map->heap()->mark_compact_collector();
collector->SetMark(obj);
// Mark the map pointer and the body.
- collector->MarkObject(map);
+ if (!map->IsMarked()) collector->MarkUnmarkedObject(map);
IterateBody(map, obj);
}
@@ -518,12 +520,13 @@
StackLimitCheck check(heap->isolate());
if (check.HasOverflowed()) return false;
+ MarkCompactCollector* collector = heap->mark_compact_collector();
// Visit the unmarked objects.
for (Object** p = start; p < end; p++) {
if (!(*p)->IsHeapObject()) continue;
HeapObject* obj = HeapObject::cast(*p);
if (obj->IsMarked()) continue;
- VisitUnmarkedObject(obj);
+ VisitUnmarkedObject(collector, obj);
}
return true;
}
@@ -561,8 +564,8 @@
// flushed.
static const int kCodeAgeThreshold = 5;
- inline static bool HasSourceCode(SharedFunctionInfo* info) {
- Object* undefined = HEAP->raw_unchecked_undefined_value();
+ inline static bool HasSourceCode(Heap* heap, SharedFunctionInfo* info) {
+ Object* undefined = heap->raw_unchecked_undefined_value();
return (info->script() != undefined) &&
(reinterpret_cast<Script*>(info->script())->source() != undefined);
}
@@ -570,15 +573,15 @@
inline static bool IsCompiled(JSFunction* function) {
return function->unchecked_code() !=
- Isolate::Current()->builtins()->builtin(Builtins::kLazyCompile);
+ function->GetIsolate()->builtins()->builtin(Builtins::kLazyCompile);
}
inline static bool IsCompiled(SharedFunctionInfo* function) {
return function->unchecked_code() !=
- Isolate::Current()->builtins()->builtin(Builtins::kLazyCompile);
+ function->GetIsolate()->builtins()->builtin(Builtins::kLazyCompile);
}
- inline static bool IsFlushable(JSFunction* function) {
+ inline static bool IsFlushable(Heap* heap, JSFunction* function) {
SharedFunctionInfo* shared_info = function->unchecked_shared();
// Code is either on stack, in compilation cache or referenced
@@ -593,10 +596,10 @@
return false;
}
- return IsFlushable(shared_info);
+ return IsFlushable(heap, shared_info);
}
- inline static bool IsFlushable(SharedFunctionInfo* shared_info) {
+ inline static bool IsFlushable(Heap* heap, SharedFunctionInfo* shared_info) {
// Code is either on stack, in compilation cache or referenced
// by optimized version of function.
if (shared_info->unchecked_code()->IsMarked()) {
@@ -606,7 +609,7 @@
// The function must be compiled and have the source code available,
// to be able to recompile it in case we need the function again.
- if (!(shared_info->is_compiled() && HasSourceCode(shared_info))) {
+ if (!(shared_info->is_compiled() && HasSourceCode(heap, shared_info))) {
return false;
}
@@ -638,7 +641,7 @@
static bool FlushCodeForFunction(Heap* heap, JSFunction* function) {
- if (!IsFlushable(function)) return false;
+ if (!IsFlushable(heap, function)) return false;
// This function's code looks flushable. But we have to postpone the
// decision until we see all functions that point to the same
@@ -715,7 +718,7 @@
if (shared->IsInobjectSlackTrackingInProgress()) shared->DetachInitialMap();
if (!known_flush_code_candidate) {
- known_flush_code_candidate = IsFlushable(shared);
+ known_flush_code_candidate = IsFlushable(heap, shared);
if (known_flush_code_candidate) {
heap->mark_compact_collector()->code_flusher()->AddCandidate(shared);
}
@@ -865,16 +868,16 @@
StaticMarkingVisitor::VisitPointers(heap_, start, end);
}
- void VisitCodeTarget(RelocInfo* rinfo) {
- StaticMarkingVisitor::VisitCodeTarget(rinfo);
+ void VisitCodeTarget(Heap* heap, RelocInfo* rinfo) {
+ StaticMarkingVisitor::VisitCodeTarget(heap, rinfo);
}
- void VisitGlobalPropertyCell(RelocInfo* rinfo) {
- StaticMarkingVisitor::VisitGlobalPropertyCell(rinfo);
+ void VisitGlobalPropertyCell(Heap* heap, RelocInfo* rinfo) {
+ StaticMarkingVisitor::VisitGlobalPropertyCell(heap, rinfo);
}
- void VisitDebugTarget(RelocInfo* rinfo) {
- StaticMarkingVisitor::VisitDebugTarget(rinfo);
+ void VisitDebugTarget(Heap* heap, RelocInfo* rinfo) {
+ StaticMarkingVisitor::VisitDebugTarget(heap, rinfo);
}
private:
@@ -887,8 +890,8 @@
explicit CodeMarkingVisitor(MarkCompactCollector* collector)
: collector_(collector) {}
- void VisitThread(ThreadLocalTop* top) {
- for (StackFrameIterator it(top); !it.done(); it.Advance()) {
+ void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
+ for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) {
collector_->MarkObject(it.frame()->unchecked_code());
}
}
@@ -922,7 +925,7 @@
void MarkCompactCollector::PrepareForCodeFlushing() {
- ASSERT(heap_ == Isolate::Current()->heap());
+ ASSERT(heap() == Isolate::Current()->heap());
if (!FLAG_flush_code) {
EnableCodeFlushing(false);
@@ -930,8 +933,8 @@
}
#ifdef ENABLE_DEBUGGER_SUPPORT
- if (heap_->isolate()->debug()->IsLoaded() ||
- heap_->isolate()->debug()->has_break_points()) {
+ if (heap()->isolate()->debug()->IsLoaded() ||
+ heap()->isolate()->debug()->has_break_points()) {
EnableCodeFlushing(false);
return;
}
@@ -940,10 +943,10 @@
// Ensure that empty descriptor array is marked. Method MarkDescriptorArray
// relies on it being marked before any other descriptor array.
- MarkObject(heap_->raw_unchecked_empty_descriptor_array());
+ MarkObject(heap()->raw_unchecked_empty_descriptor_array());
// Make sure we are not referencing the code from the stack.
- ASSERT(this == heap_->mark_compact_collector());
+ ASSERT(this == heap()->mark_compact_collector());
for (StackFrameIterator it; !it.done(); it.Advance()) {
MarkObject(it.frame()->unchecked_code());
}
@@ -951,12 +954,12 @@
// Iterate the archived stacks in all threads to check if
// the code is referenced.
CodeMarkingVisitor code_marking_visitor(this);
- heap_->isolate()->thread_manager()->IterateArchivedThreads(
+ heap()->isolate()->thread_manager()->IterateArchivedThreads(
&code_marking_visitor);
SharedFunctionInfoMarkingVisitor visitor(this);
- heap_->isolate()->compilation_cache()->IterateFunctions(&visitor);
- heap_->isolate()->handle_scope_implementer()->Iterate(&visitor);
+ heap()->isolate()->compilation_cache()->IterateFunctions(&visitor);
+ heap()->isolate()->handle_scope_implementer()->Iterate(&visitor);
ProcessMarkingStack();
}
@@ -1004,7 +1007,8 @@
// Helper class for pruning the symbol table.
class SymbolTableCleaner : public ObjectVisitor {
public:
- SymbolTableCleaner() : pointers_removed_(0) { }
+ explicit SymbolTableCleaner(Heap* heap)
+ : heap_(heap), pointers_removed_(0) { }
virtual void VisitPointers(Object** start, Object** end) {
// Visit all HeapObject pointers in [start, end).
@@ -1016,10 +1020,10 @@
// Since no objects have yet been moved we can safely access the map of
// the object.
if ((*p)->IsExternalString()) {
- HEAP->FinalizeExternalString(String::cast(*p));
+ heap_->FinalizeExternalString(String::cast(*p));
}
// Set the entry to null_value (as deleted).
- *p = HEAP->raw_unchecked_null_value();
+ *p = heap_->raw_unchecked_null_value();
pointers_removed_++;
}
}
@@ -1029,6 +1033,7 @@
return pointers_removed_;
}
private:
+ Heap* heap_;
int pointers_removed_;
};
@@ -1054,7 +1059,7 @@
if (object->IsMap()) {
Map* map = Map::cast(object);
if (FLAG_cleanup_caches_in_maps_at_gc) {
- map->ClearCodeCache(heap_);
+ map->ClearCodeCache(heap());
}
SetMark(map);
if (FLAG_collect_maps &&
@@ -1125,7 +1130,7 @@
void MarkCompactCollector::CreateBackPointers() {
- HeapObjectIterator iterator(HEAP->map_space());
+ HeapObjectIterator iterator(heap()->map_space());
for (HeapObject* next_object = iterator.next();
next_object != NULL; next_object = iterator.next()) {
if (next_object->IsMap()) { // Could also be ByteArray on free list.
@@ -1134,7 +1139,7 @@
map->instance_type() <= JS_FUNCTION_TYPE) {
map->CreateBackPointers();
} else {
- ASSERT(map->instance_descriptors() == HEAP->empty_descriptor_array());
+ ASSERT(map->instance_descriptors() == heap()->empty_descriptor_array());
}
}
}
@@ -1182,11 +1187,11 @@
void MarkCompactCollector::MarkSymbolTable() {
- SymbolTable* symbol_table = heap_->raw_unchecked_symbol_table();
+ SymbolTable* symbol_table = heap()->raw_unchecked_symbol_table();
// Mark the symbol table itself.
SetMark(symbol_table);
// Explicitly mark the prefix.
- MarkingVisitor marker(heap_);
+ MarkingVisitor marker(heap());
symbol_table->IteratePrefix(&marker);
ProcessMarkingStack();
}
@@ -1195,7 +1200,7 @@
void MarkCompactCollector::MarkRoots(RootMarkingVisitor* visitor) {
// Mark the heap roots including global variables, stack variables,
// etc., and all objects reachable from them.
- HEAP->IterateStrongRoots(visitor, VISIT_ONLY_STRONG);
+ heap()->IterateStrongRoots(visitor, VISIT_ONLY_STRONG);
// Handle the symbol table specially.
MarkSymbolTable();
@@ -1210,15 +1215,16 @@
void MarkCompactCollector::MarkObjectGroups() {
List<ObjectGroup*>* object_groups =
- heap_->isolate()->global_handles()->object_groups();
+ heap()->isolate()->global_handles()->object_groups();
+ int last = 0;
for (int i = 0; i < object_groups->length(); i++) {
ObjectGroup* entry = object_groups->at(i);
- if (entry == NULL) continue;
+ ASSERT(entry != NULL);
- List<Object**>& objects = entry->objects_;
+ Object*** objects = entry->objects_;
bool group_marked = false;
- for (int j = 0; j < objects.length(); j++) {
+ for (size_t j = 0; j < entry->length_; j++) {
Object* object = *objects[j];
if (object->IsHeapObject() && HeapObject::cast(object)->IsMarked()) {
group_marked = true;
@@ -1226,48 +1232,54 @@
}
}
- if (!group_marked) continue;
+ if (!group_marked) {
+ (*object_groups)[last++] = entry;
+ continue;
+ }
- // An object in the group is marked, so mark as gray all white heap
- // objects in the group.
- for (int j = 0; j < objects.length(); ++j) {
+ // An object in the group is marked, so mark all heap objects in
+ // the group.
+ for (size_t j = 0; j < entry->length_; ++j) {
if ((*objects[j])->IsHeapObject()) {
MarkObject(HeapObject::cast(*objects[j]));
}
}
- // Once the entire group has been colored gray, set the object group
- // to NULL so it won't be processed again.
- delete entry;
- object_groups->at(i) = NULL;
+ // Once the entire group has been marked, dispose it because it's
+ // not needed anymore.
+ entry->Dispose();
}
+ object_groups->Rewind(last);
}
void MarkCompactCollector::MarkImplicitRefGroups() {
List<ImplicitRefGroup*>* ref_groups =
- heap_->isolate()->global_handles()->implicit_ref_groups();
+ heap()->isolate()->global_handles()->implicit_ref_groups();
+ int last = 0;
for (int i = 0; i < ref_groups->length(); i++) {
ImplicitRefGroup* entry = ref_groups->at(i);
- if (entry == NULL) continue;
+ ASSERT(entry != NULL);
- if (!entry->parent_->IsMarked()) continue;
+ if (!(*entry->parent_)->IsMarked()) {
+ (*ref_groups)[last++] = entry;
+ continue;
+ }
- List<Object**>& children = entry->children_;
- // A parent object is marked, so mark as gray all child white heap
- // objects.
- for (int j = 0; j < children.length(); ++j) {
+ Object*** children = entry->children_;
+ // A parent object is marked, so mark all child heap objects.
+ for (size_t j = 0; j < entry->length_; ++j) {
if ((*children[j])->IsHeapObject()) {
MarkObject(HeapObject::cast(*children[j]));
}
}
- // Once the entire group has been colored gray, set the group
- // to NULL so it won't be processed again.
- delete entry;
- ref_groups->at(i) = NULL;
+ // Once the entire group has been marked, dispose it because it's
+ // not needed anymore.
+ entry->Dispose();
}
+ ref_groups->Rewind(last);
}
@@ -1279,7 +1291,7 @@
while (!marking_stack_.is_empty()) {
HeapObject* object = marking_stack_.Pop();
ASSERT(object->IsHeapObject());
- ASSERT(heap_->Contains(object));
+ ASSERT(heap()->Contains(object));
ASSERT(object->IsMarked());
ASSERT(!object->IsOverflowed());
@@ -1303,32 +1315,32 @@
void MarkCompactCollector::RefillMarkingStack() {
ASSERT(marking_stack_.overflowed());
- SemiSpaceIterator new_it(HEAP->new_space(), &OverflowObjectSize);
+ SemiSpaceIterator new_it(heap()->new_space(), &OverflowObjectSize);
OverflowedObjectsScanner::ScanOverflowedObjects(this, &new_it);
if (marking_stack_.is_full()) return;
- HeapObjectIterator old_pointer_it(HEAP->old_pointer_space(),
+ HeapObjectIterator old_pointer_it(heap()->old_pointer_space(),
&OverflowObjectSize);
OverflowedObjectsScanner::ScanOverflowedObjects(this, &old_pointer_it);
if (marking_stack_.is_full()) return;
- HeapObjectIterator old_data_it(HEAP->old_data_space(), &OverflowObjectSize);
+ HeapObjectIterator old_data_it(heap()->old_data_space(), &OverflowObjectSize);
OverflowedObjectsScanner::ScanOverflowedObjects(this, &old_data_it);
if (marking_stack_.is_full()) return;
- HeapObjectIterator code_it(HEAP->code_space(), &OverflowObjectSize);
+ HeapObjectIterator code_it(heap()->code_space(), &OverflowObjectSize);
OverflowedObjectsScanner::ScanOverflowedObjects(this, &code_it);
if (marking_stack_.is_full()) return;
- HeapObjectIterator map_it(HEAP->map_space(), &OverflowObjectSize);
+ HeapObjectIterator map_it(heap()->map_space(), &OverflowObjectSize);
OverflowedObjectsScanner::ScanOverflowedObjects(this, &map_it);
if (marking_stack_.is_full()) return;
- HeapObjectIterator cell_it(HEAP->cell_space(), &OverflowObjectSize);
+ HeapObjectIterator cell_it(heap()->cell_space(), &OverflowObjectSize);
OverflowedObjectsScanner::ScanOverflowedObjects(this, &cell_it);
if (marking_stack_.is_full()) return;
- LargeObjectIterator lo_it(HEAP->lo_space(), &OverflowObjectSize);
+ LargeObjectIterator lo_it(heap()->lo_space(), &OverflowObjectSize);
OverflowedObjectsScanner::ScanOverflowedObjects(this, &lo_it);
if (marking_stack_.is_full()) return;
@@ -1366,7 +1378,7 @@
// The recursive GC marker detects when it is nearing stack overflow,
// and switches to a different marking system. JS interrupts interfere
// with the C stack limit check.
- PostponeInterruptsScope postpone(heap_->isolate());
+ PostponeInterruptsScope postpone(heap()->isolate());
#ifdef DEBUG
ASSERT(state_ == PREPARE_GC);
@@ -1374,14 +1386,14 @@
#endif
// The to space contains live objects, the from space is used as a marking
// stack.
- marking_stack_.Initialize(heap_->new_space()->FromSpaceLow(),
- heap_->new_space()->FromSpaceHigh());
+ marking_stack_.Initialize(heap()->new_space()->FromSpaceLow(),
+ heap()->new_space()->FromSpaceHigh());
ASSERT(!marking_stack_.overflowed());
PrepareForCodeFlushing();
- RootMarkingVisitor root_visitor(heap_);
+ RootMarkingVisitor root_visitor(heap());
MarkRoots(&root_visitor);
// The objects reachable from the roots are marked, yet unreachable
@@ -1395,10 +1407,10 @@
//
// First we identify nonlive weak handles and mark them as pending
// destruction.
- heap_->isolate()->global_handles()->IdentifyWeakHandles(
+ heap()->isolate()->global_handles()->IdentifyWeakHandles(
&IsUnmarkedHeapObject);
// Then we mark the objects and process the transitive closure.
- heap_->isolate()->global_handles()->IterateWeakRoots(&root_visitor);
+ heap()->isolate()->global_handles()->IterateWeakRoots(&root_visitor);
while (marking_stack_.overflowed()) {
RefillMarkingStack();
EmptyMarkingStack();
@@ -1411,20 +1423,20 @@
// Prune the symbol table removing all symbols only pointed to by the
// symbol table. Cannot use symbol_table() here because the symbol
// table is marked.
- SymbolTable* symbol_table = heap_->raw_unchecked_symbol_table();
- SymbolTableCleaner v;
+ SymbolTable* symbol_table = heap()->raw_unchecked_symbol_table();
+ SymbolTableCleaner v(heap());
symbol_table->IterateElements(&v);
symbol_table->ElementsRemoved(v.PointersRemoved());
- heap_->external_string_table_.Iterate(&v);
- heap_->external_string_table_.CleanUp();
+ heap()->external_string_table_.Iterate(&v);
+ heap()->external_string_table_.CleanUp();
// Process the weak references.
MarkCompactWeakObjectRetainer mark_compact_object_retainer;
- heap_->ProcessWeakReferences(&mark_compact_object_retainer);
+ heap()->ProcessWeakReferences(&mark_compact_object_retainer);
// Remove object groups after marking phase.
- heap_->isolate()->global_handles()->RemoveObjectGroups();
- heap_->isolate()->global_handles()->RemoveImplicitRefGroups();
+ heap()->isolate()->global_handles()->RemoveObjectGroups();
+ heap()->isolate()->global_handles()->RemoveImplicitRefGroups();
// Flush code from collected candidates.
if (is_code_flushing_enabled()) {
@@ -1432,28 +1444,28 @@
}
// Clean up dead objects from the runtime profiler.
- heap_->isolate()->runtime_profiler()->RemoveDeadSamples();
+ heap()->isolate()->runtime_profiler()->RemoveDeadSamples();
}
#ifdef DEBUG
void MarkCompactCollector::UpdateLiveObjectCount(HeapObject* obj) {
live_bytes_ += obj->Size();
- if (HEAP->new_space()->Contains(obj)) {
+ if (heap()->new_space()->Contains(obj)) {
live_young_objects_size_ += obj->Size();
- } else if (HEAP->map_space()->Contains(obj)) {
+ } else if (heap()->map_space()->Contains(obj)) {
ASSERT(obj->IsMap());
live_map_objects_size_ += obj->Size();
- } else if (HEAP->cell_space()->Contains(obj)) {
+ } else if (heap()->cell_space()->Contains(obj)) {
ASSERT(obj->IsJSGlobalPropertyCell());
live_cell_objects_size_ += obj->Size();
- } else if (HEAP->old_pointer_space()->Contains(obj)) {
+ } else if (heap()->old_pointer_space()->Contains(obj)) {
live_old_pointer_objects_size_ += obj->Size();
- } else if (HEAP->old_data_space()->Contains(obj)) {
+ } else if (heap()->old_data_space()->Contains(obj)) {
live_old_data_objects_size_ += obj->Size();
- } else if (HEAP->code_space()->Contains(obj)) {
+ } else if (heap()->code_space()->Contains(obj)) {
live_code_objects_size_ += obj->Size();
- } else if (HEAP->lo_space()->Contains(obj)) {
+ } else if (heap()->lo_space()->Contains(obj)) {
live_lo_objects_size_ += obj->Size();
} else {
UNREACHABLE();
@@ -1469,7 +1481,7 @@
compacting_collection_ ? ENCODE_FORWARDING_ADDRESSES : SWEEP_SPACES;
#endif
// Deallocate unmarked objects and clear marked bits for marked objects.
- HEAP->lo_space()->FreeUnmarkedObjects();
+ heap()->lo_space()->FreeUnmarkedObjects();
}
@@ -1482,7 +1494,7 @@
void MarkCompactCollector::ClearNonLiveTransitions() {
- HeapObjectIterator map_iterator(HEAP->map_space(), &SizeOfMarkedObject);
+ HeapObjectIterator map_iterator(heap() ->map_space(), &SizeOfMarkedObject);
// Iterate over the map space, setting map transitions that go from
// a marked map to an unmarked map to null transitions. At the same time,
// set all the prototype fields of maps back to their original value,
@@ -1532,7 +1544,7 @@
// This test will always be false on the first iteration.
if (on_dead_path && current->IsMarked()) {
on_dead_path = false;
- current->ClearNonLiveTransitions(heap_, real_prototype);
+ current->ClearNonLiveTransitions(heap(), real_prototype);
}
*HeapObject::RawField(current, Map::kPrototypeOffset) =
real_prototype;
@@ -1690,7 +1702,7 @@
// Most non-live objects are ignored.
-inline void IgnoreNonLiveObject(HeapObject* object) {}
+inline void IgnoreNonLiveObject(HeapObject* object, Isolate* isolate) {}
// Function template that, given a range of addresses (eg, a semispace or a
@@ -1744,7 +1756,7 @@
}
} else { // Non-live object.
object_size = object->Size();
- ProcessNonLive(object);
+ ProcessNonLive(object, collector->heap()->isolate());
if (is_prev_alive) { // Transition from live to non-live.
free_start = current;
is_prev_alive = false;
@@ -1767,8 +1779,8 @@
EncodeForwardingAddressInNewSpace,
IgnoreNonLiveObject>(
this,
- heap_->new_space()->bottom(),
- heap_->new_space()->top(),
+ heap()->new_space()->bottom(),
+ heap()->new_space()->top(),
&ignored);
}
@@ -2089,7 +2101,8 @@
is_previous_alive = true;
}
} else {
- heap->mark_compact_collector()->ReportDeleteIfNeeded(object);
+ heap->mark_compact_collector()->ReportDeleteIfNeeded(
+ object, heap->isolate());
if (is_previous_alive) { // Transition from live to free.
free_start = current;
is_previous_alive = false;
@@ -2189,24 +2202,24 @@
// Objects in the active semispace of the young generation may be
// relocated to the inactive semispace (if not promoted). Set the
// relocation info to the beginning of the inactive semispace.
- heap_->new_space()->MCResetRelocationInfo();
+ heap()->new_space()->MCResetRelocationInfo();
// Compute the forwarding pointers in each space.
EncodeForwardingAddressesInPagedSpace<MCAllocateFromOldPointerSpace,
ReportDeleteIfNeeded>(
- heap_->old_pointer_space());
+ heap()->old_pointer_space());
EncodeForwardingAddressesInPagedSpace<MCAllocateFromOldDataSpace,
IgnoreNonLiveObject>(
- heap_->old_data_space());
+ heap()->old_data_space());
EncodeForwardingAddressesInPagedSpace<MCAllocateFromCodeSpace,
ReportDeleteIfNeeded>(
- heap_->code_space());
+ heap()->code_space());
EncodeForwardingAddressesInPagedSpace<MCAllocateFromCellSpace,
IgnoreNonLiveObject>(
- heap_->cell_space());
+ heap()->cell_space());
// Compute new space next to last after the old and code spaces have been
@@ -2218,25 +2231,26 @@
// non-live map pointers to get the sizes of non-live objects.
EncodeForwardingAddressesInPagedSpace<MCAllocateFromMapSpace,
IgnoreNonLiveObject>(
- heap_->map_space());
+ heap()->map_space());
// Write relocation info to the top page, so we can use it later. This is
// done after promoting objects from the new space so we get the correct
// allocation top.
- heap_->old_pointer_space()->MCWriteRelocationInfoToPage();
- heap_->old_data_space()->MCWriteRelocationInfoToPage();
- heap_->code_space()->MCWriteRelocationInfoToPage();
- heap_->map_space()->MCWriteRelocationInfoToPage();
- heap_->cell_space()->MCWriteRelocationInfoToPage();
+ heap()->old_pointer_space()->MCWriteRelocationInfoToPage();
+ heap()->old_data_space()->MCWriteRelocationInfoToPage();
+ heap()->code_space()->MCWriteRelocationInfoToPage();
+ heap()->map_space()->MCWriteRelocationInfoToPage();
+ heap()->cell_space()->MCWriteRelocationInfoToPage();
}
class MapIterator : public HeapObjectIterator {
public:
- MapIterator() : HeapObjectIterator(HEAP->map_space(), &SizeCallback) { }
+ explicit MapIterator(Heap* heap)
+ : HeapObjectIterator(heap->map_space(), &SizeCallback) { }
- explicit MapIterator(Address start)
- : HeapObjectIterator(HEAP->map_space(), start, &SizeCallback) { }
+ MapIterator(Heap* heap, Address start)
+ : HeapObjectIterator(heap->map_space(), start, &SizeCallback) { }
private:
static int SizeCallback(HeapObject* unused) {
@@ -2252,7 +2266,8 @@
: heap_(heap),
live_maps_(live_maps),
to_evacuate_start_(heap->map_space()->TopAfterCompaction(live_maps)),
- map_to_evacuate_it_(to_evacuate_start_),
+ vacant_map_it_(heap),
+ map_to_evacuate_it_(heap, to_evacuate_start_),
first_map_to_evacuate_(
reinterpret_cast<Map*>(HeapObject::FromAddress(to_evacuate_start_))) {
}
@@ -2273,36 +2288,41 @@
void UpdateMapPointersInRoots() {
MapUpdatingVisitor map_updating_visitor;
- heap_->IterateRoots(&map_updating_visitor, VISIT_ONLY_STRONG);
- heap_->isolate()->global_handles()->IterateWeakRoots(&map_updating_visitor);
+ heap()->IterateRoots(&map_updating_visitor, VISIT_ONLY_STRONG);
+ heap()->isolate()->global_handles()->IterateWeakRoots(
+ &map_updating_visitor);
LiveObjectList::IterateElements(&map_updating_visitor);
}
void UpdateMapPointersInPagedSpace(PagedSpace* space) {
- ASSERT(space != heap_->map_space());
+ ASSERT(space != heap()->map_space());
PageIterator it(space, PageIterator::PAGES_IN_USE);
while (it.has_next()) {
Page* p = it.next();
- UpdateMapPointersInRange(heap_, p->ObjectAreaStart(), p->AllocationTop());
+ UpdateMapPointersInRange(heap(),
+ p->ObjectAreaStart(),
+ p->AllocationTop());
}
}
void UpdateMapPointersInNewSpace() {
- NewSpace* space = heap_->new_space();
- UpdateMapPointersInRange(heap_, space->bottom(), space->top());
+ NewSpace* space = heap()->new_space();
+ UpdateMapPointersInRange(heap(), space->bottom(), space->top());
}
void UpdateMapPointersInLargeObjectSpace() {
- LargeObjectIterator it(heap_->lo_space());
+ LargeObjectIterator it(heap()->lo_space());
for (HeapObject* obj = it.next(); obj != NULL; obj = it.next())
- UpdateMapPointersInObject(heap_, obj);
+ UpdateMapPointersInObject(heap(), obj);
}
void Finish() {
- heap_->map_space()->FinishCompaction(to_evacuate_start_, live_maps_);
+ heap()->map_space()->FinishCompaction(to_evacuate_start_, live_maps_);
}
+ inline Heap* heap() const { return heap_; }
+
private:
Heap* heap_;
int live_maps_;
@@ -2452,26 +2472,26 @@
// the map space last because freeing non-live maps overwrites them and
// the other spaces rely on possibly non-live maps to get the sizes for
// non-live objects.
- SweepSpace(heap_, heap_->old_pointer_space());
- SweepSpace(heap_, heap_->old_data_space());
- SweepSpace(heap_, heap_->code_space());
- SweepSpace(heap_, heap_->cell_space());
+ SweepSpace(heap(), heap()->old_pointer_space());
+ SweepSpace(heap(), heap()->old_data_space());
+ SweepSpace(heap(), heap()->code_space());
+ SweepSpace(heap(), heap()->cell_space());
{ GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_SWEEP_NEWSPACE);
- SweepNewSpace(heap_, heap_->new_space());
+ SweepNewSpace(heap(), heap()->new_space());
}
- SweepSpace(heap_, heap_->map_space());
+ SweepSpace(heap(), heap()->map_space());
- heap_->IterateDirtyRegions(heap_->map_space(),
- &heap_->IteratePointersInDirtyMapsRegion,
+ heap()->IterateDirtyRegions(heap()->map_space(),
+ &heap()->IteratePointersInDirtyMapsRegion,
&UpdatePointerToNewGen,
- heap_->WATERMARK_SHOULD_BE_VALID);
+ heap()->WATERMARK_SHOULD_BE_VALID);
- intptr_t live_maps_size = heap_->map_space()->Size();
+ intptr_t live_maps_size = heap()->map_space()->Size();
int live_maps = static_cast<int>(live_maps_size / Map::kSize);
ASSERT(live_map_objects_size_ == live_maps_size);
- if (heap_->map_space()->NeedsCompaction(live_maps)) {
- MapCompact map_compact(heap_, live_maps);
+ if (heap()->map_space()->NeedsCompaction(live_maps)) {
+ MapCompact map_compact(heap(), live_maps);
map_compact.CompactMaps();
map_compact.UpdateMapPointersInRoots();
@@ -2479,7 +2499,7 @@
PagedSpaces spaces;
for (PagedSpace* space = spaces.next();
space != NULL; space = spaces.next()) {
- if (space == heap_->map_space()) continue;
+ if (space == heap()->map_space()) continue;
map_compact.UpdateMapPointersInPagedSpace(space);
}
map_compact.UpdateMapPointersInNewSpace();
@@ -2575,6 +2595,8 @@
reinterpret_cast<Code*>(target)->instruction_start());
}
+ inline Heap* heap() const { return heap_; }
+
private:
void UpdatePointer(Object** p) {
if (!(*p)->IsHeapObject()) return;
@@ -2582,27 +2604,27 @@
HeapObject* obj = HeapObject::cast(*p);
Address old_addr = obj->address();
Address new_addr;
- ASSERT(!heap_->InFromSpace(obj));
+ ASSERT(!heap()->InFromSpace(obj));
- if (heap_->new_space()->Contains(obj)) {
+ if (heap()->new_space()->Contains(obj)) {
Address forwarding_pointer_addr =
- heap_->new_space()->FromSpaceLow() +
- heap_->new_space()->ToSpaceOffsetForAddress(old_addr);
+ heap()->new_space()->FromSpaceLow() +
+ heap()->new_space()->ToSpaceOffsetForAddress(old_addr);
new_addr = Memory::Address_at(forwarding_pointer_addr);
#ifdef DEBUG
- ASSERT(heap_->old_pointer_space()->Contains(new_addr) ||
- heap_->old_data_space()->Contains(new_addr) ||
- heap_->new_space()->FromSpaceContains(new_addr) ||
- heap_->lo_space()->Contains(HeapObject::FromAddress(new_addr)));
+ ASSERT(heap()->old_pointer_space()->Contains(new_addr) ||
+ heap()->old_data_space()->Contains(new_addr) ||
+ heap()->new_space()->FromSpaceContains(new_addr) ||
+ heap()->lo_space()->Contains(HeapObject::FromAddress(new_addr)));
- if (heap_->new_space()->FromSpaceContains(new_addr)) {
- ASSERT(heap_->new_space()->FromSpaceOffsetForAddress(new_addr) <=
- heap_->new_space()->ToSpaceOffsetForAddress(old_addr));
+ if (heap()->new_space()->FromSpaceContains(new_addr)) {
+ ASSERT(heap()->new_space()->FromSpaceOffsetForAddress(new_addr) <=
+ heap()->new_space()->ToSpaceOffsetForAddress(old_addr));
}
#endif
- } else if (heap_->lo_space()->Contains(obj)) {
+ } else if (heap()->lo_space()->Contains(obj)) {
// Don't move objects in the large object space.
return;
@@ -2641,34 +2663,34 @@
ASSERT(state_ == ENCODE_FORWARDING_ADDRESSES);
state_ = UPDATE_POINTERS;
#endif
- UpdatingVisitor updating_visitor(heap_);
- heap_->isolate()->runtime_profiler()->UpdateSamplesAfterCompact(
+ UpdatingVisitor updating_visitor(heap());
+ heap()->isolate()->runtime_profiler()->UpdateSamplesAfterCompact(
&updating_visitor);
- heap_->IterateRoots(&updating_visitor, VISIT_ONLY_STRONG);
- heap_->isolate()->global_handles()->IterateWeakRoots(&updating_visitor);
+ heap()->IterateRoots(&updating_visitor, VISIT_ONLY_STRONG);
+ heap()->isolate()->global_handles()->IterateWeakRoots(&updating_visitor);
// Update the pointer to the head of the weak list of global contexts.
- updating_visitor.VisitPointer(&heap_->global_contexts_list_);
+ updating_visitor.VisitPointer(&heap()->global_contexts_list_);
LiveObjectList::IterateElements(&updating_visitor);
int live_maps_size = IterateLiveObjects(
- heap_->map_space(), &MarkCompactCollector::UpdatePointersInOldObject);
+ heap()->map_space(), &MarkCompactCollector::UpdatePointersInOldObject);
int live_pointer_olds_size = IterateLiveObjects(
- heap_->old_pointer_space(),
+ heap()->old_pointer_space(),
&MarkCompactCollector::UpdatePointersInOldObject);
int live_data_olds_size = IterateLiveObjects(
- heap_->old_data_space(),
+ heap()->old_data_space(),
&MarkCompactCollector::UpdatePointersInOldObject);
int live_codes_size = IterateLiveObjects(
- heap_->code_space(), &MarkCompactCollector::UpdatePointersInOldObject);
+ heap()->code_space(), &MarkCompactCollector::UpdatePointersInOldObject);
int live_cells_size = IterateLiveObjects(
- heap_->cell_space(), &MarkCompactCollector::UpdatePointersInOldObject);
+ heap()->cell_space(), &MarkCompactCollector::UpdatePointersInOldObject);
int live_news_size = IterateLiveObjects(
- heap_->new_space(), &MarkCompactCollector::UpdatePointersInNewObject);
+ heap()->new_space(), &MarkCompactCollector::UpdatePointersInNewObject);
// Large objects do not move, the map word can be updated directly.
- LargeObjectIterator it(heap_->lo_space());
+ LargeObjectIterator it(heap()->lo_space());
for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) {
UpdatePointersInNewObject(obj);
}
@@ -2695,8 +2717,8 @@
Address forwarded = GetForwardingAddressInOldSpace(old_map);
- ASSERT(heap_->map_space()->Contains(old_map));
- ASSERT(heap_->map_space()->Contains(forwarded));
+ ASSERT(heap()->map_space()->Contains(old_map));
+ ASSERT(heap()->map_space()->Contains(forwarded));
#ifdef DEBUG
if (FLAG_gc_verbose) {
PrintF("update %p : %p -> %p\n", obj->address(), old_map->address(),
@@ -2711,7 +2733,7 @@
int obj_size = obj->SizeFromMap(old_map);
// Update pointers in the object body.
- UpdatingVisitor updating_visitor(heap_);
+ UpdatingVisitor updating_visitor(heap());
obj->IterateBody(old_map->instance_type(), obj_size, &updating_visitor);
return obj_size;
}
@@ -2720,8 +2742,8 @@
int MarkCompactCollector::UpdatePointersInOldObject(HeapObject* obj) {
// Decode the map pointer.
MapWord encoding = obj->map_word();
- Address map_addr = encoding.DecodeMapAddress(heap_->map_space());
- ASSERT(heap_->map_space()->Contains(HeapObject::FromAddress(map_addr)));
+ Address map_addr = encoding.DecodeMapAddress(heap()->map_space());
+ ASSERT(heap()->map_space()->Contains(HeapObject::FromAddress(map_addr)));
// At this point, the first word of map_addr is also encoded, cannot
// cast it to Map* using Map::cast.
@@ -2742,7 +2764,7 @@
#endif
// Update pointers in the object body.
- UpdatingVisitor updating_visitor(heap_);
+ UpdatingVisitor updating_visitor(heap());
obj->IterateBody(type, obj_size, &updating_visitor);
return obj_size;
}
@@ -2799,18 +2821,18 @@
// Relocates objects, always relocate map objects first. Relocating
// objects in other space relies on map objects to get object size.
int live_maps_size = IterateLiveObjects(
- heap_->map_space(), &MarkCompactCollector::RelocateMapObject);
+ heap()->map_space(), &MarkCompactCollector::RelocateMapObject);
int live_pointer_olds_size = IterateLiveObjects(
- heap_->old_pointer_space(),
+ heap()->old_pointer_space(),
&MarkCompactCollector::RelocateOldPointerObject);
int live_data_olds_size = IterateLiveObjects(
- heap_->old_data_space(), &MarkCompactCollector::RelocateOldDataObject);
+ heap()->old_data_space(), &MarkCompactCollector::RelocateOldDataObject);
int live_codes_size = IterateLiveObjects(
- heap_->code_space(), &MarkCompactCollector::RelocateCodeObject);
+ heap()->code_space(), &MarkCompactCollector::RelocateCodeObject);
int live_cells_size = IterateLiveObjects(
- heap_->cell_space(), &MarkCompactCollector::RelocateCellObject);
+ heap()->cell_space(), &MarkCompactCollector::RelocateCellObject);
int live_news_size = IterateLiveObjects(
- heap_->new_space(), &MarkCompactCollector::RelocateNewObject);
+ heap()->new_space(), &MarkCompactCollector::RelocateNewObject);
USE(live_maps_size);
USE(live_pointer_olds_size);
@@ -2826,28 +2848,28 @@
ASSERT(live_news_size == live_young_objects_size_);
// Flip from and to spaces
- heap_->new_space()->Flip();
+ heap()->new_space()->Flip();
- heap_->new_space()->MCCommitRelocationInfo();
+ heap()->new_space()->MCCommitRelocationInfo();
// Set age_mark to bottom in to space
- Address mark = heap_->new_space()->bottom();
- heap_->new_space()->set_age_mark(mark);
+ Address mark = heap()->new_space()->bottom();
+ heap()->new_space()->set_age_mark(mark);
PagedSpaces spaces;
for (PagedSpace* space = spaces.next(); space != NULL; space = spaces.next())
space->MCCommitRelocationInfo();
- heap_->CheckNewSpaceExpansionCriteria();
- heap_->IncrementYoungSurvivorsCounter(live_news_size);
+ heap()->CheckNewSpaceExpansionCriteria();
+ heap()->IncrementYoungSurvivorsCounter(live_news_size);
}
int MarkCompactCollector::RelocateMapObject(HeapObject* obj) {
// Recover map pointer.
MapWord encoding = obj->map_word();
- Address map_addr = encoding.DecodeMapAddress(heap_->map_space());
- ASSERT(heap_->map_space()->Contains(HeapObject::FromAddress(map_addr)));
+ Address map_addr = encoding.DecodeMapAddress(heap()->map_space());
+ ASSERT(heap()->map_space()->Contains(HeapObject::FromAddress(map_addr)));
// Get forwarding address before resetting map pointer
Address new_addr = GetForwardingAddressInOldSpace(obj);
@@ -2860,7 +2882,7 @@
if (new_addr != old_addr) {
// Move contents.
- heap_->MoveBlockToOldSpaceAndUpdateRegionMarks(new_addr,
+ heap()->MoveBlockToOldSpaceAndUpdateRegionMarks(new_addr,
old_addr,
Map::kSize);
}
@@ -2906,8 +2928,8 @@
PagedSpace* space) {
// Recover map pointer.
MapWord encoding = obj->map_word();
- Address map_addr = encoding.DecodeMapAddress(heap_->map_space());
- ASSERT(heap_->map_space()->Contains(map_addr));
+ Address map_addr = encoding.DecodeMapAddress(heap()->map_space());
+ ASSERT(heap()->map_space()->Contains(map_addr));
// Get forwarding address before resetting map pointer.
Address new_addr = GetForwardingAddressInOldSpace(obj);
@@ -2919,10 +2941,10 @@
if (new_addr != old_addr) {
// Move contents.
- if (space == heap_->old_data_space()) {
- heap_->MoveBlock(new_addr, old_addr, obj_size);
+ if (space == heap()->old_data_space()) {
+ heap()->MoveBlock(new_addr, old_addr, obj_size);
} else {
- heap_->MoveBlockToOldSpaceAndUpdateRegionMarks(new_addr,
+ heap()->MoveBlockToOldSpaceAndUpdateRegionMarks(new_addr,
old_addr,
obj_size);
}
@@ -2932,47 +2954,47 @@
HeapObject* copied_to = HeapObject::FromAddress(new_addr);
if (copied_to->IsSharedFunctionInfo()) {
- PROFILE(heap_->isolate(),
+ PROFILE(heap()->isolate(),
SharedFunctionInfoMoveEvent(old_addr, new_addr));
}
- HEAP_PROFILE(heap_, ObjectMoveEvent(old_addr, new_addr));
+ HEAP_PROFILE(heap(), ObjectMoveEvent(old_addr, new_addr));
return obj_size;
}
int MarkCompactCollector::RelocateOldPointerObject(HeapObject* obj) {
- return RelocateOldNonCodeObject(obj, heap_->old_pointer_space());
+ return RelocateOldNonCodeObject(obj, heap()->old_pointer_space());
}
int MarkCompactCollector::RelocateOldDataObject(HeapObject* obj) {
- return RelocateOldNonCodeObject(obj, heap_->old_data_space());
+ return RelocateOldNonCodeObject(obj, heap()->old_data_space());
}
int MarkCompactCollector::RelocateCellObject(HeapObject* obj) {
- return RelocateOldNonCodeObject(obj, heap_->cell_space());
+ return RelocateOldNonCodeObject(obj, heap()->cell_space());
}
int MarkCompactCollector::RelocateCodeObject(HeapObject* obj) {
// Recover map pointer.
MapWord encoding = obj->map_word();
- Address map_addr = encoding.DecodeMapAddress(heap_->map_space());
- ASSERT(heap_->map_space()->Contains(HeapObject::FromAddress(map_addr)));
+ Address map_addr = encoding.DecodeMapAddress(heap()->map_space());
+ ASSERT(heap()->map_space()->Contains(HeapObject::FromAddress(map_addr)));
// Get forwarding address before resetting map pointer
Address new_addr = GetForwardingAddressInOldSpace(obj);
// Reset the map pointer.
- int obj_size = RestoreMap(obj, heap_->code_space(), new_addr, map_addr);
+ int obj_size = RestoreMap(obj, heap()->code_space(), new_addr, map_addr);
Address old_addr = obj->address();
if (new_addr != old_addr) {
// Move contents.
- heap_->MoveBlock(new_addr, old_addr, obj_size);
+ heap()->MoveBlock(new_addr, old_addr, obj_size);
}
HeapObject* copied_to = HeapObject::FromAddress(new_addr);
@@ -2980,9 +3002,9 @@
// May also update inline cache target.
Code::cast(copied_to)->Relocate(new_addr - old_addr);
// Notify the logger that compiled code has moved.
- PROFILE(heap_->isolate(), CodeMoveEvent(old_addr, new_addr));
+ PROFILE(heap()->isolate(), CodeMoveEvent(old_addr, new_addr));
}
- HEAP_PROFILE(heap_, ObjectMoveEvent(old_addr, new_addr));
+ HEAP_PROFILE(heap(), ObjectMoveEvent(old_addr, new_addr));
return obj_size;
}
@@ -2993,26 +3015,26 @@
// Get forwarding address
Address old_addr = obj->address();
- int offset = heap_->new_space()->ToSpaceOffsetForAddress(old_addr);
+ int offset = heap()->new_space()->ToSpaceOffsetForAddress(old_addr);
Address new_addr =
- Memory::Address_at(heap_->new_space()->FromSpaceLow() + offset);
+ Memory::Address_at(heap()->new_space()->FromSpaceLow() + offset);
#ifdef DEBUG
- if (heap_->new_space()->FromSpaceContains(new_addr)) {
- ASSERT(heap_->new_space()->FromSpaceOffsetForAddress(new_addr) <=
- heap_->new_space()->ToSpaceOffsetForAddress(old_addr));
+ if (heap()->new_space()->FromSpaceContains(new_addr)) {
+ ASSERT(heap()->new_space()->FromSpaceOffsetForAddress(new_addr) <=
+ heap()->new_space()->ToSpaceOffsetForAddress(old_addr));
} else {
- ASSERT(heap_->TargetSpace(obj) == heap_->old_pointer_space() ||
- heap_->TargetSpace(obj) == heap_->old_data_space());
+ ASSERT(heap()->TargetSpace(obj) == heap()->old_pointer_space() ||
+ heap()->TargetSpace(obj) == heap()->old_data_space());
}
#endif
// New and old addresses cannot overlap.
- if (heap_->InNewSpace(HeapObject::FromAddress(new_addr))) {
- heap_->CopyBlock(new_addr, old_addr, obj_size);
+ if (heap()->InNewSpace(HeapObject::FromAddress(new_addr))) {
+ heap()->CopyBlock(new_addr, old_addr, obj_size);
} else {
- heap_->CopyBlockToOldSpaceAndUpdateRegionMarks(new_addr,
+ heap()->CopyBlockToOldSpaceAndUpdateRegionMarks(new_addr,
old_addr,
obj_size);
}
@@ -3025,10 +3047,10 @@
HeapObject* copied_to = HeapObject::FromAddress(new_addr);
if (copied_to->IsSharedFunctionInfo()) {
- PROFILE(heap_->isolate(),
+ PROFILE(heap()->isolate(),
SharedFunctionInfoMoveEvent(old_addr, new_addr));
}
- HEAP_PROFILE(heap_, ObjectMoveEvent(old_addr, new_addr));
+ HEAP_PROFILE(heap(), ObjectMoveEvent(old_addr, new_addr));
return obj_size;
}
@@ -3037,7 +3059,7 @@
void MarkCompactCollector::EnableCodeFlushing(bool enable) {
if (enable) {
if (code_flusher_ != NULL) return;
- code_flusher_ = new CodeFlusher(heap_->isolate());
+ code_flusher_ = new CodeFlusher(heap()->isolate());
} else {
if (code_flusher_ == NULL) return;
delete code_flusher_;
@@ -3046,7 +3068,8 @@
}
-void MarkCompactCollector::ReportDeleteIfNeeded(HeapObject* obj) {
+void MarkCompactCollector::ReportDeleteIfNeeded(HeapObject* obj,
+ Isolate* isolate) {
#ifdef ENABLE_GDB_JIT_INTERFACE
if (obj->IsCode()) {
GDBJITInterface::RemoveCode(reinterpret_cast<Code*>(obj));
@@ -3054,7 +3077,7 @@
#endif
#ifdef ENABLE_LOGGING_AND_PROFILING
if (obj->IsCode()) {
- PROFILE(ISOLATE, CodeDeleteEvent(obj->address()));
+ PROFILE(isolate, CodeDeleteEvent(obj->address()));
}
#endif
}
diff --git a/src/mark-compact.h b/src/mark-compact.h
index 3c9d28b..04d0ff6 100644
--- a/src/mark-compact.h
+++ b/src/mark-compact.h
@@ -98,8 +98,6 @@
// -------------------------------------------------------------------------
// Mark-Compact collector
-//
-// All methods are static.
class OverflowedObjectsScanner;
@@ -129,7 +127,7 @@
int* offset);
// Type of functions to process non-live objects.
- typedef void (*ProcessNonLiveFunction)(HeapObject* object);
+ typedef void (*ProcessNonLiveFunction)(HeapObject* object, Isolate* isolate);
// Pointer to member function, used in IterateLiveObjects.
typedef int (MarkCompactCollector::*LiveObjectCallback)(HeapObject* obj);
@@ -179,7 +177,7 @@
#endif
// Determine type of object and emit deletion log event.
- static void ReportDeleteIfNeeded(HeapObject* obj);
+ static void ReportDeleteIfNeeded(HeapObject* obj, Isolate* isolate);
// Returns size of a possibly marked object.
static int SizeOfMarkedObject(HeapObject* obj);
diff --git a/src/messages.cc b/src/messages.cc
index cab982c..abc2537 100644
--- a/src/messages.cc
+++ b/src/messages.cc
@@ -56,11 +56,6 @@
}
-void MessageHandler::ReportMessage(const char* msg) {
- PrintF("%s\n", msg);
-}
-
-
Handle<JSMessageObject> MessageHandler::MakeMessageObject(
const char* type,
MessageLocation* loc,
@@ -106,14 +101,25 @@
}
-void MessageHandler::ReportMessage(MessageLocation* loc,
+void MessageHandler::ReportMessage(Isolate* isolate,
+ MessageLocation* loc,
Handle<Object> message) {
+ // We are calling into embedder's code which can throw exceptions.
+ // Thus we need to save current exception state, reset it to the clean one
+ // and ignore scheduled exceptions callbacks can throw.
+ Isolate::ExceptionScope exception_scope(isolate);
+ isolate->clear_pending_exception();
+ isolate->set_external_caught_exception(false);
+
v8::Local<v8::Message> api_message_obj = v8::Utils::MessageToLocal(message);
v8::NeanderArray global_listeners(FACTORY->message_listeners());
int global_length = global_listeners.length();
if (global_length == 0) {
DefaultMessageReport(loc, message);
+ if (isolate->has_scheduled_exception()) {
+ isolate->clear_scheduled_exception();
+ }
} else {
for (int i = 0; i < global_length; i++) {
HandleScope scope;
@@ -123,7 +129,14 @@
v8::MessageCallback callback =
FUNCTION_CAST<v8::MessageCallback>(callback_obj->proxy());
Handle<Object> callback_data(listener.get(1));
- callback(api_message_obj, v8::Utils::ToLocal(callback_data));
+ {
+ // Do not allow exceptions to propagate.
+ v8::TryCatch tryCatch;
+ callback(api_message_obj, v8::Utils::ToLocal(callback_data));
+ }
+ if (isolate->has_scheduled_exception()) {
+ isolate->clear_scheduled_exception();
+ }
}
}
}
diff --git a/src/messages.h b/src/messages.h
index 48f3244..fc2162d 100644
--- a/src/messages.h
+++ b/src/messages.h
@@ -89,9 +89,6 @@
// of message listeners registered in an environment
class MessageHandler {
public:
- // Report a message (w/o JS heap allocation).
- static void ReportMessage(const char* msg);
-
// Returns a message object for the API to use.
static Handle<JSMessageObject> MakeMessageObject(
const char* type,
@@ -101,7 +98,9 @@
Handle<JSArray> stack_frames);
// Report a formatted message (needs JS allocation).
- static void ReportMessage(MessageLocation* loc, Handle<Object> message);
+ static void ReportMessage(Isolate* isolate,
+ MessageLocation* loc,
+ Handle<Object> message);
static void DefaultMessageReport(const MessageLocation* loc,
Handle<Object> message_obj);
diff --git a/src/messages.js b/src/messages.js
index 3eb056f..e657fc0 100644
--- a/src/messages.js
+++ b/src/messages.js
@@ -190,6 +190,7 @@
property_desc_object: ["Property description must be an object: ", "%0"],
redefine_disallowed: ["Cannot redefine property: ", "%0"],
define_disallowed: ["Cannot define property, object is not extensible: ", "%0"],
+ non_extensible_proto: ["%0", " is not extensible"],
// RangeError
invalid_array_length: ["Invalid array length"],
stack_overflow: ["Maximum call stack size exceeded"],
diff --git a/src/mips/frames-mips.h b/src/mips/frames-mips.h
index 6441470..f507590 100644
--- a/src/mips/frames-mips.h
+++ b/src/mips/frames-mips.h
@@ -147,7 +147,7 @@
public:
// FP-relative.
static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset;
- static const int kSavedRegistersOffset = +2 * kPointerSize;
+ static const int kLastParameterOffset = +2 * kPointerSize;
static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset;
// Caller SP-relative.
diff --git a/src/mips/virtual-frame-mips.h b/src/mips/virtual-frame-mips.h
index be8b74e..cf30b09 100644
--- a/src/mips/virtual-frame-mips.h
+++ b/src/mips/virtual-frame-mips.h
@@ -106,7 +106,7 @@
inline VirtualFrame();
// Construct an invalid virtual frame, used by JumpTargets.
- inline VirtualFrame(InvalidVirtualFrameInitializer* dummy);
+ explicit inline VirtualFrame(InvalidVirtualFrameInitializer* dummy);
// Construct a virtual frame as a clone of an existing one.
explicit inline VirtualFrame(VirtualFrame* original);
diff --git a/src/natives.h b/src/natives.h
index 639a2d3..92f0d90 100644
--- a/src/natives.h
+++ b/src/natives.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -36,7 +36,7 @@
int index);
enum NativeType {
- CORE, D8
+ CORE, EXPERIMENTAL, D8, I18N
};
template <NativeType type>
@@ -57,6 +57,7 @@
};
typedef NativesCollection<CORE> Natives;
+typedef NativesCollection<EXPERIMENTAL> ExperimentalNatives;
} } // namespace v8::internal
diff --git a/src/objects-inl.h b/src/objects-inl.h
index 5395bbb..823b2da 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -1774,7 +1774,7 @@
void DescriptorArray::Get(int descriptor_number, Descriptor* desc) {
desc->Init(GetKey(descriptor_number),
GetValue(descriptor_number),
- GetDetails(descriptor_number));
+ PropertyDetails(GetDetails(descriptor_number)));
}
@@ -2573,7 +2573,6 @@
int Code::major_key() {
ASSERT(kind() == STUB ||
- kind() == BINARY_OP_IC ||
kind() == TYPE_RECORDING_BINARY_OP_IC ||
kind() == COMPARE_IC);
return READ_BYTE_FIELD(this, kStubMajorKeyOffset);
@@ -2582,7 +2581,6 @@
void Code::set_major_key(int major) {
ASSERT(kind() == STUB ||
- kind() == BINARY_OP_IC ||
kind() == TYPE_RECORDING_BINARY_OP_IC ||
kind() == COMPARE_IC);
ASSERT(0 <= major && major < 256);
@@ -2691,18 +2689,6 @@
}
-byte Code::binary_op_type() {
- ASSERT(is_binary_op_stub());
- return READ_BYTE_FIELD(this, kBinaryOpTypeOffset);
-}
-
-
-void Code::set_binary_op_type(byte value) {
- ASSERT(is_binary_op_stub());
- WRITE_BYTE_FIELD(this, kBinaryOpTypeOffset, value);
-}
-
-
byte Code::type_recording_binary_op_type() {
ASSERT(is_type_recording_binary_op_stub());
return READ_BYTE_FIELD(this, kBinaryOpTypeOffset);
@@ -2862,6 +2848,34 @@
}
+Heap* Code::heap() {
+ // NOTE: address() helper is not used to save one instruction.
+ Heap* heap = Page::FromAddress(reinterpret_cast<Address>(this))->heap_;
+ ASSERT(heap != NULL);
+ ASSERT(heap->isolate() == Isolate::Current());
+ return heap;
+}
+
+
+Isolate* Code::isolate() {
+ return heap()->isolate();
+}
+
+
+Heap* JSGlobalPropertyCell::heap() {
+ // NOTE: address() helper is not used to save one instruction.
+ Heap* heap = Page::FromAddress(reinterpret_cast<Address>(this))->heap_;
+ ASSERT(heap != NULL);
+ ASSERT(heap->isolate() == Isolate::Current());
+ return heap;
+}
+
+
+Isolate* JSGlobalPropertyCell::isolate() {
+ return heap()->isolate();
+}
+
+
Object* Code::GetObjectFromEntryAddress(Address location_of_address) {
return HeapObject::
FromAddress(Memory::Address_at(location_of_address) - Code::kHeaderSize);
@@ -3028,10 +3042,6 @@
kHasOnlySimpleThisPropertyAssignments)
BOOL_ACCESSORS(SharedFunctionInfo,
compiler_hints,
- try_full_codegen,
- kTryFullCodegen)
-BOOL_ACCESSORS(SharedFunctionInfo,
- compiler_hints,
allows_lazy_compilation,
kAllowLazyCompilation)
@@ -3299,6 +3309,11 @@
}
+bool JSFunction::IsOptimizable() {
+ return code()->kind() == Code::FUNCTION && code()->optimizable();
+}
+
+
bool JSFunction::IsMarkedForLazyRecompilation() {
return code() == GetIsolate()->builtins()->builtin(Builtins::kLazyRecompile);
}
@@ -3929,6 +3944,15 @@
set_flag(Smi::FromInt(rest_value | AttributesField::encode(attributes)));
}
+
+template<typename Shape, typename Key>
+void Dictionary<Shape, Key>::SetEntry(int entry,
+ Object* key,
+ Object* value) {
+ SetEntry(entry, key, value, PropertyDetails(Smi::FromInt(0)));
+}
+
+
template<typename Shape, typename Key>
void Dictionary<Shape, Key>::SetEntry(int entry,
Object* key,
diff --git a/src/objects-visiting.h b/src/objects-visiting.h
index 42f9060..da955da 100644
--- a/src/objects-visiting.h
+++ b/src/objects-visiting.h
@@ -141,13 +141,22 @@
template<typename Callback>
class VisitorDispatchTable {
public:
+ void CopyFrom(VisitorDispatchTable* other) {
+ // We are not using memcpy to guarantee that during update
+ // every element of callbacks_ array will remain correct
+ // pointer (memcpy might be implemented as a byte copying loop).
+ for (int i = 0; i < StaticVisitorBase::kVisitorIdCount; i++) {
+ NoBarrier_Store(&callbacks_[i], other->callbacks_[i]);
+ }
+ }
+
inline Callback GetVisitor(Map* map) {
- return callbacks_[map->visitor_id()];
+ return reinterpret_cast<Callback>(callbacks_[map->visitor_id()]);
}
void Register(StaticVisitorBase::VisitorId id, Callback callback) {
ASSERT(id < StaticVisitorBase::kVisitorIdCount); // id is unsigned.
- callbacks_[id] = callback;
+ callbacks_[id] = reinterpret_cast<AtomicWord>(callback);
}
template<typename Visitor,
@@ -179,7 +188,7 @@
}
private:
- Callback callbacks_[StaticVisitorBase::kVisitorIdCount];
+ AtomicWord callbacks_[StaticVisitorBase::kVisitorIdCount];
};
diff --git a/src/objects.cc b/src/objects.cc
index 8cb36e9..6ce4c44 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -113,39 +113,47 @@
if (IsSmi()) {
return Isolate::Current()->heap()->ToBoolean(Smi::cast(this)->value() != 0);
}
- if (IsUndefined() || IsNull()) {
- return HeapObject::cast(this)->GetHeap()->false_value();
+ HeapObject* heap_object = HeapObject::cast(this);
+ if (heap_object->IsUndefined() || heap_object->IsNull()) {
+ return heap_object->GetHeap()->false_value();
}
// Undetectable object is false
- if (IsUndetectableObject()) {
- return HeapObject::cast(this)->GetHeap()->false_value();
+ if (heap_object->IsUndetectableObject()) {
+ return heap_object->GetHeap()->false_value();
}
- if (IsString()) {
- return HeapObject::cast(this)->GetHeap()->ToBoolean(
+ if (heap_object->IsString()) {
+ return heap_object->GetHeap()->ToBoolean(
String::cast(this)->length() != 0);
}
- if (IsHeapNumber()) {
+ if (heap_object->IsHeapNumber()) {
return HeapNumber::cast(this)->HeapNumberToBoolean();
}
- return Isolate::Current()->heap()->true_value();
+ return heap_object->GetHeap()->true_value();
}
void Object::Lookup(String* name, LookupResult* result) {
- if (IsJSObject()) return JSObject::cast(this)->Lookup(name, result);
Object* holder = NULL;
- if (IsString()) {
- Heap* heap = HeapObject::cast(this)->GetHeap();
- Context* global_context = heap->isolate()->context()->global_context();
- holder = global_context->string_function()->instance_prototype();
- } else if (IsNumber()) {
+ if (IsSmi()) {
Heap* heap = Isolate::Current()->heap();
Context* global_context = heap->isolate()->context()->global_context();
holder = global_context->number_function()->instance_prototype();
- } else if (IsBoolean()) {
- Heap* heap = HeapObject::cast(this)->GetHeap();
- Context* global_context = heap->isolate()->context()->global_context();
- holder = global_context->boolean_function()->instance_prototype();
+ } else {
+ HeapObject* heap_object = HeapObject::cast(this);
+ if (heap_object->IsJSObject()) {
+ return JSObject::cast(this)->Lookup(name, result);
+ }
+ Heap* heap = heap_object->GetHeap();
+ if (heap_object->IsString()) {
+ Context* global_context = heap->isolate()->context()->global_context();
+ holder = global_context->string_function()->instance_prototype();
+ } else if (heap_object->IsHeapNumber()) {
+ Context* global_context = heap->isolate()->context()->global_context();
+ holder = global_context->number_function()->instance_prototype();
+ } else if (heap_object->IsBoolean()) {
+ Context* global_context = heap->isolate()->context()->global_context();
+ holder = global_context->boolean_function()->instance_prototype();
+ }
}
ASSERT(holder != NULL); // Cannot handle null or undefined.
JSObject::cast(holder)->Lookup(name, result);
@@ -247,7 +255,6 @@
LookupResult* result,
String* name,
PropertyAttributes* attributes) {
- Heap* heap = name->GetHeap();
if (result->IsProperty()) {
switch (result->type()) {
case CALLBACKS: {
@@ -299,6 +306,7 @@
// No accessible property found.
*attributes = ABSENT;
+ Heap* heap = name->GetHeap();
heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_GET);
return heap->undefined_value();
}
@@ -309,7 +317,6 @@
LookupResult* result,
String* name,
bool continue_search) {
- Heap* heap = name->GetHeap();
if (result->IsProperty()) {
switch (result->type()) {
case CALLBACKS: {
@@ -363,7 +370,7 @@
}
}
- heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
+ GetHeap()->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
return ABSENT;
}
@@ -397,11 +404,11 @@
Object* value,
PropertyDetails details) {
ASSERT(!HasFastProperties());
- Heap* heap = name->GetHeap();
int entry = property_dictionary()->FindEntry(name);
if (entry == StringDictionary::kNotFound) {
Object* store_value = value;
if (IsGlobalObject()) {
+ Heap* heap = name->GetHeap();
MaybeObject* maybe_store_value =
heap->AllocateJSGlobalPropertyCell(value);
if (!maybe_store_value->ToObject(&store_value)) return maybe_store_value;
@@ -433,7 +440,6 @@
MaybeObject* JSObject::DeleteNormalizedProperty(String* name, DeleteMode mode) {
ASSERT(!HasFastProperties());
- Heap* heap = GetHeap();
StringDictionary* dictionary = property_dictionary();
int entry = dictionary->FindEntry(name);
if (entry != StringDictionary::kNotFound) {
@@ -441,7 +447,7 @@
if (IsGlobalObject()) {
PropertyDetails details = dictionary->DetailsAt(entry);
if (details.IsDontDelete()) {
- if (mode != FORCE_DELETION) return heap->false_value();
+ if (mode != FORCE_DELETION) return GetHeap()->false_value();
// When forced to delete global properties, we have to make a
// map change to invalidate any ICs that think they can load
// from the DontDelete cell without checking if it contains
@@ -454,13 +460,13 @@
}
JSGlobalPropertyCell* cell =
JSGlobalPropertyCell::cast(dictionary->ValueAt(entry));
- cell->set_value(heap->the_hole_value());
+ cell->set_value(cell->heap()->the_hole_value());
dictionary->DetailsAtPut(entry, details.AsDeleted());
} else {
return dictionary->DeleteProperty(entry, mode);
}
}
- return heap->true_value();
+ return GetHeap()->true_value();
}
@@ -550,22 +556,31 @@
MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) {
- if (IsJSObject()) {
- return JSObject::cast(this)->GetElementWithReceiver(receiver, index);
- }
-
Object* holder = NULL;
- Context* global_context = Isolate::Current()->context()->global_context();
- if (IsString()) {
- holder = global_context->string_function()->instance_prototype();
- } else if (IsNumber()) {
+ if (IsSmi()) {
+ Context* global_context = Isolate::Current()->context()->global_context();
holder = global_context->number_function()->instance_prototype();
- } else if (IsBoolean()) {
- holder = global_context->boolean_function()->instance_prototype();
} else {
- // Undefined and null have no indexed properties.
- ASSERT(IsUndefined() || IsNull());
- return HEAP->undefined_value();
+ HeapObject* heap_object = HeapObject::cast(this);
+
+ if (heap_object->IsJSObject()) {
+ return JSObject::cast(this)->GetElementWithReceiver(receiver, index);
+ }
+ Heap* heap = heap_object->GetHeap();
+ Isolate* isolate = heap->isolate();
+
+ Context* global_context = isolate->context()->global_context();
+ if (heap_object->IsString()) {
+ holder = global_context->string_function()->instance_prototype();
+ } else if (heap_object->IsHeapNumber()) {
+ holder = global_context->number_function()->instance_prototype();
+ } else if (heap_object->IsBoolean()) {
+ holder = global_context->boolean_function()->instance_prototype();
+ } else {
+ // Undefined and null have no indexed properties.
+ ASSERT(heap_object->IsUndefined() || heap_object->IsNull());
+ return heap->undefined_value();
+ }
}
return JSObject::cast(holder)->GetElementWithReceiver(receiver, index);
@@ -573,14 +588,28 @@
Object* Object::GetPrototype() {
+ if (IsSmi()) {
+ Heap* heap = Isolate::Current()->heap();
+ Context* context = heap->isolate()->context()->global_context();
+ return context->number_function()->instance_prototype();
+ }
+
+ HeapObject* heap_object = HeapObject::cast(this);
+
// The object is either a number, a string, a boolean, or a real JS object.
- if (IsJSObject()) return JSObject::cast(this)->map()->prototype();
- Heap* heap = Isolate::Current()->heap();
+ if (heap_object->IsJSObject()) {
+ return JSObject::cast(this)->map()->prototype();
+ }
+ Heap* heap = heap_object->GetHeap();
Context* context = heap->isolate()->context()->global_context();
- if (IsNumber()) return context->number_function()->instance_prototype();
- if (IsString()) return context->string_function()->instance_prototype();
- if (IsBoolean()) {
+ if (heap_object->IsHeapNumber()) {
+ return context->number_function()->instance_prototype();
+ }
+ if (heap_object->IsString()) {
+ return context->string_function()->instance_prototype();
+ }
+ if (heap_object->IsBoolean()) {
return context->boolean_function()->instance_prototype();
} else {
return heap->null_value();
@@ -908,8 +937,9 @@
// All other JSObjects are rather similar to each other (JSObject,
// JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue).
default: {
- Heap* heap = GetHeap();
- Object* constructor = map()->constructor();
+ Map* map_of_this = map();
+ Heap* heap = map_of_this->heap();
+ Object* constructor = map_of_this->constructor();
bool printed = false;
if (constructor->IsHeapObject() &&
!heap->Contains(HeapObject::cast(constructor))) {
@@ -1249,6 +1279,22 @@
}
+static bool IsIdentifier(UnicodeCache* cache,
+ unibrow::CharacterStream* buffer) {
+ // Checks whether the buffer contains an identifier (no escape).
+ if (!buffer->has_more()) return false;
+ if (!cache->IsIdentifierStart(buffer->GetNext())) {
+ return false;
+ }
+ while (buffer->has_more()) {
+ if (!cache->IsIdentifierPart(buffer->GetNext())) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
MaybeObject* JSObject::AddFastProperty(String* name,
Object* value,
PropertyAttributes attributes) {
@@ -1258,7 +1304,7 @@
// hidden symbols) and is not a real identifier.
Isolate* isolate = GetHeap()->isolate();
StringInputBuffer buffer(name);
- if (!isolate->scanner_constants()->IsIdentifier(&buffer)
+ if (!IsIdentifier(isolate->unicode_cache(), &buffer)
&& name != isolate->heap()->hidden_symbol()) {
Object* obj;
{ MaybeObject* maybe_obj =
@@ -1350,8 +1396,7 @@
String* name,
JSFunction* function,
PropertyAttributes attributes) {
- Heap* heap = GetHeap();
- ASSERT(!heap->InNewSpace(function));
+ ASSERT(!GetHeap()->InNewSpace(function));
// Allocate new instance descriptors with (name, function) added
ConstantFunctionDescriptor d(name, function, attributes);
@@ -1376,6 +1421,7 @@
// If the old map is the global object map (from new Object()),
// then transitions are not added to it, so we are done.
+ Heap* heap = old_map->heap();
if (old_map == heap->isolate()->context()->global_context()->
object_function()->map()) {
return function;
@@ -1412,7 +1458,6 @@
Object* value,
PropertyAttributes attributes) {
ASSERT(!HasFastProperties());
- Heap* heap = GetHeap();
StringDictionary* dict = property_dictionary();
Object* store_value = value;
if (IsGlobalObject()) {
@@ -1429,6 +1474,7 @@
dict->SetEntry(entry, name, store_value, details);
return value;
}
+ Heap* heap = GetHeap();
{ MaybeObject* maybe_store_value =
heap->AllocateJSGlobalPropertyCell(value);
if (!maybe_store_value->ToObject(&store_value)) return maybe_store_value;
@@ -1450,8 +1496,9 @@
PropertyAttributes attributes,
StrictModeFlag strict_mode) {
ASSERT(!IsJSGlobalProxy());
- Heap* heap = GetHeap();
- if (!map()->is_extensible()) {
+ Map* map_of_this = map();
+ Heap* heap = map_of_this->heap();
+ if (!map_of_this->is_extensible()) {
if (strict_mode == kNonStrictMode) {
return heap->undefined_value();
} else {
@@ -1463,7 +1510,7 @@
}
if (HasFastProperties()) {
// Ensure the descriptor array does not get too big.
- if (map()->instance_descriptors()->number_of_descriptors() <
+ if (map_of_this->instance_descriptors()->number_of_descriptors() <
DescriptorArray::kMaxNumberOfDescriptors) {
if (value->IsJSFunction() && !heap->InNewSpace(value)) {
return AddConstantFunctionProperty(name,
@@ -1537,7 +1584,7 @@
return result;
}
// Do not add transitions to the map of "new Object()".
- if (map() == GetHeap()->isolate()->context()->global_context()->
+ if (map() == old_map->heap()->isolate()->context()->global_context()->
object_function()->map()) {
return result;
}
@@ -1836,8 +1883,9 @@
MaybeObject* Map::GetExternalArrayElementsMap(ExternalArrayType array_type,
bool safe_to_add_transition) {
+ Heap* current_heap = heap();
DescriptorArray* descriptors = instance_descriptors();
- String* external_array_sentinel_name = GetIsolate()->heap()->empty_symbol();
+ String* external_array_sentinel_name = current_heap->empty_symbol();
if (safe_to_add_transition) {
// It's only safe to manipulate the descriptor array if it would be
@@ -1845,7 +1893,8 @@
ASSERT(!is_shared()); // no transitions can be added to shared maps.
// Check if the external array transition already exists.
- DescriptorLookupCache* cache = heap()->isolate()->descriptor_lookup_cache();
+ DescriptorLookupCache* cache =
+ current_heap->isolate()->descriptor_lookup_cache();
int index = cache->Lookup(descriptors, external_array_sentinel_name);
if (index == DescriptorLookupCache::kAbsent) {
index = descriptors->Search(external_array_sentinel_name);
@@ -1979,7 +2028,6 @@
String* name,
Object* value,
bool check_prototype) {
- Heap* heap = GetHeap();
if (check_prototype && !result->IsProperty()) {
LookupCallbackSetterInPrototypes(name, result);
}
@@ -2020,6 +2068,7 @@
HandleScope scope;
Handle<Object> value_handle(value);
+ Heap* heap = GetHeap();
heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_SET);
return *value_handle;
}
@@ -2157,7 +2206,6 @@
String* name,
Object* value,
PropertyAttributes attributes) {
- Heap* heap = GetHeap();
// Make sure that the top context does not change when doing callbacks or
// interceptor calls.
@@ -2165,9 +2213,11 @@
LookupResult result;
LocalLookup(name, &result);
// Check access rights if needed.
- if (IsAccessCheckNeeded()
- && !heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_SET)) {
- return SetPropertyWithFailedAccessCheck(&result, name, value, false);
+ if (IsAccessCheckNeeded()) {
+ Heap* heap = GetHeap();
+ if (!heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_SET)) {
+ return SetPropertyWithFailedAccessCheck(&result, name, value, false);
+ }
}
if (IsJSGlobalProxy()) {
@@ -2318,14 +2368,15 @@
LookupResult* result,
String* name,
bool continue_search) {
- Heap* heap = GetHeap();
// Check access rights if needed.
- if (IsAccessCheckNeeded() &&
- !heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_HAS)) {
- return GetPropertyAttributeWithFailedAccessCheck(receiver,
- result,
- name,
- continue_search);
+ if (IsAccessCheckNeeded()) {
+ Heap* heap = GetHeap();
+ if (!heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_HAS)) {
+ return GetPropertyAttributeWithFailedAccessCheck(receiver,
+ result,
+ name,
+ continue_search);
+ }
}
if (result->IsProperty()) {
switch (result->type()) {
@@ -2465,10 +2516,10 @@
// JSGlobalProxy must never be normalized
ASSERT(!IsJSGlobalProxy());
- Heap* heap = GetHeap();
+ Map* map_of_this = map();
// Allocate new content.
- int property_count = map()->NumberOfDescribedProperties();
+ int property_count = map_of_this->NumberOfDescribedProperties();
if (expected_additional_properties > 0) {
property_count += expected_additional_properties;
} else {
@@ -2481,9 +2532,9 @@
}
StringDictionary* dictionary = StringDictionary::cast(obj);
- DescriptorArray* descs = map()->instance_descriptors();
+ DescriptorArray* descs = map_of_this->instance_descriptors();
for (int i = 0; i < descs->number_of_descriptors(); i++) {
- PropertyDetails details = descs->GetDetails(i);
+ PropertyDetails details(descs->GetDetails(i));
switch (details.type()) {
case CONSTANT_FUNCTION: {
PropertyDetails d =
@@ -2531,11 +2582,14 @@
}
}
+ Heap* current_heap = map_of_this->heap();
+
// Copy the next enumeration index from instance descriptor.
- int index = map()->instance_descriptors()->NextEnumerationIndex();
+ int index = map_of_this->instance_descriptors()->NextEnumerationIndex();
dictionary->SetNextEnumerationIndex(index);
- { MaybeObject* maybe_obj = heap->isolate()->context()->global_context()->
+ { MaybeObject* maybe_obj =
+ current_heap->isolate()->context()->global_context()->
normalized_map_cache()->Get(this, mode);
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
@@ -2546,17 +2600,17 @@
// Resize the object in the heap if necessary.
int new_instance_size = new_map->instance_size();
- int instance_size_delta = map()->instance_size() - new_instance_size;
+ int instance_size_delta = map_of_this->instance_size() - new_instance_size;
ASSERT(instance_size_delta >= 0);
- heap->CreateFillerObjectAt(this->address() + new_instance_size,
- instance_size_delta);
+ current_heap->CreateFillerObjectAt(this->address() + new_instance_size,
+ instance_size_delta);
set_map(new_map);
- map()->set_instance_descriptors(heap->empty_descriptor_array());
+ new_map->set_instance_descriptors(current_heap->empty_descriptor_array());
set_properties(dictionary);
- heap->isolate()->counters()->props_to_dictionary()->Increment();
+ current_heap->isolate()->counters()->props_to_dictionary()->Increment();
#ifdef DEBUG
if (FLAG_trace_normalization) {
@@ -2579,10 +2633,11 @@
MaybeObject* JSObject::NormalizeElements() {
ASSERT(!HasExternalArrayElements());
if (HasDictionaryElements()) return this;
- ASSERT(map()->has_fast_elements());
+ Map* old_map = map();
+ ASSERT(old_map->has_fast_elements());
Object* obj;
- { MaybeObject* maybe_obj = map()->GetSlowElementsMap();
+ { MaybeObject* maybe_obj = old_map->GetSlowElementsMap();
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
Map* new_map = Map::cast(obj);
@@ -2617,7 +2672,7 @@
set_map(new_map);
set_elements(dictionary);
- new_map->GetHeap()->isolate()->counters()->elements_to_dictionary()->
+ new_map->heap()->isolate()->counters()->elements_to_dictionary()->
Increment();
#ifdef DEBUG
@@ -2634,10 +2689,9 @@
MaybeObject* JSObject::DeletePropertyPostInterceptor(String* name,
DeleteMode mode) {
// Check local property, ignore interceptor.
- Heap* heap = GetHeap();
LookupResult result;
LocalLookupRealNamedProperty(name, &result);
- if (!result.IsProperty()) return heap->true_value();
+ if (!result.IsProperty()) return GetHeap()->true_value();
// Normalize object if needed.
Object* obj;
@@ -2683,7 +2737,6 @@
MaybeObject* JSObject::DeleteElementPostInterceptor(uint32_t index,
DeleteMode mode) {
- Heap* heap = GetHeap();
ASSERT(!HasExternalArrayElements());
switch (GetElementsKind()) {
case FAST_ELEMENTS: {
@@ -2711,7 +2764,7 @@
UNREACHABLE();
break;
}
- return heap->true_value();
+ return GetHeap()->true_value();
}
@@ -2884,16 +2937,17 @@
// Check whether this object references another object.
bool JSObject::ReferencesObject(Object* obj) {
- Heap* heap = GetHeap();
+ Map* map_of_this = map();
+ Heap* heap = map_of_this->heap();
AssertNoAllocation no_alloc;
// Is the object the constructor for this object?
- if (map()->constructor() == obj) {
+ if (map_of_this->constructor() == obj) {
return true;
}
// Is the object the prototype for this object?
- if (map()->prototype() == obj) {
+ if (map_of_this->prototype() == obj) {
return true;
}
@@ -3503,7 +3557,6 @@
Object* JSObject::SlowReverseLookup(Object* value) {
- Heap* heap = GetHeap();
if (HasFastProperties()) {
DescriptorArray* descs = map()->instance_descriptors();
for (int i = 0; i < descs->number_of_descriptors(); i++) {
@@ -3517,7 +3570,7 @@
}
}
}
- return heap->undefined_value();
+ return GetHeap()->undefined_value();
} else {
return property_dictionary()->SlowReverseLookup(value);
}
@@ -3621,7 +3674,7 @@
// Allocate the code cache if not present.
if (code_cache()->IsFixedArray()) {
Object* result;
- { MaybeObject* maybe_result = GetHeap()->AllocateCodeCache();
+ { MaybeObject* maybe_result = code->heap()->AllocateCodeCache();
if (!maybe_result->ToObject(&result)) return maybe_result;
}
set_code_cache(result);
@@ -3807,7 +3860,6 @@
Object* CodeCache::LookupDefaultCache(String* name, Code::Flags flags) {
- Heap* heap = GetHeap();
FixedArray* cache = default_cache();
int length = cache->length();
for (int i = 0; i < length; i += kCodeCacheEntrySize) {
@@ -3822,7 +3874,7 @@
}
}
}
- return heap->undefined_value();
+ return GetHeap()->undefined_value();
}
@@ -3913,7 +3965,7 @@
MUST_USE_RESULT MaybeObject* AsObject() {
ASSERT(code_ != NULL);
Object* obj;
- { MaybeObject* maybe_obj = code_->GetHeap()->AllocateFixedArray(2);
+ { MaybeObject* maybe_obj = code_->heap()->AllocateFixedArray(2);
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
FixedArray* pair = FixedArray::cast(obj);
@@ -3991,7 +4043,6 @@
MaybeObject* FixedArray::AddKeysFromJSArray(JSArray* array) {
- Heap* heap = GetHeap();
ASSERT(!array->HasExternalArrayElements());
switch (array->GetElementsKind()) {
case JSObject::FAST_ELEMENTS:
@@ -4002,7 +4053,7 @@
// Allocate a temporary fixed array.
Object* object;
- { MaybeObject* maybe_object = heap->AllocateFixedArray(size);
+ { MaybeObject* maybe_object = GetHeap()->AllocateFixedArray(size);
if (!maybe_object->ToObject(&object)) return maybe_object;
}
FixedArray* key_array = FixedArray::cast(object);
@@ -4022,12 +4073,11 @@
UNREACHABLE();
}
UNREACHABLE();
- return heap->null_value(); // Failure case needs to "return" a value.
+ return GetHeap()->null_value(); // Failure case needs to "return" a value.
}
MaybeObject* FixedArray::UnionOfKeys(FixedArray* other) {
- Heap* heap = GetHeap();
int len0 = length();
#ifdef DEBUG
if (FLAG_enable_slow_asserts) {
@@ -4053,7 +4103,7 @@
// Allocate the result
Object* obj;
- { MaybeObject* maybe_obj = heap->AllocateFixedArray(len0 + extra);
+ { MaybeObject* maybe_obj = GetHeap()->AllocateFixedArray(len0 + extra);
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
// Fill in the content
@@ -5373,7 +5423,7 @@
if (StringShape(this).IsSymbol()) return false;
Map* map = this->map();
- Heap* heap = map->GetHeap();
+ Heap* heap = map->heap();
if (map == heap->string_map()) {
this->set_map(heap->undetectable_string_map());
return true;
@@ -5389,8 +5439,8 @@
bool String::IsEqualTo(Vector<const char> str) {
Isolate* isolate = GetIsolate();
int slen = length();
- Access<ScannerConstants::Utf8Decoder>
- decoder(isolate->scanner_constants()->utf8_decoder());
+ Access<UnicodeCache::Utf8Decoder>
+ decoder(isolate->unicode_cache()->utf8_decoder());
decoder->Reset(str.start(), str.length());
int i;
for (i = 0; i < slen && decoder->has_more(); i++) {
@@ -5702,17 +5752,18 @@
// used for constructing objects to the original object prototype.
// See ECMA-262 13.2.2.
if (!value->IsJSObject()) {
- Heap* heap = GetHeap();
// Copy the map so this does not affect unrelated functions.
// Remove map transitions because they point to maps with a
// different prototype.
- Object* new_map;
+ Object* new_object;
{ MaybeObject* maybe_new_map = map()->CopyDropTransitions();
- if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
+ if (!maybe_new_map->ToObject(&new_object)) return maybe_new_map;
}
- set_map(Map::cast(new_map));
- map()->set_constructor(value);
- map()->set_non_instance_prototype(true);
+ Map* new_map = Map::cast(new_object);
+ Heap* heap = new_map->heap();
+ set_map(new_map);
+ new_map->set_constructor(value);
+ new_map->set_non_instance_prototype(true);
construct_prototype =
heap->isolate()->context()->global_context()->
initial_object_prototype();
@@ -5740,7 +5791,7 @@
ASSERT(shared()->strict_mode() || map() == global_context->function_map());
set_map(no_prototype_map);
- set_prototype_or_initial_map(GetHeap()->the_hole_value());
+ set_prototype_or_initial_map(no_prototype_map->heap()->the_hole_value());
return this;
}
@@ -5822,8 +5873,6 @@
bool SharedFunctionInfo::CanGenerateInlineConstructor(Object* prototype) {
- Heap* heap = GetHeap();
-
// Check the basic conditions for generating inline constructor code.
if (!FLAG_inline_new
|| !has_only_simple_this_property_assignments()
@@ -5837,6 +5886,8 @@
return true;
}
+ Heap* heap = GetHeap();
+
// Traverse the proposed prototype chain looking for setters for properties of
// the same names as are set by the inline constructor.
for (Object* obj = prototype;
@@ -6156,7 +6207,7 @@
void Code::InvalidateRelocation() {
- set_relocation_info(GetHeap()->empty_byte_array());
+ set_relocation_info(heap()->empty_byte_array());
}
@@ -6456,7 +6507,6 @@
case KEYED_EXTERNAL_ARRAY_STORE_IC: return "KEYED_EXTERNAL_ARRAY_STORE_IC";
case CALL_IC: return "CALL_IC";
case KEYED_CALL_IC: return "KEYED_CALL_IC";
- case BINARY_OP_IC: return "BINARY_OP_IC";
case TYPE_RECORDING_BINARY_OP_IC: return "TYPE_RECORDING_BINARY_OP_IC";
case COMPARE_IC: return "COMPARE_IC";
}
@@ -6734,7 +6784,6 @@
MaybeObject* JSObject::SetElementsLength(Object* len) {
- Heap* heap = GetHeap();
// We should never end in here with a pixel or external array.
ASSERT(AllowsSetElementsLength());
@@ -6742,7 +6791,7 @@
Object* smi_length = Smi::FromInt(0);
if (maybe_smi_length->ToObject(&smi_length) && smi_length->IsSmi()) {
const int value = Smi::cast(smi_length)->value();
- if (value < 0) return ArrayLengthRangeError(heap);
+ if (value < 0) return ArrayLengthRangeError(GetHeap());
switch (GetElementsKind()) {
case FAST_ELEMENTS: {
int old_capacity = FixedArray::cast(elements())->length();
@@ -6808,14 +6857,14 @@
if (len->ToArrayIndex(&length)) {
return SetSlowElements(len);
} else {
- return ArrayLengthRangeError(heap);
+ return ArrayLengthRangeError(GetHeap());
}
}
// len is not a number so make the array size one and
// set only element to len.
Object* obj;
- { MaybeObject* maybe_obj = heap->AllocateFixedArray(1);
+ { MaybeObject* maybe_obj = GetHeap()->AllocateFixedArray(1);
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
FixedArray::cast(obj)->set(0, len);
@@ -6832,6 +6881,22 @@
// SpiderMonkey behaves this way.
if (!value->IsJSObject() && !value->IsNull()) return value;
+ // From 8.6.2 Object Internal Methods
+ // ...
+ // In addition, if [[Extensible]] is false the value of the [[Class]] and
+ // [[Prototype]] internal properties of the object may not be modified.
+ // ...
+ // Implementation specific extensions that modify [[Class]], [[Prototype]]
+ // or [[Extensible]] must not violate the invariants defined in the preceding
+ // paragraph.
+ if (!this->map()->is_extensible()) {
+ HandleScope scope;
+ Handle<Object> handle(this, heap->isolate());
+ return heap->isolate()->Throw(
+ *FACTORY->NewTypeError("non_extensible_proto",
+ HandleVector<Object>(&handle, 1)));
+ }
+
// Before we can set the prototype we need to be sure
// prototype cycles are prevented.
// It is sufficient to validate that the receiver is not in the new prototype
@@ -6970,13 +7035,13 @@
JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) {
- Heap* heap = GetHeap();
-
// Check access rights if needed.
- if (IsAccessCheckNeeded() &&
- !heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
- heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
- return UNDEFINED_ELEMENT;
+ if (IsAccessCheckNeeded()) {
+ Heap* heap = GetHeap();
+ if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
+ heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
+ return UNDEFINED_ELEMENT;
+ }
}
if (IsJSGlobalProxy()) {
@@ -7042,13 +7107,13 @@
bool JSObject::HasElementWithReceiver(JSObject* receiver, uint32_t index) {
- Heap* heap = GetHeap();
-
// Check access rights if needed.
- if (IsAccessCheckNeeded() &&
- !heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
- heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
- return false;
+ if (IsAccessCheckNeeded()) {
+ Heap* heap = GetHeap();
+ if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
+ heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
+ return false;
+ }
}
// Check for lookup interceptor
@@ -7320,14 +7385,15 @@
Object* value,
StrictModeFlag strict_mode,
bool check_prototype) {
- Heap* heap = GetHeap();
// Check access rights if needed.
- if (IsAccessCheckNeeded() &&
- !heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_SET)) {
- HandleScope scope;
- Handle<Object> value_handle(value);
- heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_SET);
- return *value_handle;
+ if (IsAccessCheckNeeded()) {
+ Heap* heap = GetHeap();
+ if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_SET)) {
+ HandleScope scope;
+ Handle<Object> value_handle(value);
+ heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_SET);
+ return *value_handle;
+ }
}
if (IsJSGlobalProxy()) {
@@ -7522,7 +7588,6 @@
MaybeObject* JSObject::GetElementPostInterceptor(Object* receiver,
uint32_t index) {
- Heap* heap = GetHeap();
// Get element works for both JSObject and JSArray since
// JSArray::length cannot change.
switch (GetElementsKind()) {
@@ -7571,7 +7636,7 @@
// Continue searching via the prototype chain.
Object* pt = GetPrototype();
- if (pt->IsNull()) return heap->undefined_value();
+ if (pt->IsNull()) return GetHeap()->undefined_value();
return pt->GetElementWithReceiver(receiver, index);
}
@@ -7586,7 +7651,6 @@
Handle<InterceptorInfo> interceptor(GetIndexedInterceptor(), isolate);
Handle<Object> this_handle(receiver, isolate);
Handle<JSObject> holder_handle(this, isolate);
-
if (!interceptor->getter()->IsUndefined()) {
v8::IndexedPropertyGetter getter =
v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
@@ -7613,12 +7677,13 @@
MaybeObject* JSObject::GetElementWithReceiver(Object* receiver,
uint32_t index) {
- Heap* heap = GetHeap();
// Check access rights if needed.
- if (IsAccessCheckNeeded() &&
- !heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_GET)) {
- heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_GET);
- return heap->undefined_value();
+ if (IsAccessCheckNeeded()) {
+ Heap* heap = GetHeap();
+ if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_GET)) {
+ heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_GET);
+ return heap->undefined_value();
+ }
}
if (HasIndexedInterceptor()) {
@@ -7669,6 +7734,7 @@
}
Object* pt = GetPrototype();
+ Heap* heap = GetHeap();
if (pt == heap->null_value()) return heap->undefined_value();
return pt->GetElementWithReceiver(receiver, index);
}
@@ -7895,7 +7961,6 @@
JSObject* receiver,
String* name,
PropertyAttributes* attributes) {
- Heap* heap = GetHeap();
// Check local property in holder, ignore interceptor.
LookupResult result;
LocalLookupRealNamedProperty(name, &result);
@@ -7905,7 +7970,7 @@
// Continue searching via the prototype chain.
Object* pt = GetPrototype();
*attributes = ABSENT;
- if (pt->IsNull()) return heap->undefined_value();
+ if (pt->IsNull()) return GetHeap()->undefined_value();
return pt->GetPropertyWithReceiver(receiver, name, attributes);
}
@@ -7914,14 +7979,13 @@
JSObject* receiver,
String* name,
PropertyAttributes* attributes) {
- Heap* heap = GetHeap();
// Check local property in holder, ignore interceptor.
LookupResult result;
LocalLookupRealNamedProperty(name, &result);
if (result.IsProperty()) {
return GetProperty(receiver, &result, name, attributes);
}
- return heap->undefined_value();
+ return GetHeap()->undefined_value();
}
@@ -7966,12 +8030,13 @@
bool JSObject::HasRealNamedProperty(String* key) {
- Heap* heap = GetHeap();
// Check access rights if needed.
- if (IsAccessCheckNeeded() &&
- !heap->isolate()->MayNamedAccess(this, key, v8::ACCESS_HAS)) {
- heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
- return false;
+ if (IsAccessCheckNeeded()) {
+ Heap* heap = GetHeap();
+ if (!heap->isolate()->MayNamedAccess(this, key, v8::ACCESS_HAS)) {
+ heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
+ return false;
+ }
}
LookupResult result;
@@ -7981,12 +8046,13 @@
bool JSObject::HasRealElementProperty(uint32_t index) {
- Heap* heap = GetHeap();
// Check access rights if needed.
- if (IsAccessCheckNeeded() &&
- !heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
- heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
- return false;
+ if (IsAccessCheckNeeded()) {
+ Heap* heap = GetHeap();
+ if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
+ heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
+ return false;
+ }
}
// Handle [] on String objects.
@@ -8025,17 +8091,18 @@
}
// All possibilities have been handled above already.
UNREACHABLE();
- return heap->null_value();
+ return GetHeap()->null_value();
}
bool JSObject::HasRealNamedCallbackProperty(String* key) {
- Heap* heap = GetHeap();
// Check access rights if needed.
- if (IsAccessCheckNeeded() &&
- !heap->isolate()->MayNamedAccess(this, key, v8::ACCESS_HAS)) {
- heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
- return false;
+ if (IsAccessCheckNeeded()) {
+ Heap* heap = GetHeap();
+ if (!heap->isolate()->MayNamedAccess(this, key, v8::ACCESS_HAS)) {
+ heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
+ return false;
+ }
}
LookupResult result;
@@ -8049,7 +8116,7 @@
DescriptorArray* descs = map()->instance_descriptors();
int result = 0;
for (int i = 0; i < descs->number_of_descriptors(); i++) {
- PropertyDetails details = descs->GetDetails(i);
+ PropertyDetails details(descs->GetDetails(i));
if (details.IsProperty() && (details.attributes() & filter) == 0) {
result++;
}
@@ -8674,7 +8741,6 @@
template<typename Shape, typename Key>
MaybeObject* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) {
- Heap* heap = GetHeap();
int capacity = Capacity();
int nof = NumberOfElements() + n;
int nod = NumberOfDeletedElements();
@@ -8688,7 +8754,7 @@
const int kMinCapacityForPretenure = 256;
bool pretenure =
- (capacity > kMinCapacityForPretenure) && !heap->InNewSpace(this);
+ (capacity > kMinCapacityForPretenure) && !GetHeap()->InNewSpace(this);
Object* obj;
{ MaybeObject* maybe_obj =
Allocate(nof * 2, pretenure ? TENURED : NOT_TENURED);
@@ -8820,7 +8886,6 @@
// Collates undefined and unexisting elements below limit from position
// zero of the elements. The object stays in Dictionary mode.
MaybeObject* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
- Heap* heap = GetHeap();
ASSERT(HasDictionaryElements());
// Must stay in dictionary mode, either because of requires_slow_elements,
// or because we are not going to sort (and therefore compact) all of the
@@ -8830,7 +8895,7 @@
if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
// Allocate space for result before we start mutating the object.
Object* new_double;
- { MaybeObject* maybe_new_double = heap->AllocateHeapNumber(0.0);
+ { MaybeObject* maybe_new_double = GetHeap()->AllocateHeapNumber(0.0);
if (!maybe_new_double->ToObject(&new_double)) return maybe_new_double;
}
result_double = HeapNumber::cast(new_double);
@@ -8890,6 +8955,7 @@
uint32_t result = pos;
PropertyDetails no_details = PropertyDetails(NONE, NORMAL);
+ Heap* heap = GetHeap();
while (undefs > 0) {
if (pos > static_cast<uint32_t>(Smi::kMaxValue)) {
// Adding an entry with the key beyond smi-range requires
@@ -8919,9 +8985,10 @@
// If the object is in dictionary mode, it is converted to fast elements
// mode.
MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) {
- Heap* heap = GetHeap();
ASSERT(!HasExternalArrayElements());
+ Heap* heap = GetHeap();
+
if (HasDictionaryElements()) {
// Convert to fast elements containing only the existing properties.
// Ordering is irrelevant, since we are going to sort anyway.
@@ -9175,9 +9242,9 @@
MaybeObject* GlobalObject::EnsurePropertyCell(String* name) {
ASSERT(!HasFastProperties());
- Heap* heap = GetHeap();
int entry = property_dictionary()->FindEntry(name);
if (entry == StringDictionary::kNotFound) {
+ Heap* heap = GetHeap();
Object* cell;
{ MaybeObject* maybe_cell =
heap->AllocateJSGlobalPropertyCell(heap->the_hole_value());
@@ -9352,10 +9419,9 @@
Object* CompilationCacheTable::Lookup(String* src) {
- Heap* heap = GetHeap();
StringKey key(src);
int entry = FindEntry(&key);
- if (entry == kNotFound) return heap->undefined_value();
+ if (entry == kNotFound) return GetHeap()->undefined_value();
return get(EntryToIndex(entry) + 1);
}
@@ -9372,10 +9438,9 @@
Object* CompilationCacheTable::LookupRegExp(String* src,
JSRegExp::Flags flags) {
- Heap* heap = GetHeap();
RegExpKey key(src, flags);
int entry = FindEntry(&key);
- if (entry == kNotFound) return heap->undefined_value();
+ if (entry == kNotFound) return GetHeap()->undefined_value();
return get(EntryToIndex(entry) + 1);
}
@@ -9495,10 +9560,9 @@
Object* MapCache::Lookup(FixedArray* array) {
- Heap* heap = GetHeap();
SymbolsKey key(array);
int entry = FindEntry(&key);
- if (entry == kNotFound) return heap->undefined_value();
+ if (entry == kNotFound) return GetHeap()->undefined_value();
return get(EntryToIndex(entry) + 1);
}
@@ -9619,7 +9683,7 @@
if (key->IsNumber()) {
uint32_t number = static_cast<uint32_t>(key->Number());
if (from <= number && number < to) {
- SetEntry(i, sentinel, sentinel, Smi::FromInt(0));
+ SetEntry(i, sentinel, sentinel);
removed_entries++;
}
}
@@ -9639,7 +9703,7 @@
if (details.IsDontDelete() && mode != JSObject::FORCE_DELETION) {
return heap->false_value();
}
- SetEntry(entry, heap->null_value(), heap->null_value(), Smi::FromInt(0));
+ SetEntry(entry, heap->null_value(), heap->null_value());
HashTable<Shape, Key>::ElementRemoved();
return heap->true_value();
}
@@ -9854,7 +9918,6 @@
// Backwards lookup (slow).
template<typename Shape, typename Key>
Object* Dictionary<Shape, Key>::SlowReverseLookup(Object* value) {
- Heap* heap = Dictionary<Shape, Key>::GetHeap();
int capacity = HashTable<Shape, Key>::Capacity();
for (int i = 0; i < capacity; i++) {
Object* k = HashTable<Shape, Key>::KeyAt(i);
@@ -9866,13 +9929,13 @@
if (e == value) return k;
}
}
+ Heap* heap = Dictionary<Shape, Key>::GetHeap();
return heap->undefined_value();
}
MaybeObject* StringDictionary::TransformPropertiesToFastFor(
JSObject* obj, int unused_property_fields) {
- Heap* heap = GetHeap();
// Make sure we preserve dictionary representation if there are too many
// descriptors.
if (NumberOfElements() > DescriptorArray::kMaxNumberOfDescriptors) return obj;
@@ -9892,6 +9955,8 @@
int instance_descriptor_length = 0;
int number_of_fields = 0;
+ Heap* heap = GetHeap();
+
// Compute the length of the instance descriptor.
int capacity = Capacity();
for (int i = 0; i < capacity; i++) {
@@ -10020,12 +10085,11 @@
// Get the break point info object for this code position.
Object* DebugInfo::GetBreakPointInfo(int code_position) {
- Heap* heap = GetHeap();
// Find the index of the break point info object for this code position.
int index = GetBreakPointInfoIndex(code_position);
// Return the break point info object if any.
- if (index == kNoBreakPointInfo) return heap->undefined_value();
+ if (index == kNoBreakPointInfo) return GetHeap()->undefined_value();
return BreakPointInfo::cast(break_points()->get(index));
}
@@ -10098,10 +10162,9 @@
// Get the break point objects for a code position.
Object* DebugInfo::GetBreakPointObjects(int code_position) {
- Heap* heap = GetHeap();
Object* break_point_info = GetBreakPointInfo(code_position);
if (break_point_info->IsUndefined()) {
- return heap->undefined_value();
+ return GetHeap()->undefined_value();
}
return BreakPointInfo::cast(break_point_info)->break_point_objects();
}
@@ -10124,7 +10187,7 @@
Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info,
Handle<Object> break_point_object) {
- Heap* heap = Isolate::Current()->heap();
+ Heap* heap = debug_info->GetHeap();
if (debug_info->break_points()->IsUndefined()) return heap->undefined_value();
for (int i = 0; i < debug_info->break_points()->length(); i++) {
if (!debug_info->break_points()->get(i)->IsUndefined()) {
diff --git a/src/objects.h b/src/objects.h
index 96e5cb6..03445e8 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -167,7 +167,7 @@
}
// Conversion for storing details as Object*.
- inline PropertyDetails(Smi* smi);
+ explicit inline PropertyDetails(Smi* smi);
inline Smi* AsSmi();
PropertyType type() { return TypeField::decode(value_); }
@@ -1293,14 +1293,9 @@
// is a mixture of sign, exponent and mantissa. Our current platforms are all
// little endian apart from non-EABI arm which is little endian with big
// endian floating point word ordering!
-#if !defined(V8_HOST_ARCH_ARM) || defined(USE_ARM_EABI)
static const int kMantissaOffset = kValueOffset;
static const int kExponentOffset = kValueOffset + 4;
-#else
- static const int kMantissaOffset = kValueOffset + 4;
- static const int kExponentOffset = kValueOffset;
-# define BIG_ENDIAN_FLOATING_POINT 1
-#endif
+
static const int kSize = kValueOffset + kDoubleSize;
static const uint32_t kSignMask = 0x80000000u;
static const uint32_t kExponentMask = 0x7ff00000u;
@@ -2576,6 +2571,9 @@
// Sets the entry to (key, value) pair.
inline void SetEntry(int entry,
Object* key,
+ Object* value);
+ inline void SetEntry(int entry,
+ Object* key,
Object* value,
PropertyDetails details);
@@ -3239,7 +3237,6 @@
STORE_IC,
KEYED_STORE_IC,
KEYED_EXTERNAL_ARRAY_STORE_IC,
- BINARY_OP_IC,
TYPE_RECORDING_BINARY_OP_IC,
COMPARE_IC,
// No more than 16 kinds. The value currently encoded in four bits in
@@ -3308,7 +3305,6 @@
inline bool is_keyed_store_stub() { return kind() == KEYED_STORE_IC; }
inline bool is_call_stub() { return kind() == CALL_IC; }
inline bool is_keyed_call_stub() { return kind() == KEYED_CALL_IC; }
- inline bool is_binary_op_stub() { return kind() == BINARY_OP_IC; }
inline bool is_type_recording_binary_op_stub() {
return kind() == TYPE_RECORDING_BINARY_OP_IC;
}
@@ -3366,10 +3362,6 @@
inline ExternalArrayType external_array_type();
inline void set_external_array_type(ExternalArrayType value);
- // [binary op type]: For all BINARY_OP_IC.
- inline byte binary_op_type();
- inline void set_binary_op_type(byte value);
-
// [type-recording binary op type]: For all TYPE_RECORDING_BINARY_OP_IC.
inline byte type_recording_binary_op_type();
inline void set_type_recording_binary_op_type(byte value);
@@ -3487,6 +3479,10 @@
void CodeVerify();
#endif
+ // Returns the isolate/heap this code object belongs to.
+ inline Isolate* isolate();
+ inline Heap* heap();
+
// Max loop nesting marker used to postpose OSR. We don't take loop
// nesting that is deeper than 5 levels into account.
static const int kMaxLoopNestingMarker = 6;
@@ -4255,9 +4251,6 @@
// this.x = y; where y is either a constant or refers to an argument.
inline bool has_only_simple_this_property_assignments();
- inline bool try_full_codegen();
- inline void set_try_full_codegen(bool flag);
-
// Indicates if this function can be lazy compiled.
// This is used to determine if we can safely flush code from a function
// when doing GC if we expect that the function will no longer be used.
@@ -4457,13 +4450,12 @@
// Bit positions in compiler_hints.
static const int kHasOnlySimpleThisPropertyAssignments = 0;
- static const int kTryFullCodegen = 1;
- static const int kAllowLazyCompilation = 2;
- static const int kLiveObjectsMayExist = 3;
- static const int kCodeAgeShift = 4;
+ static const int kAllowLazyCompilation = 1;
+ static const int kLiveObjectsMayExist = 2;
+ static const int kCodeAgeShift = 3;
static const int kCodeAgeMask = 0x7;
- static const int kOptimizationDisabled = 7;
- static const int kStrictModeFunction = 8;
+ static const int kOptimizationDisabled = 6;
+ static const int kStrictModeFunction = 7;
private:
#if V8_HOST_ARCH_32_BIT
@@ -4534,6 +4526,9 @@
// Tells whether or not this function has been optimized.
inline bool IsOptimized();
+ // Tells whether or not this function can be optimized.
+ inline bool IsOptimizable();
+
// Mark this function for lazy recompilation. The function will be
// recompiled the next time it is executed.
void MarkForLazyRecompilation();
@@ -5158,7 +5153,7 @@
class StringHasher {
public:
- inline StringHasher(int length);
+ explicit inline StringHasher(int length);
// Returns true if the hash of this string can be computed without
// looking at the contents.
@@ -5905,7 +5900,7 @@
public:
virtual void Seek(unsigned pos);
inline StringInputBuffer(): unibrow::InputBuffer<String, String*, 1024>() {}
- inline StringInputBuffer(String* backing):
+ explicit inline StringInputBuffer(String* backing):
unibrow::InputBuffer<String, String*, 1024>(backing) {}
};
@@ -5916,7 +5911,7 @@
virtual void Seek(unsigned pos);
inline SafeStringInputBuffer()
: unibrow::InputBuffer<String, String**, 256>() {}
- inline SafeStringInputBuffer(String** backing)
+ explicit inline SafeStringInputBuffer(String** backing)
: unibrow::InputBuffer<String, String**, 256>(backing) {}
};
@@ -6009,6 +6004,10 @@
kValueOffset + kPointerSize,
kSize> BodyDescriptor;
+ // Returns the isolate/heap this cell object belongs to.
+ inline Isolate* isolate();
+ inline Heap* heap();
+
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSGlobalPropertyCell);
};
diff --git a/src/parser.cc b/src/parser.cc
index 13e0c33..cf84bfa 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -42,7 +42,6 @@
#include "string-stream.h"
#include "ast-inl.h"
-#include "jump-target-inl.h"
namespace v8 {
namespace internal {
@@ -88,12 +87,13 @@
RegExpBuilder::RegExpBuilder()
- : pending_empty_(false),
- characters_(NULL),
- terms_(),
- alternatives_()
+ : zone_(Isolate::Current()->zone()),
+ pending_empty_(false),
+ characters_(NULL),
+ terms_(),
+ alternatives_()
#ifdef DEBUG
- , last_added_(ADD_NONE)
+ , last_added_(ADD_NONE)
#endif
{}
@@ -101,7 +101,7 @@
void RegExpBuilder::FlushCharacters() {
pending_empty_ = false;
if (characters_ != NULL) {
- RegExpTree* atom = new RegExpAtom(characters_->ToConstVector());
+ RegExpTree* atom = new(zone()) RegExpAtom(characters_->ToConstVector());
characters_ = NULL;
text_.Add(atom);
LAST(ADD_ATOM);
@@ -117,7 +117,7 @@
} else if (num_text == 1) {
terms_.Add(text_.last());
} else {
- RegExpText* text = new RegExpText();
+ RegExpText* text = new(zone()) RegExpText();
for (int i = 0; i < num_text; i++)
text_.Get(i)->AppendToText(text);
terms_.Add(text);
@@ -178,7 +178,7 @@
} else if (num_terms == 1) {
alternative = terms_.last();
} else {
- alternative = new RegExpAlternative(terms_.GetList());
+ alternative = new(zone()) RegExpAlternative(terms_.GetList());
}
alternatives_.Add(alternative);
terms_.Clear();
@@ -195,7 +195,7 @@
if (num_alternatives == 1) {
return alternatives_.last();
}
- return new RegExpDisjunction(alternatives_.GetList());
+ return new(zone()) RegExpDisjunction(alternatives_.GetList());
}
@@ -214,11 +214,11 @@
int num_chars = char_vector.length();
if (num_chars > 1) {
Vector<const uc16> prefix = char_vector.SubVector(0, num_chars - 1);
- text_.Add(new RegExpAtom(prefix));
+ text_.Add(new(zone()) RegExpAtom(prefix));
char_vector = char_vector.SubVector(num_chars - 1, num_chars);
}
characters_ = NULL;
- atom = new RegExpAtom(char_vector);
+ atom = new(zone()) RegExpAtom(char_vector);
FlushText();
} else if (text_.length() > 0) {
ASSERT(last_added_ == ADD_ATOM);
@@ -241,7 +241,7 @@
UNREACHABLE();
return;
}
- terms_.Add(new RegExpQuantifier(min, max, type, atom));
+ terms_.Add(new(zone()) RegExpQuantifier(min, max, type, atom));
LAST(ADD_TERM);
}
@@ -408,7 +408,7 @@
Scope* Parser::NewScope(Scope* parent, Scope::Type type, bool inside_with) {
- Scope* result = new Scope(parent, type);
+ Scope* result = new(zone()) Scope(parent, type);
result->Initialize(inside_with);
return result;
}
@@ -462,7 +462,7 @@
// Parser's scope stack. The constructor sets the parser's top scope
// to the incoming scope, and the destructor resets it.
//
-// Additionlaly, it stores transient information used during parsing.
+// Additionally, it stores transient information used during parsing.
// These scopes are not kept around after parsing or referenced by syntax
// trees so they can be stack-allocated and hence used by the pre-parser.
@@ -496,9 +496,6 @@
void AddProperty() { expected_property_count_++; }
int expected_property_count() { return expected_property_count_; }
- void AddLoop() { loop_count_++; }
- bool ContainsLoops() const { return loop_count_ > 0; }
-
private:
// Captures the number of literals that need materialization in the
// function. Includes regexp literals, and boilerplate for object
@@ -513,15 +510,13 @@
bool only_simple_this_property_assignments_;
Handle<FixedArray> this_property_assignments_;
- // Captures the number of loops inside the scope.
- int loop_count_;
-
// Bookkeeping
Parser* parser_;
// Previous values
LexicalScope* lexical_scope_parent_;
Scope* previous_scope_;
int previous_with_nesting_level_;
+ unsigned previous_ast_node_id_;
};
@@ -530,14 +525,15 @@
expected_property_count_(0),
only_simple_this_property_assignments_(false),
this_property_assignments_(isolate->factory()->empty_fixed_array()),
- loop_count_(0),
parser_(parser),
lexical_scope_parent_(parser->lexical_scope_),
previous_scope_(parser->top_scope_),
- previous_with_nesting_level_(parser->with_nesting_level_) {
+ previous_with_nesting_level_(parser->with_nesting_level_),
+ previous_ast_node_id_(isolate->ast_node_id()) {
parser->top_scope_ = scope;
parser->lexical_scope_ = this;
parser->with_nesting_level_ = 0;
+ isolate->set_ast_node_id(AstNode::kFunctionEntryId + 1);
}
@@ -546,6 +542,7 @@
parser_->top_scope_ = previous_scope_;
parser_->lexical_scope_ = lexical_scope_parent_;
parser_->with_nesting_level_ = previous_with_nesting_level_;
+ parser_->isolate()->set_ast_node_id(previous_ast_node_id_);
}
@@ -579,7 +576,7 @@
: isolate_(script->GetIsolate()),
symbol_cache_(pre_data ? pre_data->symbol_count() : 0),
script_(script),
- scanner_(isolate_->scanner_constants()),
+ scanner_(isolate_->unicode_cache()),
top_scope_(NULL),
with_nesting_level_(0),
lexical_scope_(NULL),
@@ -601,7 +598,7 @@
HistogramTimerScope timer(isolate()->counters()->parse());
isolate()->counters()->total_parse_size()->Increment(source->length());
- fni_ = new FuncNameInferrer();
+ fni_ = new(zone()) FuncNameInferrer();
// Initialize parser state.
source->TryFlatten();
@@ -652,7 +649,7 @@
CheckOctalLiteral(beg_loc, scanner().location().end_pos, &ok);
}
if (ok) {
- result = new FunctionLiteral(
+ result = new(zone()) FunctionLiteral(
no_name,
top_scope_,
body,
@@ -663,8 +660,7 @@
0,
0,
source->length(),
- false,
- lexical_scope.ContainsLoops());
+ false);
} else if (stack_overflow_) {
isolate()->StackOverflow();
}
@@ -713,7 +709,7 @@
ASSERT(target_stack_ == NULL);
Handle<String> name(String::cast(shared_info->name()));
- fni_ = new FuncNameInferrer();
+ fni_ = new(zone()) FuncNameInferrer();
fni_->PushEnclosingName(name);
mode_ = PARSE_EAGERLY;
@@ -1252,7 +1248,7 @@
// one must take great care not to treat it as a
// fall-through. It is much easier just to wrap the entire
// try-statement in a statement block and put the labels there
- Block* result = new Block(labels, 1, false);
+ Block* result = new(zone()) Block(labels, 1, false);
Target target(&this->target_stack_, result);
TryStatement* statement = ParseTryStatement(CHECK_OK);
if (statement) {
@@ -1350,13 +1346,13 @@
// a performance issue since it may lead to repeated
// Runtime::DeclareContextSlot() calls.
VariableProxy* proxy = top_scope_->NewUnresolved(name, inside_with());
- top_scope_->AddDeclaration(new Declaration(proxy, mode, fun));
+ top_scope_->AddDeclaration(new(zone()) Declaration(proxy, mode, fun));
// For global const variables we bind the proxy to a variable.
if (mode == Variable::CONST && top_scope_->is_global_scope()) {
ASSERT(resolve); // should be set by all callers
Variable::Kind kind = Variable::NORMAL;
- var = new Variable(top_scope_, name, Variable::CONST, true, kind);
+ var = new(zone()) Variable(top_scope_, name, Variable::CONST, true, kind);
}
// If requested and we have a local variable, bind the proxy to the variable
@@ -1444,10 +1440,11 @@
// TODO(1240846): It's weird that native function declarations are
// introduced dynamically when we meet their declarations, whereas
// other functions are setup when entering the surrounding scope.
- SharedFunctionInfoLiteral* lit = new SharedFunctionInfoLiteral(shared);
+ SharedFunctionInfoLiteral* lit =
+ new(zone()) SharedFunctionInfoLiteral(shared);
VariableProxy* var = Declare(name, Variable::VAR, NULL, true, CHECK_OK);
- return new ExpressionStatement(
- new Assignment(Token::INIT_VAR, var, lit, RelocInfo::kNoPosition));
+ return new(zone()) ExpressionStatement(new(zone()) Assignment(
+ Token::INIT_VAR, var, lit, RelocInfo::kNoPosition));
}
@@ -1479,7 +1476,7 @@
// (ECMA-262, 3rd, 12.2)
//
// Construct block expecting 16 statements.
- Block* result = new Block(labels, 16, false);
+ Block* result = new(zone()) Block(labels, 16, false);
Target target(&this->target_stack_, result);
Expect(Token::LBRACE, CHECK_OK);
while (peek() != Token::RBRACE) {
@@ -1549,7 +1546,7 @@
// is inside an initializer block, it is ignored.
//
// Create new block with one expected declaration.
- Block* block = new Block(NULL, 1, true);
+ Block* block = new(zone()) Block(NULL, 1, true);
VariableProxy* last_var = NULL; // the last variable declared
int nvars = 0; // the number of variables declared
do {
@@ -1650,7 +1647,8 @@
if (top_scope_->is_global_scope()) {
// Compute the arguments for the runtime call.
ZoneList<Expression*>* arguments = new ZoneList<Expression*>(3);
- arguments->Add(new Literal(name)); // we have at least 1 parameter
+ // We have at least 1 parameter.
+ arguments->Add(new(zone()) Literal(name));
CallRuntime* initialize;
if (is_const) {
@@ -1662,7 +1660,7 @@
// Note that the function does different things depending on
// the number of arguments (1 or 2).
initialize =
- new CallRuntime(
+ new(zone()) CallRuntime(
isolate()->factory()->InitializeConstGlobal_symbol(),
Runtime::FunctionForId(Runtime::kInitializeConstGlobal),
arguments);
@@ -1686,13 +1684,13 @@
// Note that the function does different things depending on
// the number of arguments (2 or 3).
initialize =
- new CallRuntime(
+ new(zone()) CallRuntime(
isolate()->factory()->InitializeVarGlobal_symbol(),
Runtime::FunctionForId(Runtime::kInitializeVarGlobal),
arguments);
}
- block->AddStatement(new ExpressionStatement(initialize));
+ block->AddStatement(new(zone()) ExpressionStatement(initialize));
}
// Add an assignment node to the initialization statement block if
@@ -1707,8 +1705,11 @@
// the top context for variables). Sigh...
if (value != NULL) {
Token::Value op = (is_const ? Token::INIT_CONST : Token::INIT_VAR);
- Assignment* assignment = new Assignment(op, last_var, value, position);
- if (block) block->AddStatement(new ExpressionStatement(assignment));
+ Assignment* assignment =
+ new(zone()) Assignment(op, last_var, value, position);
+ if (block) {
+ block->AddStatement(new(zone()) ExpressionStatement(assignment));
+ }
}
if (fni_ != NULL) fni_->Leave();
@@ -1774,7 +1775,7 @@
// Parsed expression statement.
ExpectSemicolon(CHECK_OK);
- return new ExpressionStatement(expr);
+ return new(zone()) ExpressionStatement(expr);
}
@@ -1794,7 +1795,7 @@
} else {
else_statement = EmptyStatement();
}
- return new IfStatement(condition, then_statement, else_statement);
+ return new(zone()) IfStatement(condition, then_statement, else_statement);
}
@@ -1824,7 +1825,7 @@
return NULL;
}
ExpectSemicolon(CHECK_OK);
- return new ContinueStatement(target);
+ return new(zone()) ContinueStatement(target);
}
@@ -1859,7 +1860,7 @@
return NULL;
}
ExpectSemicolon(CHECK_OK);
- return new BreakStatement(target);
+ return new(zone()) BreakStatement(target);
}
@@ -1880,7 +1881,7 @@
if (!top_scope_->is_function_scope()) {
Handle<String> type = isolate()->factory()->illegal_return_symbol();
Expression* throw_error = NewThrowSyntaxError(type, Handle<Object>::null());
- return new ExpressionStatement(throw_error);
+ return new(zone()) ExpressionStatement(throw_error);
}
Token::Value tok = peek();
@@ -1889,12 +1890,12 @@
tok == Token::RBRACE ||
tok == Token::EOS) {
ExpectSemicolon(CHECK_OK);
- return new ReturnStatement(GetLiteralUndefined());
+ return new(zone()) ReturnStatement(GetLiteralUndefined());
}
Expression* expr = ParseExpression(true, CHECK_OK);
ExpectSemicolon(CHECK_OK);
- return new ReturnStatement(expr);
+ return new(zone()) ReturnStatement(expr);
}
@@ -1903,7 +1904,7 @@
bool is_catch_block,
bool* ok) {
// Parse the statement and collect escaping labels.
- ZoneList<BreakTarget*>* target_list = new ZoneList<BreakTarget*>(0);
+ ZoneList<Label*>* target_list = new ZoneList<Label*>(0);
TargetCollector collector(target_list);
Statement* stat;
{ Target target(&this->target_stack_, &collector);
@@ -1915,21 +1916,21 @@
// Create resulting block with two statements.
// 1: Evaluate the with expression.
// 2: The try-finally block evaluating the body.
- Block* result = new Block(NULL, 2, false);
+ Block* result = new(zone()) Block(NULL, 2, false);
if (result != NULL) {
- result->AddStatement(new WithEnterStatement(obj, is_catch_block));
+ result->AddStatement(new(zone()) WithEnterStatement(obj, is_catch_block));
// Create body block.
- Block* body = new Block(NULL, 1, false);
+ Block* body = new(zone()) Block(NULL, 1, false);
body->AddStatement(stat);
// Create exit block.
- Block* exit = new Block(NULL, 1, false);
- exit->AddStatement(new WithExitStatement());
+ Block* exit = new(zone()) Block(NULL, 1, false);
+ exit->AddStatement(new(zone()) WithExitStatement());
// Return a try-finally statement.
- TryFinallyStatement* wrapper = new TryFinallyStatement(body, exit);
+ TryFinallyStatement* wrapper = new(zone()) TryFinallyStatement(body, exit);
wrapper->set_escaping_targets(collector.targets());
result->AddStatement(wrapper);
}
@@ -1986,7 +1987,7 @@
statements->Add(stat);
}
- return new CaseClause(label, statements, pos);
+ return new(zone()) CaseClause(label, statements, pos);
}
@@ -1995,7 +1996,7 @@
// SwitchStatement ::
// 'switch' '(' Expression ')' '{' CaseClause* '}'
- SwitchStatement* statement = new SwitchStatement(labels);
+ SwitchStatement* statement = new(zone()) SwitchStatement(labels);
Target target(&this->target_stack_, statement);
Expect(Token::SWITCH, CHECK_OK);
@@ -2031,7 +2032,7 @@
Expression* exception = ParseExpression(true, CHECK_OK);
ExpectSemicolon(CHECK_OK);
- return new ExpressionStatement(new Throw(exception, pos));
+ return new(zone()) ExpressionStatement(new(zone()) Throw(exception, pos));
}
@@ -2049,7 +2050,7 @@
Expect(Token::TRY, CHECK_OK);
- ZoneList<BreakTarget*>* target_list = new ZoneList<BreakTarget*>(0);
+ ZoneList<Label*>* target_list = new ZoneList<Label*>(0);
TargetCollector collector(target_list);
Block* try_block;
@@ -2072,7 +2073,7 @@
// then we will need to collect jump targets from the catch block. Since
// we don't know yet if there will be a finally block, we always collect
// the jump targets.
- ZoneList<BreakTarget*>* catch_target_list = new ZoneList<BreakTarget*>(0);
+ ZoneList<Label*>* catch_target_list = new ZoneList<Label*>(0);
TargetCollector catch_collector(catch_target_list);
bool has_catch = false;
if (tok == Token::CATCH) {
@@ -2095,9 +2096,10 @@
// executing the finally block.
catch_var =
top_scope_->NewTemporary(isolate()->factory()->catch_var_symbol());
- Literal* name_literal = new Literal(name);
- VariableProxy* catch_var_use = new VariableProxy(catch_var);
- Expression* obj = new CatchExtensionObject(name_literal, catch_var_use);
+ Literal* name_literal = new(zone()) Literal(name);
+ VariableProxy* catch_var_use = new(zone()) VariableProxy(catch_var);
+ Expression* obj =
+ new(zone()) CatchExtensionObject(name_literal, catch_var_use);
{ Target target(&this->target_stack_, &catch_collector);
catch_block = WithHelper(obj, NULL, true, CHECK_OK);
}
@@ -2121,11 +2123,11 @@
// 'try { try { } catch { } } finally { }'
if (catch_block != NULL && finally_block != NULL) {
- VariableProxy* catch_var_defn = new VariableProxy(catch_var);
+ VariableProxy* catch_var_defn = new(zone()) VariableProxy(catch_var);
TryCatchStatement* statement =
- new TryCatchStatement(try_block, catch_var_defn, catch_block);
+ new(zone()) TryCatchStatement(try_block, catch_var_defn, catch_block);
statement->set_escaping_targets(collector.targets());
- try_block = new Block(NULL, 1, false);
+ try_block = new(zone()) Block(NULL, 1, false);
try_block->AddStatement(statement);
catch_block = NULL;
}
@@ -2133,12 +2135,13 @@
TryStatement* result = NULL;
if (catch_block != NULL) {
ASSERT(finally_block == NULL);
- VariableProxy* catch_var_defn = new VariableProxy(catch_var);
- result = new TryCatchStatement(try_block, catch_var_defn, catch_block);
+ VariableProxy* catch_var_defn = new(zone()) VariableProxy(catch_var);
+ result =
+ new(zone()) TryCatchStatement(try_block, catch_var_defn, catch_block);
result->set_escaping_targets(collector.targets());
} else {
ASSERT(finally_block != NULL);
- result = new TryFinallyStatement(try_block, finally_block);
+ result = new(zone()) TryFinallyStatement(try_block, finally_block);
// Add the jump targets of the try block and the catch block.
for (int i = 0; i < collector.targets()->length(); i++) {
catch_collector.AddTarget(collector.targets()->at(i));
@@ -2155,8 +2158,7 @@
// DoStatement ::
// 'do' Statement 'while' '(' Expression ')' ';'
- lexical_scope_->AddLoop();
- DoWhileStatement* loop = new DoWhileStatement(labels);
+ DoWhileStatement* loop = new(zone()) DoWhileStatement(labels);
Target target(&this->target_stack_, loop);
Expect(Token::DO, CHECK_OK);
@@ -2170,7 +2172,6 @@
}
Expression* cond = ParseExpression(true, CHECK_OK);
- if (cond != NULL) cond->set_is_loop_condition(true);
Expect(Token::RPAREN, CHECK_OK);
// Allow do-statements to be terminated with and without
@@ -2188,14 +2189,12 @@
// WhileStatement ::
// 'while' '(' Expression ')' Statement
- lexical_scope_->AddLoop();
- WhileStatement* loop = new WhileStatement(labels);
+ WhileStatement* loop = new(zone()) WhileStatement(labels);
Target target(&this->target_stack_, loop);
Expect(Token::WHILE, CHECK_OK);
Expect(Token::LPAREN, CHECK_OK);
Expression* cond = ParseExpression(true, CHECK_OK);
- if (cond != NULL) cond->set_is_loop_condition(true);
Expect(Token::RPAREN, CHECK_OK);
Statement* body = ParseStatement(NULL, CHECK_OK);
@@ -2208,7 +2207,6 @@
// ForStatement ::
// 'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement
- lexical_scope_->AddLoop();
Statement* init = NULL;
Expect(Token::FOR, CHECK_OK);
@@ -2219,7 +2217,7 @@
Block* variable_statement =
ParseVariableDeclarations(false, &each, CHECK_OK);
if (peek() == Token::IN && each != NULL) {
- ForInStatement* loop = new ForInStatement(labels);
+ ForInStatement* loop = new(zone()) ForInStatement(labels);
Target target(&this->target_stack_, loop);
Expect(Token::IN, CHECK_OK);
@@ -2228,7 +2226,7 @@
Statement* body = ParseStatement(NULL, CHECK_OK);
loop->Initialize(each, enumerable, body);
- Block* result = new Block(NULL, 2, false);
+ Block* result = new(zone()) Block(NULL, 2, false);
result->AddStatement(variable_statement);
result->AddStatement(loop);
// Parsed for-in loop w/ variable/const declaration.
@@ -2249,7 +2247,7 @@
isolate()->factory()->invalid_lhs_in_for_in_symbol();
expression = NewThrowReferenceError(type);
}
- ForInStatement* loop = new ForInStatement(labels);
+ ForInStatement* loop = new(zone()) ForInStatement(labels);
Target target(&this->target_stack_, loop);
Expect(Token::IN, CHECK_OK);
@@ -2262,13 +2260,13 @@
return loop;
} else {
- init = new ExpressionStatement(expression);
+ init = new(zone()) ExpressionStatement(expression);
}
}
}
// Standard 'for' loop
- ForStatement* loop = new ForStatement(labels);
+ ForStatement* loop = new(zone()) ForStatement(labels);
Target target(&this->target_stack_, loop);
// Parsed initializer at this point.
@@ -2277,14 +2275,13 @@
Expression* cond = NULL;
if (peek() != Token::SEMICOLON) {
cond = ParseExpression(true, CHECK_OK);
- if (cond != NULL) cond->set_is_loop_condition(true);
}
Expect(Token::SEMICOLON, CHECK_OK);
Statement* next = NULL;
if (peek() != Token::RPAREN) {
Expression* exp = ParseExpression(true, CHECK_OK);
- next = new ExpressionStatement(exp);
+ next = new(zone()) ExpressionStatement(exp);
}
Expect(Token::RPAREN, CHECK_OK);
@@ -2305,7 +2302,7 @@
Expect(Token::COMMA, CHECK_OK);
int position = scanner().location().beg_pos;
Expression* right = ParseAssignmentExpression(accept_IN, CHECK_OK);
- result = new BinaryOperation(Token::COMMA, result, right, position);
+ result = new(zone()) BinaryOperation(Token::COMMA, result, right, position);
}
return result;
}
@@ -2377,7 +2374,7 @@
fni_->Leave();
}
- return new Assignment(op, expression, right, pos);
+ return new(zone()) Assignment(op, expression, right, pos);
}
@@ -2399,7 +2396,7 @@
Expect(Token::COLON, CHECK_OK);
int right_position = scanner().peek_location().beg_pos;
Expression* right = ParseAssignmentExpression(accept_IN, CHECK_OK);
- return new Conditional(expression, left, right,
+ return new(zone()) Conditional(expression, left, right,
left_position, right_position);
}
@@ -2487,12 +2484,12 @@
x = NewCompareNode(cmp, x, y, position);
if (cmp != op) {
// The comparison was negated - add a NOT.
- x = new UnaryOperation(Token::NOT, x);
+ x = new(zone()) UnaryOperation(Token::NOT, x);
}
} else {
// We have a "normal" binary operation.
- x = new BinaryOperation(op, x, y, position);
+ x = new(zone()) BinaryOperation(op, x, y, position);
}
}
}
@@ -2509,15 +2506,15 @@
bool is_strict = (op == Token::EQ_STRICT);
Literal* x_literal = x->AsLiteral();
if (x_literal != NULL && x_literal->IsNull()) {
- return new CompareToNull(is_strict, y);
+ return new(zone()) CompareToNull(is_strict, y);
}
Literal* y_literal = y->AsLiteral();
if (y_literal != NULL && y_literal->IsNull()) {
- return new CompareToNull(is_strict, x);
+ return new(zone()) CompareToNull(is_strict, x);
}
}
- return new CompareOperation(op, x, y, position);
+ return new(zone()) CompareOperation(op, x, y, position);
}
@@ -2564,7 +2561,7 @@
}
}
- return new UnaryOperation(op, expression);
+ return new(zone()) UnaryOperation(op, expression);
} else if (Token::IsCountOp(op)) {
op = Next();
@@ -2585,8 +2582,10 @@
}
int position = scanner().location().beg_pos;
- IncrementOperation* increment = new IncrementOperation(op, expression);
- return new CountOperation(true /* prefix */, increment, position);
+ return new(zone()) CountOperation(op,
+ true /* prefix */,
+ expression,
+ position);
} else {
return ParsePostfixExpression(ok);
@@ -2618,8 +2617,11 @@
Token::Value next = Next();
int position = scanner().location().beg_pos;
- IncrementOperation* increment = new IncrementOperation(next, expression);
- expression = new CountOperation(false /* postfix */, increment, position);
+ expression =
+ new(zone()) CountOperation(next,
+ false /* postfix */,
+ expression,
+ position);
}
return expression;
}
@@ -2642,7 +2644,7 @@
Consume(Token::LBRACK);
int pos = scanner().location().beg_pos;
Expression* index = ParseExpression(true, CHECK_OK);
- result = new Property(result, index, pos);
+ result = new(zone()) Property(result, index, pos);
Expect(Token::RBRACK, CHECK_OK);
break;
}
@@ -2680,7 +2682,7 @@
Consume(Token::PERIOD);
int pos = scanner().location().beg_pos;
Handle<String> name = ParseIdentifierName(CHECK_OK);
- result = new Property(result, new Literal(name), pos);
+ result = new(zone()) Property(result, new(zone()) Literal(name), pos);
if (fni_ != NULL) fni_->PushLiteralName(name);
break;
}
@@ -2716,7 +2718,7 @@
if (!stack->is_empty()) {
int last = stack->pop();
- result = new CallNew(result, new ZoneList<Expression*>(0), last);
+ result = new(zone()) CallNew(result, new ZoneList<Expression*>(0), last);
}
return result;
}
@@ -2761,7 +2763,7 @@
Consume(Token::LBRACK);
int pos = scanner().location().beg_pos;
Expression* index = ParseExpression(true, CHECK_OK);
- result = new Property(result, index, pos);
+ result = new(zone()) Property(result, index, pos);
Expect(Token::RBRACK, CHECK_OK);
break;
}
@@ -2769,7 +2771,7 @@
Consume(Token::PERIOD);
int pos = scanner().location().beg_pos;
Handle<String> name = ParseIdentifierName(CHECK_OK);
- result = new Property(result, new Literal(name), pos);
+ result = new(zone()) Property(result, new(zone()) Literal(name), pos);
if (fni_ != NULL) fni_->PushLiteralName(name);
break;
}
@@ -2797,7 +2799,7 @@
Expect(Token::DEBUGGER, CHECK_OK);
ExpectSemicolon(CHECK_OK);
- return new DebuggerStatement();
+ return new(zone()) DebuggerStatement();
}
@@ -2866,31 +2868,34 @@
case Token::NULL_LITERAL:
Consume(Token::NULL_LITERAL);
- result = new Literal(isolate()->factory()->null_value());
+ result = new(zone()) Literal(isolate()->factory()->null_value());
break;
case Token::TRUE_LITERAL:
Consume(Token::TRUE_LITERAL);
- result = new Literal(isolate()->factory()->true_value());
+ result = new(zone()) Literal(isolate()->factory()->true_value());
break;
case Token::FALSE_LITERAL:
Consume(Token::FALSE_LITERAL);
- result = new Literal(isolate()->factory()->false_value());
+ result = new(zone()) Literal(isolate()->factory()->false_value());
break;
case Token::IDENTIFIER:
case Token::FUTURE_RESERVED_WORD: {
Handle<String> name = ParseIdentifier(CHECK_OK);
if (fni_ != NULL) fni_->PushVariableName(name);
- result = top_scope_->NewUnresolved(name, inside_with());
+ result = top_scope_->NewUnresolved(name,
+ inside_with(),
+ scanner().location().beg_pos);
break;
}
case Token::NUMBER: {
Consume(Token::NUMBER);
ASSERT(scanner().is_literal_ascii());
- double value = StringToDouble(scanner().literal_ascii_string(),
+ double value = StringToDouble(isolate()->unicode_cache(),
+ scanner().literal_ascii_string(),
ALLOW_HEX | ALLOW_OCTALS);
result = NewNumberLiteral(value);
break;
@@ -2899,7 +2904,7 @@
case Token::STRING: {
Consume(Token::STRING);
Handle<String> symbol = GetSymbol(CHECK_OK);
- result = new Literal(symbol);
+ result = new(zone()) Literal(symbol);
if (fni_ != NULL) fni_->PushLiteralName(symbol);
break;
}
@@ -3026,7 +3031,7 @@
literals->set_map(isolate()->heap()->fixed_cow_array_map());
}
- return new ArrayLiteral(literals, values,
+ return new(zone()) ArrayLiteral(literals, values,
literal_index, is_simple, depth);
}
@@ -3304,7 +3309,7 @@
// Allow any number of parameters for compatiabilty with JSC.
// Specification only allows zero parameters for get and one for set.
ObjectLiteral::Property* property =
- new ObjectLiteral::Property(is_getter, value);
+ new(zone()) ObjectLiteral::Property(is_getter, value);
return property;
} else {
ReportUnexpectedToken(next);
@@ -3370,7 +3375,7 @@
}
// Failed to parse as get/set property, so it's just a property
// called "get" or "set".
- key = new Literal(id);
+ key = new(zone()) Literal(id);
break;
}
case Token::STRING: {
@@ -3382,13 +3387,14 @@
key = NewNumberLiteral(index);
break;
}
- key = new Literal(string);
+ key = new(zone()) Literal(string);
break;
}
case Token::NUMBER: {
Consume(Token::NUMBER);
ASSERT(scanner().is_literal_ascii());
- double value = StringToDouble(scanner().literal_ascii_string(),
+ double value = StringToDouble(isolate()->unicode_cache(),
+ scanner().literal_ascii_string(),
ALLOW_HEX | ALLOW_OCTALS);
key = NewNumberLiteral(value);
break;
@@ -3397,7 +3403,7 @@
if (Token::IsKeyword(next)) {
Consume(next);
Handle<String> string = GetSymbol(CHECK_OK);
- key = new Literal(string);
+ key = new(zone()) Literal(string);
} else {
// Unexpected token.
Token::Value next = Next();
@@ -3411,7 +3417,7 @@
Expression* value = ParseAssignmentExpression(true, CHECK_OK);
ObjectLiteral::Property* property =
- new ObjectLiteral::Property(key, value);
+ new(zone()) ObjectLiteral::Property(key, value);
// Mark object literals that contain function literals and pretenure the
// literal so it can be added as a constant function property.
@@ -3450,7 +3456,7 @@
&is_simple,
&fast_elements,
&depth);
- return new ObjectLiteral(constant_properties,
+ return new(zone()) ObjectLiteral(constant_properties,
properties,
literal_index,
is_simple,
@@ -3475,7 +3481,7 @@
Handle<String> js_flags = NextLiteralString(TENURED);
Next();
- return new RegExpLiteral(js_pattern, js_flags, literal_index);
+ return new(zone()) RegExpLiteral(js_pattern, js_flags, literal_index);
}
@@ -3519,16 +3525,22 @@
}
int num_parameters = 0;
+ Scope* scope = NewScope(top_scope_, Scope::FUNCTION_SCOPE, inside_with());
+ ZoneList<Statement*>* body = new ZoneList<Statement*>(8);
+ int materialized_literal_count;
+ int expected_property_count;
+ int start_pos;
+ int end_pos;
+ bool only_simple_this_property_assignments;
+ Handle<FixedArray> this_property_assignments;
// Parse function body.
- { Scope* scope =
- NewScope(top_scope_, Scope::FUNCTION_SCOPE, inside_with());
- LexicalScope lexical_scope(this, scope, isolate());
+ { LexicalScope lexical_scope(this, scope, isolate());
top_scope_->SetScopeName(name);
// FormalParameterList ::
// '(' (Identifier)*[','] ')'
Expect(Token::LPAREN, CHECK_OK);
- int start_pos = scanner().location().beg_pos;
+ start_pos = scanner().location().beg_pos;
Scanner::Location name_loc = Scanner::NoLocation();
Scanner::Location dupe_loc = Scanner::NoLocation();
Scanner::Location reserved_loc = Scanner::NoLocation();
@@ -3565,7 +3577,6 @@
Expect(Token::RPAREN, CHECK_OK);
Expect(Token::LBRACE, CHECK_OK);
- ZoneList<Statement*>* body = new ZoneList<Statement*>(8);
// If we have a named function expression, we add a local variable
// declaration to the body of the function with the name of the
@@ -3578,9 +3589,9 @@
VariableProxy* fproxy =
top_scope_->NewUnresolved(function_name, inside_with());
fproxy->BindTo(fvar);
- body->Add(new ExpressionStatement(
- new Assignment(Token::INIT_CONST, fproxy,
- new ThisFunction(),
+ body->Add(new(zone()) ExpressionStatement(
+ new(zone()) Assignment(Token::INIT_CONST, fproxy,
+ new(zone()) ThisFunction(),
RelocInfo::kNoPosition)));
}
@@ -3593,11 +3604,6 @@
parenthesized_function_ = false; // The bit was set for this function only.
int function_block_pos = scanner().location().beg_pos;
- int materialized_literal_count;
- int expected_property_count;
- int end_pos;
- bool only_simple_this_property_assignments;
- Handle<FixedArray> this_property_assignments;
if (is_lazily_compiled && pre_data() != NULL) {
FunctionEntry entry = pre_data()->GetFunctionEntry(function_block_pos);
if (!entry.is_valid()) {
@@ -3672,25 +3678,24 @@
}
CheckOctalLiteral(start_pos, end_pos, CHECK_OK);
}
-
- FunctionLiteral* function_literal =
- new FunctionLiteral(name,
- top_scope_,
- body,
- materialized_literal_count,
- expected_property_count,
- only_simple_this_property_assignments,
- this_property_assignments,
- num_parameters,
- start_pos,
- end_pos,
- function_name->length() > 0,
- lexical_scope.ContainsLoops());
- function_literal->set_function_token_position(function_token_position);
-
- if (fni_ != NULL && !is_named) fni_->AddFunction(function_literal);
- return function_literal;
}
+
+ FunctionLiteral* function_literal =
+ new(zone()) FunctionLiteral(name,
+ scope,
+ body,
+ materialized_literal_count,
+ expected_property_count,
+ only_simple_this_property_assignments,
+ this_property_assignments,
+ num_parameters,
+ start_pos,
+ end_pos,
+ (function_name->length() > 0));
+ function_literal->set_function_token_position(function_token_position);
+
+ if (fni_ != NULL && !is_named) fni_->AddFunction(function_literal);
+ return function_literal;
}
@@ -3736,7 +3741,7 @@
}
// We have a valid intrinsics call or a call to a builtin.
- return new CallRuntime(name, function, args);
+ return new(zone()) CallRuntime(name, function, args);
}
@@ -3791,12 +3796,12 @@
Literal* Parser::GetLiteralUndefined() {
- return new Literal(isolate()->factory()->undefined_value());
+ return new(zone()) Literal(isolate()->factory()->undefined_value());
}
Literal* Parser::GetLiteralTheHole() {
- return new Literal(isolate()->factory()->the_hole_value());
+ return new(zone()) Literal(isolate()->factory()->the_hole_value());
}
@@ -3932,7 +3937,7 @@
}
-void Parser::RegisterTargetUse(BreakTarget* target, Target* stop) {
+void Parser::RegisterTargetUse(Label* target, Target* stop) {
// Register that a break target found at the given stop in the
// target stack has been used from the top of the target stack. Add
// the break target to any TargetCollectors passed on the stack.
@@ -3944,7 +3949,7 @@
Literal* Parser::NewNumberLiteral(double number) {
- return new Literal(isolate()->factory()->NewNumber(number, TENURED));
+ return new(zone()) Literal(isolate()->factory()->NewNumber(number, TENURED));
}
@@ -3991,9 +3996,9 @@
TENURED);
ZoneList<Expression*>* args = new ZoneList<Expression*>(2);
- args->Add(new Literal(type));
- args->Add(new Literal(array));
- return new Throw(new CallRuntime(constructor, NULL, args),
+ args->Add(new(zone()) Literal(type));
+ args->Add(new(zone()) Literal(array));
+ return new(zone()) Throw(new(zone()) CallRuntime(constructor, NULL, args),
scanner().location().beg_pos);
}
@@ -4316,13 +4321,13 @@
// Build result of subexpression.
if (type == CAPTURE) {
- RegExpCapture* capture = new RegExpCapture(body, capture_index);
+ RegExpCapture* capture = new(zone()) RegExpCapture(body, capture_index);
captures_->at(capture_index - 1) = capture;
body = capture;
} else if (type != GROUPING) {
ASSERT(type == POSITIVE_LOOKAHEAD || type == NEGATIVE_LOOKAHEAD);
bool is_positive = (type == POSITIVE_LOOKAHEAD);
- body = new RegExpLookahead(body,
+ body = new(zone()) RegExpLookahead(body,
is_positive,
end_capture_index - capture_index,
capture_index);
@@ -4345,10 +4350,10 @@
Advance();
if (multiline_) {
builder->AddAssertion(
- new RegExpAssertion(RegExpAssertion::START_OF_LINE));
+ new(zone()) RegExpAssertion(RegExpAssertion::START_OF_LINE));
} else {
builder->AddAssertion(
- new RegExpAssertion(RegExpAssertion::START_OF_INPUT));
+ new(zone()) RegExpAssertion(RegExpAssertion::START_OF_INPUT));
set_contains_anchor();
}
continue;
@@ -4358,7 +4363,7 @@
RegExpAssertion::Type type =
multiline_ ? RegExpAssertion::END_OF_LINE :
RegExpAssertion::END_OF_INPUT;
- builder->AddAssertion(new RegExpAssertion(type));
+ builder->AddAssertion(new(zone()) RegExpAssertion(type));
continue;
}
case '.': {
@@ -4366,7 +4371,7 @@
// everything except \x0a, \x0d, \u2028 and \u2029
ZoneList<CharacterRange>* ranges = new ZoneList<CharacterRange>(2);
CharacterRange::AddClassEscape('.', ranges);
- RegExpTree* atom = new RegExpCharacterClass(ranges, false);
+ RegExpTree* atom = new(zone()) RegExpCharacterClass(ranges, false);
builder->AddAtom(atom);
break;
}
@@ -4399,7 +4404,7 @@
captures_->Add(NULL);
}
// Store current state and begin new disjunction parsing.
- stored_state = new RegExpParserState(stored_state,
+ stored_state = new(zone()) RegExpParserState(stored_state,
type,
captures_started());
builder = stored_state->builder();
@@ -4419,12 +4424,12 @@
case 'b':
Advance(2);
builder->AddAssertion(
- new RegExpAssertion(RegExpAssertion::BOUNDARY));
+ new(zone()) RegExpAssertion(RegExpAssertion::BOUNDARY));
continue;
case 'B':
Advance(2);
builder->AddAssertion(
- new RegExpAssertion(RegExpAssertion::NON_BOUNDARY));
+ new(zone()) RegExpAssertion(RegExpAssertion::NON_BOUNDARY));
continue;
// AtomEscape ::
// CharacterClassEscape
@@ -4436,7 +4441,7 @@
Advance(2);
ZoneList<CharacterRange>* ranges = new ZoneList<CharacterRange>(2);
CharacterRange::AddClassEscape(c, ranges);
- RegExpTree* atom = new RegExpCharacterClass(ranges, false);
+ RegExpTree* atom = new(zone()) RegExpCharacterClass(ranges, false);
builder->AddAtom(atom);
break;
}
@@ -4452,7 +4457,7 @@
builder->AddEmpty();
break;
}
- RegExpTree* atom = new RegExpBackReference(capture);
+ RegExpTree* atom = new(zone()) RegExpBackReference(capture);
builder->AddAtom(atom);
break;
}
@@ -4970,7 +4975,7 @@
ranges->Add(CharacterRange::Everything());
is_negated = !is_negated;
}
- return new RegExpCharacterClass(ranges, is_negated);
+ return new(zone()) RegExpCharacterClass(ranges, is_negated);
}
@@ -5053,7 +5058,7 @@
bool allow_lazy,
ParserRecorder* recorder) {
Isolate* isolate = Isolate::Current();
- V8JavaScriptScanner scanner(isolate->scanner_constants());
+ V8JavaScriptScanner scanner(isolate->unicode_cache());
scanner.Initialize(source);
intptr_t stack_limit = isolate->stack_guard()->real_climit();
if (!preparser::PreParser::PreParseProgram(&scanner,
diff --git a/src/parser.h b/src/parser.h
index 74cb049..a63651a 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -280,6 +280,9 @@
void FlushCharacters();
void FlushText();
void FlushTerms();
+ Zone* zone() { return zone_; }
+
+ Zone* zone_;
bool pending_empty_;
ZoneList<uc16>* characters_;
BufferedZoneList<RegExpTree, 2> terms_;
@@ -389,6 +392,7 @@
};
Isolate* isolate() { return isolate_; }
+ Zone* zone() { return isolate_->zone(); }
uc32 current() { return current_; }
bool has_more() { return has_more_; }
@@ -453,6 +457,7 @@
};
Isolate* isolate() { return isolate_; }
+ Zone* zone() { return isolate_->zone(); }
// Called by ParseProgram after setting up the scanner.
FunctionLiteral* DoParseProgram(Handle<String> source,
@@ -650,7 +655,7 @@
BreakableStatement* LookupBreakTarget(Handle<String> label, bool* ok);
IterationStatement* LookupContinueTarget(Handle<String> label, bool* ok);
- void RegisterTargetUse(BreakTarget* target, Target* stop);
+ void RegisterTargetUse(Label* target, Target* stop);
// Factory methods.
@@ -778,7 +783,7 @@
private:
JsonParser()
: isolate_(Isolate::Current()),
- scanner_(isolate_->scanner_constants()) { }
+ scanner_(isolate_->unicode_cache()) { }
~JsonParser() { }
Isolate* isolate() { return isolate_; }
diff --git a/src/platform-cygwin.cc b/src/platform-cygwin.cc
index 4b450c1..6511328 100644
--- a/src/platform-cygwin.cc
+++ b/src/platform-cygwin.cc
@@ -42,7 +42,6 @@
#include "v8.h"
#include "platform.h"
-#include "top.h"
#include "v8threads.h"
#include "vm-state-inl.h"
#include "win32-headers.h"
@@ -59,6 +58,9 @@
}
+static Mutex* limit_mutex = NULL;
+
+
void OS::Setup() {
// Seed the random number generator.
// Convert the current time to a 64-bit integer first, before converting it
@@ -67,6 +69,7 @@
// call this setup code within the same millisecond.
uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
srandom(static_cast<unsigned int>(seed));
+ limit_mutex = CreateMutex();
}
@@ -119,6 +122,9 @@
static void UpdateAllocatedSpaceLimits(void* address, int size) {
+ ASSERT(limit_mutex != NULL);
+ ScopedLock lock(limit_mutex);
+
lowest_ever_allocated = Min(lowest_ever_allocated, address);
highest_ever_allocated =
Max(highest_ever_allocated,
@@ -254,6 +260,7 @@
const int kLibNameLen = FILENAME_MAX + 1;
char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen));
+ i::Isolate* isolate = ISOLATE;
// This loop will terminate once the scanning hits an EOF.
while (true) {
uintptr_t start, end;
@@ -287,7 +294,7 @@
snprintf(lib_name, kLibNameLen,
"%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end);
}
- LOG(SharedLibraryEvent(lib_name, start, end));
+ LOG(isolate, SharedLibraryEvent(lib_name, start, end));
} else {
// Entry not describing executable data. Skip to end of line to setup
// reading the next entry.
@@ -314,94 +321,58 @@
}
-// Constants used for mmap.
-static const int kMmapFd = -1;
-static const int kMmapFdOffset = 0;
+// The VirtualMemory implementation is taken from platform-win32.cc.
+// The mmap-based virtual memory implementation as it is used on most posix
+// platforms does not work well because Cygwin does not support MAP_FIXED.
+// This causes VirtualMemory::Commit to not always commit the memory region
+// specified.
+
+bool VirtualMemory::IsReserved() {
+ return address_ != NULL;
+}
VirtualMemory::VirtualMemory(size_t size) {
- address_ = mmap(NULL, size, PROT_NONE,
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
- kMmapFd, kMmapFdOffset);
+ address_ = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS);
size_ = size;
}
VirtualMemory::~VirtualMemory() {
if (IsReserved()) {
- if (0 == munmap(address(), size())) address_ = MAP_FAILED;
+ if (0 == VirtualFree(address(), 0, MEM_RELEASE)) address_ = NULL;
}
}
-bool VirtualMemory::IsReserved() {
- return address_ != MAP_FAILED;
-}
-
-
bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
- int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
-
- if (mprotect(address, size, prot) != 0) {
+ int prot = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
+ if (NULL == VirtualAlloc(address, size, MEM_COMMIT, prot)) {
return false;
}
- UpdateAllocatedSpaceLimits(address, size);
+ UpdateAllocatedSpaceLimits(address, static_cast<int>(size));
return true;
}
bool VirtualMemory::Uncommit(void* address, size_t size) {
- return mmap(address, size, PROT_NONE,
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
- kMmapFd, kMmapFdOffset) != MAP_FAILED;
+ ASSERT(IsReserved());
+ return VirtualFree(address, size, MEM_DECOMMIT) != false;
}
-class ThreadHandle::PlatformData : public Malloced {
+class Thread::PlatformData : public Malloced {
public:
- explicit PlatformData(ThreadHandle::Kind kind) {
- Initialize(kind);
- }
-
- void Initialize(ThreadHandle::Kind kind) {
- switch (kind) {
- case ThreadHandle::SELF: thread_ = pthread_self(); break;
- case ThreadHandle::INVALID: thread_ = kNoThread; break;
- }
- }
-
+ PlatformData() : thread_(kNoThread) {}
pthread_t thread_; // Thread handle for pthread.
};
-ThreadHandle::ThreadHandle(Kind kind) {
- data_ = new PlatformData(kind);
-}
-
-
-void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
- data_->Initialize(kind);
-}
-
-
-ThreadHandle::~ThreadHandle() {
- delete data_;
-}
-
-
-bool ThreadHandle::IsSelf() const {
- return pthread_equal(data_->thread_, pthread_self());
-}
-
-
-bool ThreadHandle::IsValid() const {
- return data_->thread_ != kNoThread;
-}
Thread::Thread(Isolate* isolate, const Options& options)
- : ThreadHandle(ThreadHandle::INVALID),
+ : data_(new PlatformData),
isolate_(isolate),
stack_size_(options.stack_size) {
set_name(options.name);
@@ -409,7 +380,7 @@
Thread::Thread(Isolate* isolate, const char* name)
- : ThreadHandle(ThreadHandle::INVALID),
+ : data_(new PlatformData),
isolate_(isolate),
stack_size_(0) {
set_name(name);
@@ -417,6 +388,7 @@
Thread::~Thread() {
+ delete data_;
}
@@ -425,8 +397,9 @@
// This is also initialized by the first argument to pthread_create() but we
// don't know which thread will run first (the original thread or the new
// one) so we initialize it here too.
- thread->thread_handle_data()->thread_ = pthread_self();
- ASSERT(thread->IsValid());
+ thread->data()->thread_ = pthread_self();
+ ASSERT(thread->data()->thread_ != kNoThread);
+ Thread::SetThreadLocal(Isolate::isolate_key(), thread->isolate());
thread->Run();
return NULL;
}
@@ -439,13 +412,20 @@
void Thread::Start() {
- pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
- ASSERT(IsValid());
+ pthread_attr_t* attr_ptr = NULL;
+ pthread_attr_t attr;
+ if (stack_size_ > 0) {
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
+ attr_ptr = &attr;
+ }
+ pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this);
+ ASSERT(data_->thread_ != kNoThread);
}
void Thread::Join() {
- pthread_join(thread_handle_data()->thread_, NULL);
+ pthread_join(data_->thread_, NULL);
}
@@ -623,128 +603,176 @@
class Sampler::PlatformData : public Malloced {
public:
- explicit PlatformData(Sampler* sampler) {
- sampler_ = sampler;
- sampler_thread_ = INVALID_HANDLE_VALUE;
- profiled_thread_ = INVALID_HANDLE_VALUE;
- }
-
- Sampler* sampler_;
- HANDLE sampler_thread_;
- HANDLE profiled_thread_;
- RuntimeProfilerRateLimiter rate_limiter_;
-
- // Sampler thread handler.
- void Runner() {
- while (sampler_->IsActive()) {
- if (rate_limiter_.SuspendIfNecessary()) continue;
- Sample();
- Sleep(sampler_->interval_);
- }
- }
-
- void Sample() {
- if (sampler_->IsProfiling()) {
- // Context used for sampling the register state of the profiled thread.
- CONTEXT context;
- memset(&context, 0, sizeof(context));
-
- TickSample sample_obj;
- TickSample* sample = CpuProfiler::TickSampleEvent();
- if (sample == NULL) sample = &sample_obj;
-
- static const DWORD kSuspendFailed = static_cast<DWORD>(-1);
- if (SuspendThread(profiled_thread_) == kSuspendFailed) return;
- sample->state = Top::current_vm_state();
-
- context.ContextFlags = CONTEXT_FULL;
- if (GetThreadContext(profiled_thread_, &context) != 0) {
-#if V8_HOST_ARCH_X64
- sample->pc = reinterpret_cast<Address>(context.Rip);
- sample->sp = reinterpret_cast<Address>(context.Rsp);
- sample->fp = reinterpret_cast<Address>(context.Rbp);
-#else
- sample->pc = reinterpret_cast<Address>(context.Eip);
- sample->sp = reinterpret_cast<Address>(context.Esp);
- sample->fp = reinterpret_cast<Address>(context.Ebp);
-#endif
- sampler_->SampleStack(sample);
- sampler_->Tick(sample);
- }
- ResumeThread(profiled_thread_);
- }
- if (RuntimeProfiler::IsEnabled()) RuntimeProfiler::NotifyTick();
- }
-};
-
-
-// Entry point for sampler thread.
-static DWORD __stdcall SamplerEntry(void* arg) {
- Sampler::PlatformData* data =
- reinterpret_cast<Sampler::PlatformData*>(arg);
- data->Runner();
- return 0;
-}
-
-
-// Initialize a profile sampler.
-Sampler::Sampler(int interval)
- : interval_(interval),
- profiling_(false),
- active_(false),
- samples_taken_(0) {
- data_ = new PlatformData(this);
-}
-
-
-Sampler::~Sampler() {
- delete data_;
-}
-
-
-// Start profiling.
-void Sampler::Start() {
- // Do not start multiple threads for the same sampler.
- ASSERT(!IsActive());
-
// Get a handle to the calling thread. This is the thread that we are
// going to profile. We need to make a copy of the handle because we are
// going to use it in the sampler thread. Using GetThreadHandle() will
// not work in this case. We're using OpenThread because DuplicateHandle
// for some reason doesn't work in Chrome's sandbox.
- data_->profiled_thread_ = OpenThread(THREAD_GET_CONTEXT |
- THREAD_SUSPEND_RESUME |
- THREAD_QUERY_INFORMATION,
- false,
- GetCurrentThreadId());
- BOOL ok = data_->profiled_thread_ != NULL;
- if (!ok) return;
+ PlatformData() : profiled_thread_(OpenThread(THREAD_GET_CONTEXT |
+ THREAD_SUSPEND_RESUME |
+ THREAD_QUERY_INFORMATION,
+ false,
+ GetCurrentThreadId())) {}
- // Start sampler thread.
- DWORD tid;
+ ~PlatformData() {
+ if (profiled_thread_ != NULL) {
+ CloseHandle(profiled_thread_);
+ profiled_thread_ = NULL;
+ }
+ }
+
+ HANDLE profiled_thread() { return profiled_thread_; }
+
+ private:
+ HANDLE profiled_thread_;
+};
+
+
+class SamplerThread : public Thread {
+ public:
+ explicit SamplerThread(int interval)
+ : Thread(NULL, "SamplerThread"),
+ interval_(interval) {}
+
+ static void AddActiveSampler(Sampler* sampler) {
+ ScopedLock lock(mutex_);
+ SamplerRegistry::AddActiveSampler(sampler);
+ if (instance_ == NULL) {
+ instance_ = new SamplerThread(sampler->interval());
+ instance_->Start();
+ } else {
+ ASSERT(instance_->interval_ == sampler->interval());
+ }
+ }
+
+ static void RemoveActiveSampler(Sampler* sampler) {
+ ScopedLock lock(mutex_);
+ SamplerRegistry::RemoveActiveSampler(sampler);
+ if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) {
+ RuntimeProfiler::WakeUpRuntimeProfilerThreadBeforeShutdown();
+ instance_->Join();
+ delete instance_;
+ instance_ = NULL;
+ }
+ }
+
+ // Implement Thread::Run().
+ virtual void Run() {
+ SamplerRegistry::State state;
+ while ((state = SamplerRegistry::GetState()) !=
+ SamplerRegistry::HAS_NO_SAMPLERS) {
+ bool cpu_profiling_enabled =
+ (state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS);
+ bool runtime_profiler_enabled = RuntimeProfiler::IsEnabled();
+ // When CPU profiling is enabled both JavaScript and C++ code is
+ // profiled. We must not suspend.
+ if (!cpu_profiling_enabled) {
+ if (rate_limiter_.SuspendIfNecessary()) continue;
+ }
+ if (cpu_profiling_enabled) {
+ if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this)) {
+ return;
+ }
+ }
+ if (runtime_profiler_enabled) {
+ if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, NULL)) {
+ return;
+ }
+ }
+ OS::Sleep(interval_);
+ }
+ }
+
+ static void DoCpuProfile(Sampler* sampler, void* raw_sampler_thread) {
+ if (!sampler->isolate()->IsInitialized()) return;
+ if (!sampler->IsProfiling()) return;
+ SamplerThread* sampler_thread =
+ reinterpret_cast<SamplerThread*>(raw_sampler_thread);
+ sampler_thread->SampleContext(sampler);
+ }
+
+ static void DoRuntimeProfile(Sampler* sampler, void* ignored) {
+ if (!sampler->isolate()->IsInitialized()) return;
+ sampler->isolate()->runtime_profiler()->NotifyTick();
+ }
+
+ void SampleContext(Sampler* sampler) {
+ HANDLE profiled_thread = sampler->platform_data()->profiled_thread();
+ if (profiled_thread == NULL) return;
+
+ // Context used for sampling the register state of the profiled thread.
+ CONTEXT context;
+ memset(&context, 0, sizeof(context));
+
+ TickSample sample_obj;
+ TickSample* sample = CpuProfiler::TickSampleEvent(sampler->isolate());
+ if (sample == NULL) sample = &sample_obj;
+
+ static const DWORD kSuspendFailed = static_cast<DWORD>(-1);
+ if (SuspendThread(profiled_thread) == kSuspendFailed) return;
+ sample->state = sampler->isolate()->current_vm_state();
+
+ context.ContextFlags = CONTEXT_FULL;
+ if (GetThreadContext(profiled_thread, &context) != 0) {
+#if V8_HOST_ARCH_X64
+ sample->pc = reinterpret_cast<Address>(context.Rip);
+ sample->sp = reinterpret_cast<Address>(context.Rsp);
+ sample->fp = reinterpret_cast<Address>(context.Rbp);
+#else
+ sample->pc = reinterpret_cast<Address>(context.Eip);
+ sample->sp = reinterpret_cast<Address>(context.Esp);
+ sample->fp = reinterpret_cast<Address>(context.Ebp);
+#endif
+ sampler->SampleStack(sample);
+ sampler->Tick(sample);
+ }
+ ResumeThread(profiled_thread);
+ }
+
+ const int interval_;
+ RuntimeProfilerRateLimiter rate_limiter_;
+
+ // Protects the process wide state below.
+ static Mutex* mutex_;
+ static SamplerThread* instance_;
+
+ DISALLOW_COPY_AND_ASSIGN(SamplerThread);
+};
+
+
+Mutex* SamplerThread::mutex_ = OS::CreateMutex();
+SamplerThread* SamplerThread::instance_ = NULL;
+
+
+Sampler::Sampler(Isolate* isolate, int interval)
+ : isolate_(isolate),
+ interval_(interval),
+ profiling_(false),
+ active_(false),
+ samples_taken_(0) {
+ data_ = new PlatformData;
+}
+
+
+Sampler::~Sampler() {
+ ASSERT(!IsActive());
+ delete data_;
+}
+
+
+void Sampler::Start() {
+ ASSERT(!IsActive());
SetActive(true);
- data_->sampler_thread_ = CreateThread(NULL, 0, SamplerEntry, data_, 0, &tid);
- // Set thread to high priority to increase sampling accuracy.
- SetThreadPriority(data_->sampler_thread_, THREAD_PRIORITY_TIME_CRITICAL);
+ SamplerThread::AddActiveSampler(this);
}
-// Stop profiling.
void Sampler::Stop() {
- // Seting active to false triggers termination of the sampler
- // thread.
+ ASSERT(IsActive());
+ SamplerThread::RemoveActiveSampler(this);
SetActive(false);
-
- // Wait for sampler thread to terminate.
- Top::WakeUpRuntimeProfilerThreadBeforeShutdown();
- WaitForSingleObject(data_->sampler_thread_, INFINITE);
-
- // Release the thread handles
- CloseHandle(data_->sampler_thread_);
- CloseHandle(data_->profiled_thread_);
}
-
#endif // ENABLE_LOGGING_AND_PROFILING
} } // namespace v8::internal
diff --git a/src/platform-freebsd.cc b/src/platform-freebsd.cc
index 2a73b6e..8b83f2b 100644
--- a/src/platform-freebsd.cc
+++ b/src/platform-freebsd.cc
@@ -391,18 +391,8 @@
}
-class ThreadHandle::PlatformData : public Malloced {
+class Thread::PlatformData : public Malloced {
public:
- explicit PlatformData(ThreadHandle::Kind kind) {
- Initialize(kind);
- }
-
- void Initialize(ThreadHandle::Kind kind) {
- switch (kind) {
- case ThreadHandle::SELF: thread_ = pthread_self(); break;
- case ThreadHandle::INVALID: thread_ = kNoThread; break;
- }
- }
pthread_t thread_; // Thread handle for pthread.
};
@@ -433,7 +423,7 @@
Thread::Thread(Isolate* isolate, const Options& options)
- : ThreadHandle(ThreadHandle::INVALID),
+ : data_(new PlatformData),
isolate_(isolate),
stack_size_(options.stack_size) {
set_name(options.name);
@@ -441,7 +431,7 @@
Thread::Thread(Isolate* isolate, const char* name)
- : ThreadHandle(ThreadHandle::INVALID),
+ : data_(new PlatformData),
isolate_(isolate),
stack_size_(0) {
set_name(name);
@@ -449,6 +439,7 @@
Thread::~Thread() {
+ delete data_;
}
@@ -457,7 +448,7 @@
// This is also initialized by the first argument to pthread_create() but we
// don't know which thread will run first (the original thread or the new
// one) so we initialize it here too.
- thread->thread_handle_data()->thread_ = pthread_self();
+ thread_->data_->thread_ = pthread_self();
ASSERT(thread->IsValid());
Thread::SetThreadLocal(Isolate::isolate_key(), thread->isolate());
thread->Run();
@@ -651,6 +642,11 @@
// We require a fully initialized and entered isolate.
return;
}
+ if (v8::Locker::IsActive() &&
+ !isolate->thread_manager()->IsLockedByCurrentThread()) {
+ return;
+ }
+
Sampler* sampler = isolate->logger()->sampler();
if (sampler == NULL || !sampler->IsActive()) return;
diff --git a/src/platform-linux.cc b/src/platform-linux.cc
index 73a6ccb..1ecd8fc 100644
--- a/src/platform-linux.cc
+++ b/src/platform-linux.cc
@@ -92,9 +92,10 @@
uint64_t OS::CpuFeaturesImpliedByPlatform() {
#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
- // Here gcc is telling us that we are on an ARM and gcc is assuming that we
- // have VFP3 instructions. If gcc can assume it then so can we.
- return 1u << VFP3;
+ // Here gcc is telling us that we are on an ARM and gcc is assuming
+ // that we have VFP3 instructions. If gcc can assume it then so can
+ // we. VFPv3 implies ARMv7, see ARM DDI 0406B, page A1-6.
+ return 1u << VFP3 | 1u << ARMv7;
#elif CAN_USE_ARMV7_INSTRUCTIONS
return 1u << ARMv7;
#elif(defined(__mips_hard_float) && __mips_hard_float != 0)
@@ -588,50 +589,15 @@
}
-class ThreadHandle::PlatformData : public Malloced {
+class Thread::PlatformData : public Malloced {
public:
- explicit PlatformData(ThreadHandle::Kind kind) {
- Initialize(kind);
- }
-
- void Initialize(ThreadHandle::Kind kind) {
- switch (kind) {
- case ThreadHandle::SELF: thread_ = pthread_self(); break;
- case ThreadHandle::INVALID: thread_ = kNoThread; break;
- }
- }
+ PlatformData() : thread_(kNoThread) {}
pthread_t thread_; // Thread handle for pthread.
};
-
-ThreadHandle::ThreadHandle(Kind kind) {
- data_ = new PlatformData(kind);
-}
-
-
-void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
- data_->Initialize(kind);
-}
-
-
-ThreadHandle::~ThreadHandle() {
- delete data_;
-}
-
-
-bool ThreadHandle::IsSelf() const {
- return pthread_equal(data_->thread_, pthread_self());
-}
-
-
-bool ThreadHandle::IsValid() const {
- return data_->thread_ != kNoThread;
-}
-
-
Thread::Thread(Isolate* isolate, const Options& options)
- : ThreadHandle(ThreadHandle::INVALID),
+ : data_(new PlatformData()),
isolate_(isolate),
stack_size_(options.stack_size) {
set_name(options.name);
@@ -639,7 +605,7 @@
Thread::Thread(Isolate* isolate, const char* name)
- : ThreadHandle(ThreadHandle::INVALID),
+ : data_(new PlatformData()),
isolate_(isolate),
stack_size_(0) {
set_name(name);
@@ -647,6 +613,7 @@
Thread::~Thread() {
+ delete data_;
}
@@ -658,8 +625,8 @@
prctl(PR_SET_NAME,
reinterpret_cast<unsigned long>(thread->name()), // NOLINT
0, 0, 0);
- thread->thread_handle_data()->thread_ = pthread_self();
- ASSERT(thread->IsValid());
+ thread->data()->thread_ = pthread_self();
+ ASSERT(thread->data()->thread_ != kNoThread);
Thread::SetThreadLocal(Isolate::isolate_key(), thread->isolate());
thread->Run();
return NULL;
@@ -680,13 +647,13 @@
pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
attr_ptr = &attr;
}
- pthread_create(&thread_handle_data()->thread_, attr_ptr, ThreadEntry, this);
- ASSERT(IsValid());
+ pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this);
+ ASSERT(data_->thread_ != kNoThread);
}
void Thread::Join() {
- pthread_join(thread_handle_data()->thread_, NULL);
+ pthread_join(data_->thread_, NULL);
}
@@ -886,6 +853,11 @@
// We require a fully initialized and entered isolate.
return;
}
+ if (v8::Locker::IsActive() &&
+ !isolate->thread_manager()->IsLockedByCurrentThread()) {
+ return;
+ }
+
Sampler* sampler = isolate->logger()->sampler();
if (sampler == NULL || !sampler->IsActive()) return;
diff --git a/src/platform-macos.cc b/src/platform-macos.cc
index 17e3042..3e10b6a 100644
--- a/src/platform-macos.cc
+++ b/src/platform-macos.cc
@@ -48,8 +48,10 @@
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/types.h>
+#include <sys/sysctl.h>
#include <stdarg.h>
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#undef MAP_TYPE
@@ -390,50 +392,14 @@
}
-class ThreadHandle::PlatformData : public Malloced {
+class Thread::PlatformData : public Malloced {
public:
- explicit PlatformData(ThreadHandle::Kind kind) {
- Initialize(kind);
- }
-
- void Initialize(ThreadHandle::Kind kind) {
- switch (kind) {
- case ThreadHandle::SELF: thread_ = pthread_self(); break;
- case ThreadHandle::INVALID: thread_ = kNoThread; break;
- }
- }
+ PlatformData() : thread_(kNoThread) {}
pthread_t thread_; // Thread handle for pthread.
};
-
-
-ThreadHandle::ThreadHandle(Kind kind) {
- data_ = new PlatformData(kind);
-}
-
-
-void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
- data_->Initialize(kind);
-}
-
-
-ThreadHandle::~ThreadHandle() {
- delete data_;
-}
-
-
-bool ThreadHandle::IsSelf() const {
- return pthread_equal(data_->thread_, pthread_self());
-}
-
-
-bool ThreadHandle::IsValid() const {
- return data_->thread_ != kNoThread;
-}
-
-
Thread::Thread(Isolate* isolate, const Options& options)
- : ThreadHandle(ThreadHandle::INVALID),
+ : data_(new PlatformData),
isolate_(isolate),
stack_size_(options.stack_size) {
set_name(options.name);
@@ -441,7 +407,7 @@
Thread::Thread(Isolate* isolate, const char* name)
- : ThreadHandle(ThreadHandle::INVALID),
+ : data_(new PlatformData),
isolate_(isolate),
stack_size_(0) {
set_name(name);
@@ -449,6 +415,7 @@
Thread::~Thread() {
+ delete data_;
}
@@ -474,9 +441,9 @@
// This is also initialized by the first argument to pthread_create() but we
// don't know which thread will run first (the original thread or the new
// one) so we initialize it here too.
- thread->thread_handle_data()->thread_ = pthread_self();
+ thread->data()->thread_ = pthread_self();
SetThreadName(thread->name());
- ASSERT(thread->IsValid());
+ ASSERT(thread->data()->thread_ != kNoThread);
Thread::SetThreadLocal(Isolate::isolate_key(), thread->isolate());
thread->Run();
return NULL;
@@ -497,22 +464,89 @@
pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
attr_ptr = &attr;
}
- pthread_create(&thread_handle_data()->thread_, attr_ptr, ThreadEntry, this);
- ASSERT(IsValid());
+ pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this);
+ ASSERT(data_->thread_ != kNoThread);
}
void Thread::Join() {
- pthread_join(thread_handle_data()->thread_, NULL);
+ pthread_join(data_->thread_, NULL);
}
+#ifdef V8_FAST_TLS_SUPPORTED
+
+static Atomic32 tls_base_offset_initialized = 0;
+intptr_t kMacTlsBaseOffset = 0;
+
+// It's safe to do the initialization more that once, but it has to be
+// done at least once.
+static void InitializeTlsBaseOffset() {
+ const size_t kBufferSize = 128;
+ char buffer[kBufferSize];
+ size_t buffer_size = kBufferSize;
+ int ctl_name[] = { CTL_KERN , KERN_OSRELEASE };
+ if (sysctl(ctl_name, 2, buffer, &buffer_size, NULL, 0) != 0) {
+ V8_Fatal(__FILE__, __LINE__, "V8 failed to get kernel version");
+ }
+ // The buffer now contains a string of the form XX.YY.ZZ, where
+ // XX is the major kernel version component.
+ // Make sure the buffer is 0-terminated.
+ buffer[kBufferSize - 1] = '\0';
+ char* period_pos = strchr(buffer, '.');
+ *period_pos = '\0';
+ int kernel_version_major =
+ static_cast<int>(strtol(buffer, NULL, 10)); // NOLINT
+ // The constants below are taken from pthreads.s from the XNU kernel
+ // sources archive at www.opensource.apple.com.
+ if (kernel_version_major < 11) {
+ // 8.x.x (Tiger), 9.x.x (Leopard), 10.x.x (Snow Leopard) have the
+ // same offsets.
+#if defined(V8_HOST_ARCH_IA32)
+ kMacTlsBaseOffset = 0x48;
+#else
+ kMacTlsBaseOffset = 0x60;
+#endif
+ } else {
+ // 11.x.x (Lion) changed the offset.
+ kMacTlsBaseOffset = 0;
+ }
+
+ Release_Store(&tls_base_offset_initialized, 1);
+}
+
+static void CheckFastTls(Thread::LocalStorageKey key) {
+ void* expected = reinterpret_cast<void*>(0x1234CAFE);
+ Thread::SetThreadLocal(key, expected);
+ void* actual = Thread::GetExistingThreadLocal(key);
+ if (expected != actual) {
+ V8_Fatal(__FILE__, __LINE__,
+ "V8 failed to initialize fast TLS on current kernel");
+ }
+ Thread::SetThreadLocal(key, NULL);
+}
+
+#endif // V8_FAST_TLS_SUPPORTED
+
+
Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
+#ifdef V8_FAST_TLS_SUPPORTED
+ bool check_fast_tls = false;
+ if (tls_base_offset_initialized == 0) {
+ check_fast_tls = true;
+ InitializeTlsBaseOffset();
+ }
+#endif
pthread_key_t key;
int result = pthread_key_create(&key, NULL);
USE(result);
ASSERT(result == 0);
- return static_cast<LocalStorageKey>(key);
+ LocalStorageKey typed_key = static_cast<LocalStorageKey>(key);
+#ifdef V8_FAST_TLS_SUPPORTED
+ // If we just initialized fast TLS support, make sure it works.
+ if (check_fast_tls) CheckFastTls(typed_key);
+#endif
+ return typed_key;
}
diff --git a/src/platform-nullos.cc b/src/platform-nullos.cc
index 5409936..aacad14 100644
--- a/src/platform-nullos.cc
+++ b/src/platform-nullos.cc
@@ -299,9 +299,9 @@
}
-class ThreadHandle::PlatformData : public Malloced {
+class Thread::PlatformData : public Malloced {
public:
- explicit PlatformData(ThreadHandle::Kind kind) {
+ PlatformData() {
UNIMPLEMENTED();
}
@@ -309,39 +309,8 @@
};
-ThreadHandle::ThreadHandle(Kind kind) {
- UNIMPLEMENTED();
- // Shared setup follows.
- data_ = new PlatformData(kind);
-}
-
-
-void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
- UNIMPLEMENTED();
-}
-
-
-ThreadHandle::~ThreadHandle() {
- UNIMPLEMENTED();
- // Shared tear down follows.
- delete data_;
-}
-
-
-bool ThreadHandle::IsSelf() const {
- UNIMPLEMENTED();
- return false;
-}
-
-
-bool ThreadHandle::IsValid() const {
- UNIMPLEMENTED();
- return false;
-}
-
-
Thread::Thread(Isolate* isolate, const Options& options)
- : ThreadHandle(ThreadHandle::INVALID),
+ : data_(new PlatformData()),
isolate_(isolate),
stack_size_(options.stack_size) {
set_name(options.name);
@@ -350,7 +319,7 @@
Thread::Thread(Isolate* isolate, const char* name)
- : ThreadHandle(ThreadHandle::INVALID),
+ : data_(new PlatformData()),
isolate_(isolate),
stack_size_(0) {
set_name(name);
@@ -359,6 +328,7 @@
Thread::~Thread() {
+ delete data_;
UNIMPLEMENTED();
}
diff --git a/src/platform-openbsd.cc b/src/platform-openbsd.cc
index fe1a62a..e90b3e8 100644
--- a/src/platform-openbsd.cc
+++ b/src/platform-openbsd.cc
@@ -359,49 +359,16 @@
}
-class ThreadHandle::PlatformData : public Malloced {
+class Thread::PlatformData : public Malloced {
public:
- explicit PlatformData(ThreadHandle::Kind kind) {
- Initialize(kind);
- }
+ PlatformData() : thread_(kNoThread) {}
- void Initialize(ThreadHandle::Kind kind) {
- switch (kind) {
- case ThreadHandle::SELF: thread_ = pthread_self(); break;
- case ThreadHandle::INVALID: thread_ = kNoThread; break;
- }
- }
pthread_t thread_; // Thread handle for pthread.
};
-ThreadHandle::ThreadHandle(Kind kind) {
- data_ = new PlatformData(kind);
-}
-
-
-void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
- data_->Initialize(kind);
-}
-
-
-ThreadHandle::~ThreadHandle() {
- delete data_;
-}
-
-
-bool ThreadHandle::IsSelf() const {
- return pthread_equal(data_->thread_, pthread_self());
-}
-
-
-bool ThreadHandle::IsValid() const {
- return data_->thread_ != kNoThread;
-}
-
-
Thread::Thread(Isolate* isolate, const Options& options)
- : ThreadHandle(ThreadHandle::INVALID),
+ : data_(new PlatformData()),
isolate_(isolate),
stack_size_(options.stack_size) {
set_name(options.name);
@@ -409,7 +376,7 @@
Thread::Thread(Isolate* isolate, const char* name)
- : ThreadHandle(ThreadHandle::INVALID),
+ : data_(new PlatfromData()),
isolate_(isolate),
stack_size_(0) {
set_name(name);
@@ -417,6 +384,7 @@
Thread::~Thread() {
+ delete data_;
}
@@ -425,8 +393,8 @@
// This is also initialized by the first argument to pthread_create() but we
// don't know which thread will run first (the original thread or the new
// one) so we initialize it here too.
- thread->thread_handle_data()->thread_ = pthread_self();
- ASSERT(thread->IsValid());
+ thread->data()->thread_ = pthread_self();
+ ASSERT(thread->data()->thread_ != kNoThread);
Thread::SetThreadLocal(Isolate::isolate_key(), thread->isolate());
thread->Run();
return NULL;
@@ -447,13 +415,13 @@
pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
attr_ptr = &attr;
}
- pthread_create(&thread_handle_data()->thread_, attr_ptr, ThreadEntry, this);
+ pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this);
ASSERT(IsValid());
}
void Thread::Join() {
- pthread_join(thread_handle_data()->thread_, NULL);
+ pthread_join(data_->thread_, NULL);
}
diff --git a/src/platform-posix.cc b/src/platform-posix.cc
index 1dd486e..c4b0fb8 100644
--- a/src/platform-posix.cc
+++ b/src/platform-posix.cc
@@ -1,4 +1,4 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -205,6 +205,31 @@
}
+#if defined(V8_TARGET_ARCH_IA32)
+static OS::MemCopyFunction memcopy_function = NULL;
+static Mutex* memcopy_function_mutex = OS::CreateMutex();
+// Defined in codegen-ia32.cc.
+OS::MemCopyFunction CreateMemCopyFunction();
+
+// Copy memory area to disjoint memory area.
+void OS::MemCopy(void* dest, const void* src, size_t size) {
+ if (memcopy_function == NULL) {
+ ScopedLock lock(memcopy_function_mutex);
+ if (memcopy_function == NULL) {
+ OS::MemCopyFunction temp = CreateMemCopyFunction();
+ MemoryBarrier();
+ memcopy_function = temp;
+ }
+ }
+ // Note: here we rely on dependent reads being ordered. This is true
+ // on all architectures we currently support.
+ (*memcopy_function)(dest, src, size);
+#ifdef DEBUG
+ CHECK_EQ(0, memcmp(dest, src, size));
+#endif
+}
+#endif // V8_TARGET_ARCH_IA32
+
// ----------------------------------------------------------------------------
// POSIX string support.
//
diff --git a/src/platform-solaris.cc b/src/platform-solaris.cc
index da278f3..1a19bac 100644
--- a/src/platform-solaris.cc
+++ b/src/platform-solaris.cc
@@ -373,50 +373,15 @@
}
-class ThreadHandle::PlatformData : public Malloced {
+class Thread::PlatformData : public Malloced {
public:
- explicit PlatformData(ThreadHandle::Kind kind) {
- Initialize(kind);
- }
-
- void Initialize(ThreadHandle::Kind kind) {
- switch (kind) {
- case ThreadHandle::SELF: thread_ = pthread_self(); break;
- case ThreadHandle::INVALID: thread_ = kNoThread; break;
- }
- }
+ PlatformData() : thread_(kNoThread) { }
pthread_t thread_; // Thread handle for pthread.
};
-
-ThreadHandle::ThreadHandle(Kind kind) {
- data_ = new PlatformData(kind);
-}
-
-
-void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
- data_->Initialize(kind);
-}
-
-
-ThreadHandle::~ThreadHandle() {
- delete data_;
-}
-
-
-bool ThreadHandle::IsSelf() const {
- return pthread_equal(data_->thread_, pthread_self());
-}
-
-
-bool ThreadHandle::IsValid() const {
- return data_->thread_ != kNoThread;
-}
-
-
Thread::Thread(Isolate* isolate, const Options& options)
- : ThreadHandle(ThreadHandle::INVALID),
+ : data_(new PlatformData()),
isolate_(isolate),
stack_size_(options.stack_size) {
set_name(options.name);
@@ -424,7 +389,7 @@
Thread::Thread(Isolate* isolate, const char* name)
- : ThreadHandle(ThreadHandle::INVALID),
+ : data_(new PlatformData()),
isolate_(isolate),
stack_size_(0) {
set_name(name);
@@ -432,6 +397,7 @@
Thread::~Thread() {
+ delete data_;
}
@@ -440,8 +406,8 @@
// This is also initialized by the first argument to pthread_create() but we
// don't know which thread will run first (the original thread or the new
// one) so we initialize it here too.
- thread->thread_handle_data()->thread_ = pthread_self();
- ASSERT(thread->IsValid());
+ thread->data()->thread_ = pthread_self();
+ ASSERT(thread->data()->thread_ != kNoThread);
Thread::SetThreadLocal(Isolate::isolate_key(), thread->isolate());
thread->Run();
return NULL;
@@ -462,13 +428,13 @@
pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
attr_ptr = &attr;
}
- pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
- ASSERT(IsValid());
+ pthread_create(&data_->thread_, NULL, ThreadEntry, this);
+ ASSERT(data_->thread_ != kNoThread);
}
void Thread::Join() {
- pthread_join(thread_handle_data()->thread_, NULL);
+ pthread_join(data_->thread_, NULL);
}
diff --git a/src/platform-tls-mac.h b/src/platform-tls-mac.h
index 86a3347..728524e 100644
--- a/src/platform-tls-mac.h
+++ b/src/platform-tls-mac.h
@@ -37,20 +37,20 @@
#define V8_FAST_TLS_SUPPORTED 1
+extern intptr_t kMacTlsBaseOffset;
+
INLINE(intptr_t InternalGetExistingThreadLocal(intptr_t index));
inline intptr_t InternalGetExistingThreadLocal(intptr_t index) {
- // The constants below are taken from pthreads.s from the XNU kernel
- // sources archive at www.opensource.apple.com.
intptr_t result;
#if defined(V8_HOST_ARCH_IA32)
- asm("movl %%gs:0x48(,%1,4), %0;"
+ asm("movl %%gs:(%1,%2,4), %0;"
:"=r"(result) // Output must be a writable register.
- :"0"(index)); // Input is the same as output.
+ :"r"(kMacTlsBaseOffset), "r"(index));
#else
- asm("movq %%gs:0x60(,%1,8), %0;"
+ asm("movq %%gs:(%1,%2,8), %0;"
:"=r"(result)
- :"0"(index));
+ :"r"(kMacTlsBaseOffset), "r"(index));
#endif
return result;
}
diff --git a/src/platform-win32.cc b/src/platform-win32.cc
index 50a9e5b..8673f04 100644
--- a/src/platform-win32.cc
+++ b/src/platform-win32.cc
@@ -176,16 +176,50 @@
static Mutex* limit_mutex = NULL;
+#if defined(V8_TARGET_ARCH_IA32)
+static OS::MemCopyFunction memcopy_function = NULL;
+static Mutex* memcopy_function_mutex = OS::CreateMutex();
+// Defined in codegen-ia32.cc.
+OS::MemCopyFunction CreateMemCopyFunction();
+
+// Copy memory area to disjoint memory area.
+void OS::MemCopy(void* dest, const void* src, size_t size) {
+ if (memcopy_function == NULL) {
+ ScopedLock lock(memcopy_function_mutex);
+ if (memcopy_function == NULL) {
+ OS::MemCopyFunction temp = CreateMemCopyFunction();
+ MemoryBarrier();
+ memcopy_function = temp;
+ }
+ }
+ // Note: here we rely on dependent reads being ordered. This is true
+ // on all architectures we currently support.
+ (*memcopy_function)(dest, src, size);
+#ifdef DEBUG
+ CHECK_EQ(0, memcmp(dest, src, size));
+#endif
+}
+#endif // V8_TARGET_ARCH_IA32
#ifdef _WIN64
typedef double (*ModuloFunction)(double, double);
-
+static ModuloFunction modulo_function = NULL;
+static Mutex* modulo_function_mutex = OS::CreateMutex();
// Defined in codegen-x64.cc.
ModuloFunction CreateModuloFunction();
double modulo(double x, double y) {
- static ModuloFunction function = CreateModuloFunction();
- return function(x, y);
+ if (modulo_function == NULL) {
+ ScopedLock lock(modulo_function_mutex);
+ if (modulo_function == NULL) {
+ ModuloFunction temp = CreateModuloFunction();
+ MemoryBarrier();
+ modulo_function = temp;
+ }
+ }
+ // Note: here we rely on dependent reads being ordered. This is true
+ // on all architectures we currently support.
+ return (*modulo_function)(x, y);
}
#else // Win32
@@ -1434,24 +1468,6 @@
// Definition of invalid thread handle and id.
static const HANDLE kNoThread = INVALID_HANDLE_VALUE;
-static const DWORD kNoThreadId = 0;
-
-
-class ThreadHandle::PlatformData : public Malloced {
- public:
- explicit PlatformData(ThreadHandle::Kind kind) {
- Initialize(kind);
- }
-
- void Initialize(ThreadHandle::Kind kind) {
- switch (kind) {
- case ThreadHandle::SELF: tid_ = GetCurrentThreadId(); break;
- case ThreadHandle::INVALID: tid_ = kNoThreadId; break;
- }
- }
- DWORD tid_; // Win32 thread identifier.
-};
-
// Entry point for threads. The supplied argument is a pointer to the thread
// object. The entry function dispatches to the run method in the thread
@@ -1462,41 +1478,12 @@
// This is also initialized by the last parameter to _beginthreadex() but we
// don't know which thread will run first (the original thread or the new
// one) so we initialize it here too.
- thread->thread_handle_data()->tid_ = GetCurrentThreadId();
Thread::SetThreadLocal(Isolate::isolate_key(), thread->isolate());
thread->Run();
return 0;
}
-// Initialize thread handle to invalid handle.
-ThreadHandle::ThreadHandle(ThreadHandle::Kind kind) {
- data_ = new PlatformData(kind);
-}
-
-
-ThreadHandle::~ThreadHandle() {
- delete data_;
-}
-
-
-// The thread is running if it has the same id as the current thread.
-bool ThreadHandle::IsSelf() const {
- return GetCurrentThreadId() == data_->tid_;
-}
-
-
-// Test for invalid thread handle.
-bool ThreadHandle::IsValid() const {
- return data_->tid_ != kNoThreadId;
-}
-
-
-void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
- data_->Initialize(kind);
-}
-
-
class Thread::PlatformData : public Malloced {
public:
explicit PlatformData(HANDLE thread) : thread_(thread) {}
@@ -1508,8 +1495,7 @@
// handle until it is started.
Thread::Thread(Isolate* isolate, const Options& options)
- : ThreadHandle(ThreadHandle::INVALID),
- isolate_(isolate),
+ : isolate_(isolate),
stack_size_(options.stack_size) {
data_ = new PlatformData(kNoThread);
set_name(options.name);
@@ -1517,8 +1503,7 @@
Thread::Thread(Isolate* isolate, const char* name)
- : ThreadHandle(ThreadHandle::INVALID),
- isolate_(isolate),
+ : isolate_(isolate),
stack_size_(0) {
data_ = new PlatformData(kNoThread);
set_name(name);
@@ -1548,9 +1533,7 @@
ThreadEntry,
this,
0,
- reinterpret_cast<unsigned int*>(
- &thread_handle_data()->tid_)));
- ASSERT(IsValid());
+ NULL));
}
diff --git a/src/platform.h b/src/platform.h
index b2e0c48..fc417ef 100644
--- a/src/platform.h
+++ b/src/platform.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -303,6 +303,21 @@
static void ReleaseStore(volatile AtomicWord* ptr, AtomicWord value);
+#if defined(V8_TARGET_ARCH_IA32)
+ // Copy memory area to disjoint memory area.
+ static void MemCopy(void* dest, const void* src, size_t size);
+ // Limit below which the extra overhead of the MemCopy function is likely
+ // to outweigh the benefits of faster copying.
+ static const int kMinComplexMemCopy = 64;
+ typedef void (*MemCopyFunction)(void* dest, const void* src, size_t size);
+
+#else // V8_TARGET_ARCH_IA32
+ static void MemCopy(void* dest, const void* src, size_t size) {
+ memcpy(dest, src, size);
+ }
+ static const int kMinComplexMemCopy = 256;
+#endif // V8_TARGET_ARCH_IA32
+
private:
static const int msPerSecond = 1000;
@@ -339,40 +354,6 @@
size_t size_; // Size of the virtual memory.
};
-
-// ----------------------------------------------------------------------------
-// ThreadHandle
-//
-// A ThreadHandle represents a thread identifier for a thread. The ThreadHandle
-// does not own the underlying os handle. Thread handles can be used for
-// refering to threads and testing equality.
-
-class ThreadHandle {
- public:
- enum Kind { SELF, INVALID };
- explicit ThreadHandle(Kind kind);
-
- // Destructor.
- ~ThreadHandle();
-
- // Test for thread running.
- bool IsSelf() const;
-
- // Test for valid thread handle.
- bool IsValid() const;
-
- // Get platform-specific data.
- class PlatformData;
- PlatformData* thread_handle_data() { return data_; }
-
- // Initialize the handle to kind
- void Initialize(Kind kind);
-
- private:
- PlatformData* data_; // Captures platform dependent data.
-};
-
-
// ----------------------------------------------------------------------------
// Thread
//
@@ -381,7 +362,7 @@
// thread. The Thread object should not be deallocated before the thread has
// terminated.
-class Thread: public ThreadHandle {
+class Thread {
public:
// Opaque data type for thread-local storage keys.
// LOCAL_STORAGE_KEY_MIN_VALUE and LOCAL_STORAGE_KEY_MAX_VALUE are specified
@@ -453,11 +434,15 @@
// The thread name length is limited to 16 based on Linux's implementation of
// prctl().
static const int kMaxThreadNameLength = 16;
+
+ class PlatformData;
+ PlatformData* data() { return data_; }
+
private:
void set_name(const char *name);
- class PlatformData;
PlatformData* data_;
+
Isolate* isolate_;
char name_[kMaxThreadNameLength];
int stack_size_;
@@ -493,10 +478,10 @@
// ----------------------------------------------------------------------------
-// ScopedLock/ScopedUnlock
+// ScopedLock
//
-// Stack-allocated ScopedLocks/ScopedUnlocks provide block-scoped
-// locking and unlocking of a mutex.
+// Stack-allocated ScopedLocks provide block-scoped locking and
+// unlocking of a mutex.
class ScopedLock {
public:
explicit ScopedLock(Mutex* mutex): mutex_(mutex) {
@@ -596,7 +581,8 @@
sp(NULL),
fp(NULL),
tos(NULL),
- frames_count(0) {}
+ frames_count(0),
+ has_external_callback(false) {}
StateTag state; // The state of the VM.
Address pc; // Instruction pointer.
Address sp; // Stack pointer.
diff --git a/src/preparser-api.cc b/src/preparser-api.cc
index 61e9e7e..9646eb6 100644
--- a/src/preparser-api.cc
+++ b/src/preparser-api.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -159,8 +159,8 @@
class StandAloneJavaScriptScanner : public JavaScriptScanner {
public:
- explicit StandAloneJavaScriptScanner(ScannerConstants* scanner_constants)
- : JavaScriptScanner(scanner_constants) { }
+ explicit StandAloneJavaScriptScanner(UnicodeCache* unicode_cache)
+ : JavaScriptScanner(unicode_cache) { }
void Initialize(UC16CharacterStream* source) {
source_ = source;
@@ -192,8 +192,8 @@
PreParserData Preparse(UnicodeInputStream* input, size_t max_stack) {
internal::InputStreamUTF16Buffer buffer(input);
uintptr_t stack_limit = reinterpret_cast<uintptr_t>(&buffer) - max_stack;
- internal::ScannerConstants scanner_constants;
- internal::StandAloneJavaScriptScanner scanner(&scanner_constants);
+ internal::UnicodeCache unicode_cache;
+ internal::StandAloneJavaScriptScanner scanner(&unicode_cache);
scanner.Initialize(&buffer);
internal::CompleteParserRecorder recorder;
preparser::PreParser::PreParseResult result =
diff --git a/src/prettyprinter.cc b/src/prettyprinter.cc
index 043ad1c..c777ab4 100644
--- a/src/prettyprinter.cc
+++ b/src/prettyprinter.cc
@@ -376,11 +376,6 @@
}
-void PrettyPrinter::VisitIncrementOperation(IncrementOperation* node) {
- UNREACHABLE();
-}
-
-
void PrettyPrinter::VisitCountOperation(CountOperation* node) {
Print("(");
if (node->is_prefix()) Print("%s", Token::String(node->op()));
@@ -609,16 +604,6 @@
IndentedScope(AstPrinter* printer, const char* txt, AstNode* node = NULL)
: ast_printer_(printer) {
ast_printer_->PrintIndented(txt);
- if (node != NULL && node->AsExpression() != NULL) {
- Expression* expr = node->AsExpression();
- bool printed_first = false;
- if ((expr->type() != NULL) && (expr->type()->IsKnown())) {
- ast_printer_->Print(" (type = ");
- ast_printer_->Print(StaticType::Type2String(expr->type()));
- printed_first = true;
- }
- if (printed_first) ast_printer_->Print(")");
- }
ast_printer_->Print("\n");
ast_printer_->inc_indent();
}
@@ -664,18 +649,13 @@
void AstPrinter::PrintLiteralWithModeIndented(const char* info,
Variable* var,
- Handle<Object> value,
- StaticType* type) {
+ Handle<Object> value) {
if (var == NULL) {
PrintLiteralIndented(info, value, true);
} else {
EmbeddedVector<char, 256> buf;
int pos = OS::SNPrintF(buf, "%s (mode = %s", info,
Variable::Mode2String(var->mode()));
- if (type->IsKnown()) {
- pos += OS::SNPrintF(buf + pos, ", type = %s",
- StaticType::Type2String(type));
- }
OS::SNPrintF(buf + pos, ")");
PrintLiteralIndented(buf.start(), value, true);
}
@@ -732,8 +712,7 @@
IndentedScope indent(this, "PARAMS");
for (int i = 0; i < scope->num_parameters(); i++) {
PrintLiteralWithModeIndented("VAR", scope->parameter(i),
- scope->parameter(i)->name(),
- scope->parameter(i)->type());
+ scope->parameter(i)->name());
}
}
}
@@ -777,8 +756,7 @@
// var or const declarations
PrintLiteralWithModeIndented(Variable::Mode2String(node->mode()),
node->proxy()->AsVariable(),
- node->proxy()->name(),
- node->proxy()->AsVariable()->type());
+ node->proxy()->name());
} else {
// function declarations
PrintIndented("FUNCTION ");
@@ -996,8 +974,7 @@
void AstPrinter::VisitVariableProxy(VariableProxy* node) {
- PrintLiteralWithModeIndented("VAR PROXY", node->AsVariable(), node->name(),
- node->type());
+ PrintLiteralWithModeIndented("VAR PROXY", node->AsVariable(), node->name());
Variable* var = node->var();
if (var != NULL && var->rewrite() != NULL) {
IndentedScope indent(this);
@@ -1056,22 +1033,10 @@
}
-void AstPrinter::VisitIncrementOperation(IncrementOperation* node) {
- UNREACHABLE();
-}
-
-
void AstPrinter::VisitCountOperation(CountOperation* node) {
EmbeddedVector<char, 128> buf;
- if (node->type()->IsKnown()) {
- OS::SNPrintF(buf, "%s %s (type = %s)",
- (node->is_prefix() ? "PRE" : "POST"),
- Token::Name(node->op()),
- StaticType::Type2String(node->type()));
- } else {
- OS::SNPrintF(buf, "%s %s", (node->is_prefix() ? "PRE" : "POST"),
- Token::Name(node->op()));
- }
+ OS::SNPrintF(buf, "%s %s", (node->is_prefix() ? "PRE" : "POST"),
+ Token::Name(node->op()));
PrintIndentedVisit(buf.start(), node->expression());
}
@@ -1461,11 +1426,6 @@
}
-void JsonAstBuilder::VisitIncrementOperation(IncrementOperation* expr) {
- UNREACHABLE();
-}
-
-
void JsonAstBuilder::VisitCountOperation(CountOperation* expr) {
TagScope tag(this, "CountOperation");
{
diff --git a/src/prettyprinter.h b/src/prettyprinter.h
index 284a93f..451b17e 100644
--- a/src/prettyprinter.h
+++ b/src/prettyprinter.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -104,8 +104,7 @@
void PrintLiteralIndented(const char* info, Handle<Object> value, bool quote);
void PrintLiteralWithModeIndented(const char* info,
Variable* var,
- Handle<Object> value,
- StaticType* type);
+ Handle<Object> value);
void PrintLabelsIndented(const char* info, ZoneStringList* labels);
void inc_indent() { indent_++; }
diff --git a/src/profile-generator.cc b/src/profile-generator.cc
index c9db94f..4cf62e2 100644
--- a/src/profile-generator.cc
+++ b/src/profile-generator.cc
@@ -1690,7 +1690,7 @@
: "",
children_count,
retainers_count);
- } else if (object->IsFixedArray()) {
+ } else if (object->IsFixedArray() || object->IsByteArray()) {
return AddEntry(object,
HeapEntry::kArray,
"",
@@ -1705,7 +1705,7 @@
}
return AddEntry(object,
HeapEntry::kHidden,
- "system",
+ GetSystemEntryName(object),
children_count,
retainers_count);
}
@@ -1731,6 +1731,21 @@
}
+const char* V8HeapExplorer::GetSystemEntryName(HeapObject* object) {
+ switch (object->map()->instance_type()) {
+ case MAP_TYPE: return "system / Map";
+ case JS_GLOBAL_PROPERTY_CELL_TYPE: return "system / JSGlobalPropertyCell";
+ case PROXY_TYPE: return "system / Proxy";
+ case ODDBALL_TYPE: return "system / Oddball";
+#define MAKE_STRUCT_CASE(NAME, Name, name) \
+ case NAME##_TYPE: return "system / "#Name;
+ STRUCT_LIST(MAKE_STRUCT_CASE)
+#undef MAKE_STRUCT_CASE
+ default: return "system";
+ }
+}
+
+
int V8HeapExplorer::EstimateObjectsCount() {
HeapIterator iterator(HeapIterator::kFilterUnreachable);
int objects_count = 0;
@@ -1745,12 +1760,10 @@
public:
IndexedReferencesExtractor(V8HeapExplorer* generator,
HeapObject* parent_obj,
- HeapEntry* parent_entry,
- bool process_field_marks = false)
+ HeapEntry* parent_entry)
: generator_(generator),
parent_obj_(parent_obj),
parent_(parent_entry),
- process_field_marks_(process_field_marks),
next_index_(1) {
}
void VisitPointers(Object** start, Object** end) {
@@ -1768,7 +1781,7 @@
}
private:
bool CheckVisitedAndUnmark(Object** field) {
- if (process_field_marks_ && (*field)->IsFailure()) {
+ if ((*field)->IsFailure()) {
intptr_t untagged = reinterpret_cast<intptr_t>(*field) & ~kFailureTagMask;
*field = reinterpret_cast<Object*>(untagged | kHeapObjectTag);
ASSERT((*field)->IsHeapObject());
@@ -1779,7 +1792,6 @@
V8HeapExplorer* generator_;
HeapObject* parent_obj_;
HeapEntry* parent_;
- bool process_field_marks_;
int next_index_;
};
@@ -1794,6 +1806,7 @@
// uses for the global object.
JSGlobalProxy* proxy = JSGlobalProxy::cast(obj);
SetRootShortcutReference(proxy->map()->prototype());
+ SetInternalReference(obj, entry, "map", obj->map(), HeapObject::kMapOffset);
IndexedReferencesExtractor refs_extractor(this, obj, entry);
obj->Iterate(&refs_extractor);
} else if (obj->IsJSObject()) {
@@ -1806,10 +1819,6 @@
obj, entry, HEAP->Proto_symbol(), js_obj->GetPrototype());
if (obj->IsJSFunction()) {
JSFunction* js_fun = JSFunction::cast(js_obj);
- SetInternalReference(
- js_fun, entry,
- "code", js_fun->shared(),
- JSFunction::kSharedFunctionInfoOffset);
Object* proto_or_map = js_fun->prototype_or_initial_map();
if (!proto_or_map->IsTheHole()) {
if (!proto_or_map->IsMap()) {
@@ -1823,8 +1832,24 @@
HEAP->prototype_symbol(), js_fun->prototype());
}
}
+ SetInternalReference(js_fun, entry,
+ "shared", js_fun->shared(),
+ JSFunction::kSharedFunctionInfoOffset);
+ SetInternalReference(js_fun, entry,
+ "context", js_fun->unchecked_context(),
+ JSFunction::kContextOffset);
+ SetInternalReference(js_fun, entry,
+ "literals", js_fun->literals(),
+ JSFunction::kLiteralsOffset);
}
- IndexedReferencesExtractor refs_extractor(this, obj, entry, true);
+ SetInternalReference(obj, entry,
+ "properties", js_obj->properties(),
+ JSObject::kPropertiesOffset);
+ SetInternalReference(obj, entry,
+ "elements", js_obj->elements(),
+ JSObject::kElementsOffset);
+ SetInternalReference(obj, entry, "map", obj->map(), HeapObject::kMapOffset);
+ IndexedReferencesExtractor refs_extractor(this, obj, entry);
obj->Iterate(&refs_extractor);
} else if (obj->IsString()) {
if (obj->IsConsString()) {
@@ -1832,7 +1857,41 @@
SetInternalReference(obj, entry, 1, cs->first());
SetInternalReference(obj, entry, 2, cs->second());
}
+ } else if (obj->IsMap()) {
+ Map* map = Map::cast(obj);
+ SetInternalReference(obj, entry,
+ "prototype", map->prototype(), Map::kPrototypeOffset);
+ SetInternalReference(obj, entry,
+ "constructor", map->constructor(),
+ Map::kConstructorOffset);
+ SetInternalReference(obj, entry,
+ "descriptors", map->instance_descriptors(),
+ Map::kInstanceDescriptorsOffset);
+ SetInternalReference(obj, entry,
+ "code_cache", map->code_cache(),
+ Map::kCodeCacheOffset);
+ SetInternalReference(obj, entry, "map", obj->map(), HeapObject::kMapOffset);
+ IndexedReferencesExtractor refs_extractor(this, obj, entry);
+ obj->Iterate(&refs_extractor);
+ } else if (obj->IsSharedFunctionInfo()) {
+ SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
+ SetInternalReference(obj, entry,
+ "name", shared->name(),
+ SharedFunctionInfo::kNameOffset);
+ SetInternalReference(obj, entry,
+ "code", shared->unchecked_code(),
+ SharedFunctionInfo::kCodeOffset);
+ SetInternalReference(obj, entry,
+ "instance_class_name", shared->instance_class_name(),
+ SharedFunctionInfo::kInstanceClassNameOffset);
+ SetInternalReference(obj, entry,
+ "script", shared->script(),
+ SharedFunctionInfo::kScriptOffset);
+ SetInternalReference(obj, entry, "map", obj->map(), HeapObject::kMapOffset);
+ IndexedReferencesExtractor refs_extractor(this, obj, entry);
+ obj->Iterate(&refs_extractor);
} else {
+ SetInternalReference(obj, entry, "map", obj->map(), HeapObject::kMapOffset);
IndexedReferencesExtractor refs_extractor(this, obj, entry);
obj->Iterate(&refs_extractor);
}
@@ -2236,7 +2295,7 @@
ObjectGroup* group = groups->at(i);
if (group->info_ == NULL) continue;
List<HeapObject*>* list = GetListMaybeDisposeInfo(group->info_);
- for (int j = 0; j < group->objects_.length(); ++j) {
+ for (size_t j = 0; j < group->length_; ++j) {
HeapObject* obj = HeapObject::cast(*group->objects_[j]);
list->Add(obj);
in_groups_.Insert(obj);
@@ -2307,7 +2366,7 @@
ASSERT(info_entry != NULL);
filler_->SetNamedReference(HeapGraphEdge::kInternal,
wrapper, wrapper_entry,
- "Native",
+ "native",
info, info_entry);
filler_->SetIndexedAutoIndexReference(HeapGraphEdge::kElement,
info, info_entry,
diff --git a/src/profile-generator.h b/src/profile-generator.h
index 377c083..bbc9efc 100644
--- a/src/profile-generator.h
+++ b/src/profile-generator.h
@@ -930,6 +930,7 @@
const char* name,
int children_count,
int retainers_count);
+ const char* GetSystemEntryName(HeapObject* object);
void ExtractReferences(HeapObject* obj);
void ExtractClosureReferences(JSObject* js_obj, HeapEntry* entry);
void ExtractPropertyReferences(JSObject* js_obj, HeapEntry* entry);
diff --git a/src/property.h b/src/property.h
index fa3916e..ee95ca2 100644
--- a/src/property.h
+++ b/src/property.h
@@ -185,6 +185,13 @@
number_ = number;
}
+ void DescriptorResult(JSObject* holder, Smi* details, int number) {
+ lookup_type_ = DESCRIPTOR_TYPE;
+ holder_ = holder;
+ details_ = PropertyDetails(details);
+ number_ = number;
+ }
+
void ConstantResult(JSObject* holder) {
lookup_type_ = CONSTANT_TYPE;
holder_ = holder;
diff --git a/src/virtual-frame-inl.h b/src/proxy.js
similarity index 82%
rename from src/virtual-frame-inl.h
rename to src/proxy.js
index c9f4aac..2516983 100644
--- a/src/virtual-frame-inl.h
+++ b/src/proxy.js
@@ -1,4 +1,4 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -25,15 +25,4 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#ifndef V8_VIRTUAL_FRAME_INL_H_
-#define V8_VIRTUAL_FRAME_INL_H_
-
-#include "virtual-frame.h"
-
-#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64
-#include "virtual-frame-heavy-inl.h"
-#else
-#include "virtual-frame-light-inl.h"
-#endif
-
-#endif // V8_VIRTUAL_FRAME_INL_H_
+global.Proxy = new $Object();
diff --git a/src/register-allocator-inl.h b/src/register-allocator-inl.h
deleted file mode 100644
index 5a68ab0..0000000
--- a/src/register-allocator-inl.h
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_REGISTER_ALLOCATOR_INL_H_
-#define V8_REGISTER_ALLOCATOR_INL_H_
-
-#include "codegen.h"
-#include "register-allocator.h"
-
-#if V8_TARGET_ARCH_IA32
-#include "ia32/register-allocator-ia32-inl.h"
-#elif V8_TARGET_ARCH_X64
-#include "x64/register-allocator-x64-inl.h"
-#elif V8_TARGET_ARCH_ARM
-#include "arm/register-allocator-arm-inl.h"
-#elif V8_TARGET_ARCH_MIPS
-#include "mips/register-allocator-mips-inl.h"
-#else
-#error Unsupported target architecture.
-#endif
-
-
-namespace v8 {
-namespace internal {
-
-Result::Result(const Result& other) {
- other.CopyTo(this);
-}
-
-
-Result& Result::operator=(const Result& other) {
- if (this != &other) {
- Unuse();
- other.CopyTo(this);
- }
- return *this;
-}
-
-
-Result::~Result() {
- if (is_register()) {
- CodeGeneratorScope::Current(Isolate::Current())->allocator()->Unuse(reg());
- }
-}
-
-
-void Result::Unuse() {
- if (is_register()) {
- CodeGeneratorScope::Current(Isolate::Current())->allocator()->Unuse(reg());
- }
- invalidate();
-}
-
-
-void Result::CopyTo(Result* destination) const {
- destination->value_ = value_;
- if (is_register()) {
- CodeGeneratorScope::Current(Isolate::Current())->allocator()->Use(reg());
- }
-}
-
-
-bool RegisterAllocator::is_used(Register reg) {
- return registers_.is_used(ToNumber(reg));
-}
-
-
-int RegisterAllocator::count(Register reg) {
- return registers_.count(ToNumber(reg));
-}
-
-
-void RegisterAllocator::Use(Register reg) {
- registers_.Use(ToNumber(reg));
-}
-
-
-void RegisterAllocator::Unuse(Register reg) {
- registers_.Unuse(ToNumber(reg));
-}
-
-
-TypeInfo Result::type_info() const {
- ASSERT(is_valid());
- return TypeInfo::FromInt(TypeInfoField::decode(value_));
-}
-
-
-void Result::set_type_info(TypeInfo info) {
- ASSERT(is_valid());
- value_ &= ~TypeInfoField::mask();
- value_ |= TypeInfoField::encode(info.ToInt());
-}
-
-
-bool Result::is_number() const {
- return type_info().IsNumber();
-}
-
-
-bool Result::is_smi() const {
- return type_info().IsSmi();
-}
-
-
-bool Result::is_integer32() const {
- return type_info().IsInteger32();
-}
-
-
-bool Result::is_double() const {
- return type_info().IsDouble();
-}
-
-} } // namespace v8::internal
-
-#endif // V8_REGISTER_ALLOCATOR_INL_H_
diff --git a/src/register-allocator.cc b/src/register-allocator.cc
deleted file mode 100644
index cb5e35f..0000000
--- a/src/register-allocator.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "v8.h"
-
-#include "codegen-inl.h"
-#include "register-allocator-inl.h"
-#include "virtual-frame-inl.h"
-
-namespace v8 {
-namespace internal {
-
-// -------------------------------------------------------------------------
-// Result implementation.
-
-
-Result::Result(Register reg, TypeInfo info) {
- ASSERT(reg.is_valid() && !RegisterAllocator::IsReserved(reg));
- CodeGeneratorScope::Current(Isolate::Current())->allocator()->Use(reg);
- value_ = TypeField::encode(REGISTER)
- | TypeInfoField::encode(info.ToInt())
- | DataField::encode(reg.code_);
-}
-
-
-// -------------------------------------------------------------------------
-// RegisterAllocator implementation.
-
-
-Result RegisterAllocator::AllocateWithoutSpilling() {
- // Return the first free register, if any.
- int num = registers_.ScanForFreeRegister();
- if (num == RegisterAllocator::kInvalidRegister) {
- return Result();
- }
- return Result(RegisterAllocator::ToRegister(num));
-}
-
-
-Result RegisterAllocator::Allocate() {
- Result result = AllocateWithoutSpilling();
- if (!result.is_valid()) {
- // Ask the current frame to spill a register.
- ASSERT(cgen_->has_valid_frame());
- Register free_reg = cgen_->frame()->SpillAnyRegister();
- if (free_reg.is_valid()) {
- ASSERT(!is_used(free_reg));
- return Result(free_reg);
- }
- }
- return result;
-}
-
-
-Result RegisterAllocator::Allocate(Register target) {
- // If the target is not referenced, it can simply be allocated.
- if (!is_used(RegisterAllocator::ToNumber(target))) {
- return Result(target);
- }
- // If the target is only referenced in the frame, it can be spilled and
- // then allocated.
- ASSERT(cgen_->has_valid_frame());
- if (cgen_->frame()->is_used(RegisterAllocator::ToNumber(target)) &&
- count(target) == 1) {
- cgen_->frame()->Spill(target);
- ASSERT(!is_used(RegisterAllocator::ToNumber(target)));
- return Result(target);
- }
- // Otherwise (if it's referenced outside the frame) we cannot allocate it.
- return Result();
-}
-
-
-} } // namespace v8::internal
diff --git a/src/register-allocator.h b/src/register-allocator.h
deleted file mode 100644
index f0ef9c3..0000000
--- a/src/register-allocator.h
+++ /dev/null
@@ -1,310 +0,0 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_REGISTER_ALLOCATOR_H_
-#define V8_REGISTER_ALLOCATOR_H_
-
-#include "macro-assembler.h"
-#include "type-info.h"
-
-#if V8_TARGET_ARCH_IA32
-#include "ia32/register-allocator-ia32.h"
-#elif V8_TARGET_ARCH_X64
-#include "x64/register-allocator-x64.h"
-#elif V8_TARGET_ARCH_ARM
-#include "arm/register-allocator-arm.h"
-#elif V8_TARGET_ARCH_MIPS
-#include "mips/register-allocator-mips.h"
-#else
-#error Unsupported target architecture.
-#endif
-
-namespace v8 {
-namespace internal {
-
-
-// -------------------------------------------------------------------------
-// Results
-//
-// Results encapsulate the compile-time values manipulated by the code
-// generator. They can represent registers or constants.
-
-class Result BASE_EMBEDDED {
- public:
- enum Type {
- INVALID,
- REGISTER,
- CONSTANT
- };
-
- // Construct an invalid result.
- Result() { invalidate(); }
-
- // Construct a register Result.
- explicit Result(Register reg, TypeInfo info = TypeInfo::Unknown());
-
- // Construct a Result whose value is a compile-time constant.
- explicit Result(Handle<Object> value) {
- ZoneObjectList* constant_list = Isolate::Current()->result_constant_list();
- TypeInfo info = TypeInfo::TypeFromValue(value);
- value_ = TypeField::encode(CONSTANT)
- | TypeInfoField::encode(info.ToInt())
- | IsUntaggedInt32Field::encode(false)
- | DataField::encode(constant_list->length());
- constant_list->Add(value);
- }
-
- // The copy constructor and assignment operators could each create a new
- // register reference.
- inline Result(const Result& other);
-
- inline Result& operator=(const Result& other);
-
- inline ~Result();
-
- inline void Unuse();
-
- Type type() const { return TypeField::decode(value_); }
-
- void invalidate() { value_ = TypeField::encode(INVALID); }
-
- inline TypeInfo type_info() const;
- inline void set_type_info(TypeInfo info);
- inline bool is_number() const;
- inline bool is_smi() const;
- inline bool is_integer32() const;
- inline bool is_double() const;
-
- bool is_valid() const { return type() != INVALID; }
- bool is_register() const { return type() == REGISTER; }
- bool is_constant() const { return type() == CONSTANT; }
-
- // An untagged int32 Result contains a signed int32 in a register
- // or as a constant. These are only allowed in a side-effect-free
- // int32 calculation, and if a non-int32 input shows up or an overflow
- // occurs, we bail out and drop all the int32 values. Constants are
- // not converted to int32 until they are loaded into a register.
- bool is_untagged_int32() const {
- return IsUntaggedInt32Field::decode(value_);
- }
- void set_untagged_int32(bool value) {
- value_ &= ~IsUntaggedInt32Field::mask();
- value_ |= IsUntaggedInt32Field::encode(value);
- }
-
- Register reg() const {
- ASSERT(is_register());
- uint32_t reg = DataField::decode(value_);
- Register result;
- result.code_ = reg;
- return result;
- }
-
- Handle<Object> handle() const {
- ASSERT(type() == CONSTANT);
- return Isolate::Current()->result_constant_list()->
- at(DataField::decode(value_));
- }
-
- // Move this result to an arbitrary register. The register is not
- // necessarily spilled from the frame or even singly-referenced outside
- // it.
- void ToRegister();
-
- // Move this result to a specified register. The register is spilled from
- // the frame, and the register is singly-referenced (by this result)
- // outside the frame.
- void ToRegister(Register reg);
-
- private:
- uint32_t value_;
-
- // Declare BitFields with template parameters <type, start, size>.
- class TypeField: public BitField<Type, 0, 2> {};
- class TypeInfoField : public BitField<int, 2, 6> {};
- class IsUntaggedInt32Field : public BitField<bool, 8, 1> {};
- class DataField: public BitField<uint32_t, 9, 32 - 9> {};
-
- inline void CopyTo(Result* destination) const;
-
- friend class CodeGeneratorScope;
-};
-
-
-// -------------------------------------------------------------------------
-// Register file
-//
-// The register file tracks reference counts for the processor registers.
-// It is used by both the register allocator and the virtual frame.
-
-class RegisterFile BASE_EMBEDDED {
- public:
- RegisterFile() { Reset(); }
-
- void Reset() {
- for (int i = 0; i < kNumRegisters; i++) {
- ref_counts_[i] = 0;
- }
- }
-
- // Predicates and accessors for the reference counts.
- bool is_used(int num) {
- ASSERT(0 <= num && num < kNumRegisters);
- return ref_counts_[num] > 0;
- }
-
- int count(int num) {
- ASSERT(0 <= num && num < kNumRegisters);
- return ref_counts_[num];
- }
-
- // Record a use of a register by incrementing its reference count.
- void Use(int num) {
- ASSERT(0 <= num && num < kNumRegisters);
- ref_counts_[num]++;
- }
-
- // Record that a register will no longer be used by decrementing its
- // reference count.
- void Unuse(int num) {
- ASSERT(is_used(num));
- ref_counts_[num]--;
- }
-
- // Copy the reference counts from this register file to the other.
- void CopyTo(RegisterFile* other) {
- for (int i = 0; i < kNumRegisters; i++) {
- other->ref_counts_[i] = ref_counts_[i];
- }
- }
-
- private:
- // C++ doesn't like zero length arrays, so we make the array length 1 even if
- // we don't need it.
- static const int kNumRegisters =
- (RegisterAllocatorConstants::kNumRegisters == 0) ?
- 1 : RegisterAllocatorConstants::kNumRegisters;
-
- int ref_counts_[kNumRegisters];
-
- // Very fast inlined loop to find a free register. Used in
- // RegisterAllocator::AllocateWithoutSpilling. Returns
- // kInvalidRegister if no free register found.
- int ScanForFreeRegister() {
- for (int i = 0; i < RegisterAllocatorConstants::kNumRegisters; i++) {
- if (!is_used(i)) return i;
- }
- return RegisterAllocatorConstants::kInvalidRegister;
- }
-
- friend class RegisterAllocator;
-};
-
-
-// -------------------------------------------------------------------------
-// Register allocator
-//
-
-class RegisterAllocator BASE_EMBEDDED {
- public:
- static const int kNumRegisters =
- RegisterAllocatorConstants::kNumRegisters;
- static const int kInvalidRegister =
- RegisterAllocatorConstants::kInvalidRegister;
-
- explicit RegisterAllocator(CodeGenerator* cgen) : cgen_(cgen) {}
-
- // True if the register is reserved by the code generator, false if it
- // can be freely used by the allocator Defined in the
- // platform-specific XXX-inl.h files..
- static inline bool IsReserved(Register reg);
-
- // Convert between (unreserved) assembler registers and allocator
- // numbers. Defined in the platform-specific XXX-inl.h files.
- static inline int ToNumber(Register reg);
- static inline Register ToRegister(int num);
-
- // Predicates and accessors for the registers' reference counts.
- bool is_used(int num) { return registers_.is_used(num); }
- inline bool is_used(Register reg);
-
- int count(int num) { return registers_.count(num); }
- inline int count(Register reg);
-
- // Explicitly record a reference to a register.
- void Use(int num) { registers_.Use(num); }
- inline void Use(Register reg);
-
- // Explicitly record that a register will no longer be used.
- void Unuse(int num) { registers_.Unuse(num); }
- inline void Unuse(Register reg);
-
- // Reset the register reference counts to free all non-reserved registers.
- void Reset() { registers_.Reset(); }
-
- // Initialize the register allocator for entry to a JS function. On
- // entry, the (non-reserved) registers used by the JS calling
- // convention are referenced and the other (non-reserved) registers
- // are free.
- inline void Initialize();
-
- // Allocate a free register and return a register result if possible or
- // fail and return an invalid result.
- Result Allocate();
-
- // Allocate a specific register if possible, spilling it from the
- // current frame if necessary, or else fail and return an invalid
- // result.
- Result Allocate(Register target);
-
- // Allocate a free register without spilling any from the current
- // frame or fail and return an invalid result.
- Result AllocateWithoutSpilling();
-
- // Allocate a free byte register without spilling any from the current
- // frame or fail and return an invalid result.
- Result AllocateByteRegisterWithoutSpilling();
-
- // Copy the internal state to a register file, to be restored later by
- // RestoreFrom.
- void SaveTo(RegisterFile* register_file) {
- registers_.CopyTo(register_file);
- }
-
- // Restore the internal state.
- void RestoreFrom(RegisterFile* register_file) {
- register_file->CopyTo(®isters_);
- }
-
- private:
- CodeGenerator* cgen_;
- RegisterFile registers_;
-};
-
-} } // namespace v8::internal
-
-#endif // V8_REGISTER_ALLOCATOR_H_
diff --git a/src/rewriter.cc b/src/rewriter.cc
index 780314d..efe8044 100644
--- a/src/rewriter.cc
+++ b/src/rewriter.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -36,649 +36,6 @@
namespace v8 {
namespace internal {
-class AstOptimizer: public AstVisitor {
- public:
- explicit AstOptimizer() : has_function_literal_(false) {}
-
- void Optimize(ZoneList<Statement*>* statements);
-
- private:
- // Used for loop condition analysis. Cleared before visiting a loop
- // condition, set when a function literal is visited.
- bool has_function_literal_;
-
- // Helpers
- void OptimizeArguments(ZoneList<Expression*>* arguments);
-
- // Node visitors.
-#define DEF_VISIT(type) \
- virtual void Visit##type(type* node);
- AST_NODE_LIST(DEF_VISIT)
-#undef DEF_VISIT
-
- DISALLOW_COPY_AND_ASSIGN(AstOptimizer);
-};
-
-
-void AstOptimizer::Optimize(ZoneList<Statement*>* statements) {
- int len = statements->length();
- for (int i = 0; i < len; i++) {
- Visit(statements->at(i));
- }
-}
-
-
-void AstOptimizer::OptimizeArguments(ZoneList<Expression*>* arguments) {
- for (int i = 0; i < arguments->length(); i++) {
- Visit(arguments->at(i));
- }
-}
-
-
-void AstOptimizer::VisitBlock(Block* node) {
- Optimize(node->statements());
-}
-
-
-void AstOptimizer::VisitExpressionStatement(ExpressionStatement* node) {
- node->expression()->set_no_negative_zero(true);
- Visit(node->expression());
-}
-
-
-void AstOptimizer::VisitIfStatement(IfStatement* node) {
- node->condition()->set_no_negative_zero(true);
- Visit(node->condition());
- Visit(node->then_statement());
- if (node->HasElseStatement()) {
- Visit(node->else_statement());
- }
-}
-
-
-void AstOptimizer::VisitDoWhileStatement(DoWhileStatement* node) {
- node->cond()->set_no_negative_zero(true);
- Visit(node->cond());
- Visit(node->body());
-}
-
-
-void AstOptimizer::VisitWhileStatement(WhileStatement* node) {
- has_function_literal_ = false;
- node->cond()->set_no_negative_zero(true);
- Visit(node->cond());
- node->set_may_have_function_literal(has_function_literal_);
- Visit(node->body());
-}
-
-
-void AstOptimizer::VisitForStatement(ForStatement* node) {
- if (node->init() != NULL) {
- Visit(node->init());
- }
- if (node->cond() != NULL) {
- has_function_literal_ = false;
- node->cond()->set_no_negative_zero(true);
- Visit(node->cond());
- node->set_may_have_function_literal(has_function_literal_);
- }
- Visit(node->body());
- if (node->next() != NULL) {
- Visit(node->next());
- }
-}
-
-
-void AstOptimizer::VisitForInStatement(ForInStatement* node) {
- Visit(node->each());
- Visit(node->enumerable());
- Visit(node->body());
-}
-
-
-void AstOptimizer::VisitTryCatchStatement(TryCatchStatement* node) {
- Visit(node->try_block());
- Visit(node->catch_var());
- Visit(node->catch_block());
-}
-
-
-void AstOptimizer::VisitTryFinallyStatement(TryFinallyStatement* node) {
- Visit(node->try_block());
- Visit(node->finally_block());
-}
-
-
-void AstOptimizer::VisitSwitchStatement(SwitchStatement* node) {
- node->tag()->set_no_negative_zero(true);
- Visit(node->tag());
- for (int i = 0; i < node->cases()->length(); i++) {
- CaseClause* clause = node->cases()->at(i);
- if (!clause->is_default()) {
- Visit(clause->label());
- }
- Optimize(clause->statements());
- }
-}
-
-
-void AstOptimizer::VisitContinueStatement(ContinueStatement* node) {
- USE(node);
-}
-
-
-void AstOptimizer::VisitBreakStatement(BreakStatement* node) {
- USE(node);
-}
-
-
-void AstOptimizer::VisitDeclaration(Declaration* node) {
- // Will not be reached by the current optimizations.
- USE(node);
-}
-
-
-void AstOptimizer::VisitEmptyStatement(EmptyStatement* node) {
- USE(node);
-}
-
-
-void AstOptimizer::VisitReturnStatement(ReturnStatement* node) {
- Visit(node->expression());
-}
-
-
-void AstOptimizer::VisitWithEnterStatement(WithEnterStatement* node) {
- Visit(node->expression());
-}
-
-
-void AstOptimizer::VisitWithExitStatement(WithExitStatement* node) {
- USE(node);
-}
-
-
-void AstOptimizer::VisitDebuggerStatement(DebuggerStatement* node) {
- USE(node);
-}
-
-
-void AstOptimizer::VisitFunctionLiteral(FunctionLiteral* node) {
- has_function_literal_ = true;
-}
-
-
-void AstOptimizer::VisitSharedFunctionInfoLiteral(
- SharedFunctionInfoLiteral* node) {
- USE(node);
-}
-
-
-void AstOptimizer::VisitConditional(Conditional* node) {
- node->condition()->set_no_negative_zero(true);
- Visit(node->condition());
- Visit(node->then_expression());
- Visit(node->else_expression());
-}
-
-
-void AstOptimizer::VisitVariableProxy(VariableProxy* node) {
- Variable* var = node->AsVariable();
- if (var != NULL) {
- if (var->type()->IsKnown()) {
- node->type()->CopyFrom(var->type());
- } else if (node->type()->IsLikelySmi()) {
- var->type()->SetAsLikelySmi();
- }
-
- if (FLAG_safe_int32_compiler) {
- if (var->IsStackAllocated() &&
- !var->is_arguments() &&
- var->mode() != Variable::CONST) {
- node->set_side_effect_free(true);
- }
- }
- }
-}
-
-
-void AstOptimizer::VisitLiteral(Literal* node) {
- Handle<Object> literal = node->handle();
- if (literal->IsSmi()) {
- node->type()->SetAsLikelySmi();
- node->set_side_effect_free(true);
- } else if (literal->IsHeapNumber()) {
- if (node->to_int32()) {
- // Any HeapNumber has an int32 value if it is the input to a bit op.
- node->set_side_effect_free(true);
- } else {
- double double_value = HeapNumber::cast(*literal)->value();
- int32_t int32_value = DoubleToInt32(double_value);
- node->set_side_effect_free(double_value == int32_value);
- }
- }
-}
-
-
-void AstOptimizer::VisitRegExpLiteral(RegExpLiteral* node) {
- USE(node);
-}
-
-
-void AstOptimizer::VisitArrayLiteral(ArrayLiteral* node) {
- for (int i = 0; i < node->values()->length(); i++) {
- Visit(node->values()->at(i));
- }
-}
-
-void AstOptimizer::VisitObjectLiteral(ObjectLiteral* node) {
- for (int i = 0; i < node->properties()->length(); i++) {
- Visit(node->properties()->at(i)->key());
- Visit(node->properties()->at(i)->value());
- }
-}
-
-
-void AstOptimizer::VisitCatchExtensionObject(CatchExtensionObject* node) {
- Visit(node->key());
- Visit(node->value());
-}
-
-
-void AstOptimizer::VisitAssignment(Assignment* node) {
- switch (node->op()) {
- case Token::INIT_VAR:
- case Token::INIT_CONST:
- case Token::ASSIGN:
- // No type can be infered from the general assignment.
- break;
- case Token::ASSIGN_BIT_OR:
- case Token::ASSIGN_BIT_XOR:
- case Token::ASSIGN_BIT_AND:
- case Token::ASSIGN_SHL:
- case Token::ASSIGN_SAR:
- case Token::ASSIGN_SHR:
- node->type()->SetAsLikelySmiIfUnknown();
- node->target()->type()->SetAsLikelySmiIfUnknown();
- node->value()->type()->SetAsLikelySmiIfUnknown();
- node->value()->set_to_int32(true);
- node->value()->set_no_negative_zero(true);
- break;
- case Token::ASSIGN_ADD:
- case Token::ASSIGN_SUB:
- case Token::ASSIGN_MUL:
- case Token::ASSIGN_DIV:
- case Token::ASSIGN_MOD:
- if (node->type()->IsLikelySmi()) {
- node->target()->type()->SetAsLikelySmiIfUnknown();
- node->value()->type()->SetAsLikelySmiIfUnknown();
- }
- break;
- default:
- UNREACHABLE();
- break;
- }
-
- Visit(node->target());
- Visit(node->value());
-
- switch (node->op()) {
- case Token::INIT_VAR:
- case Token::INIT_CONST:
- case Token::ASSIGN:
- // Pure assignment copies the type from the value.
- node->type()->CopyFrom(node->value()->type());
- break;
- case Token::ASSIGN_BIT_OR:
- case Token::ASSIGN_BIT_XOR:
- case Token::ASSIGN_BIT_AND:
- case Token::ASSIGN_SHL:
- case Token::ASSIGN_SAR:
- case Token::ASSIGN_SHR:
- // Should have been setup above already.
- break;
- case Token::ASSIGN_ADD:
- case Token::ASSIGN_SUB:
- case Token::ASSIGN_MUL:
- case Token::ASSIGN_DIV:
- case Token::ASSIGN_MOD:
- if (node->type()->IsUnknown()) {
- if (node->target()->type()->IsLikelySmi() ||
- node->value()->type()->IsLikelySmi()) {
- node->type()->SetAsLikelySmi();
- }
- }
- break;
- default:
- UNREACHABLE();
- break;
- }
-
- // Since this is an assignment. We have to propagate this node's type to the
- // variable.
- VariableProxy* proxy = node->target()->AsVariableProxy();
- if (proxy != NULL) {
- Variable* var = proxy->AsVariable();
- if (var != NULL) {
- StaticType* var_type = var->type();
- if (var_type->IsUnknown()) {
- var_type->CopyFrom(node->type());
- } else if (var_type->IsLikelySmi()) {
- // We do not reset likely types to Unknown.
- }
- }
- }
-}
-
-
-void AstOptimizer::VisitThrow(Throw* node) {
- Visit(node->exception());
-}
-
-
-void AstOptimizer::VisitProperty(Property* node) {
- node->key()->set_no_negative_zero(true);
- Visit(node->obj());
- Visit(node->key());
-}
-
-
-void AstOptimizer::VisitCall(Call* node) {
- Visit(node->expression());
- OptimizeArguments(node->arguments());
-}
-
-
-void AstOptimizer::VisitCallNew(CallNew* node) {
- Visit(node->expression());
- OptimizeArguments(node->arguments());
-}
-
-
-void AstOptimizer::VisitCallRuntime(CallRuntime* node) {
- OptimizeArguments(node->arguments());
-}
-
-
-void AstOptimizer::VisitUnaryOperation(UnaryOperation* node) {
- if (node->op() == Token::ADD || node->op() == Token::SUB) {
- node->expression()->set_no_negative_zero(node->no_negative_zero());
- } else {
- node->expression()->set_no_negative_zero(true);
- }
- Visit(node->expression());
- if (FLAG_safe_int32_compiler) {
- switch (node->op()) {
- case Token::BIT_NOT:
- node->expression()->set_no_negative_zero(true);
- node->expression()->set_to_int32(true);
- // Fall through.
- case Token::ADD:
- case Token::SUB:
- node->set_side_effect_free(node->expression()->side_effect_free());
- break;
- case Token::NOT:
- case Token::DELETE:
- case Token::TYPEOF:
- case Token::VOID:
- break;
- default:
- UNREACHABLE();
- break;
- }
- } else if (node->op() == Token::BIT_NOT) {
- node->expression()->set_to_int32(true);
- }
-}
-
-
-void AstOptimizer::VisitIncrementOperation(IncrementOperation* node) {
- UNREACHABLE();
-}
-
-
-void AstOptimizer::VisitCountOperation(CountOperation* node) {
- // Count operations assume that they work on Smis.
- node->expression()->set_no_negative_zero(node->is_prefix() ?
- true :
- node->no_negative_zero());
- node->type()->SetAsLikelySmiIfUnknown();
- node->expression()->type()->SetAsLikelySmiIfUnknown();
- Visit(node->expression());
-}
-
-
-static bool CouldBeNegativeZero(AstNode* node) {
- Literal* literal = node->AsLiteral();
- if (literal != NULL) {
- Handle<Object> handle = literal->handle();
- if (handle->IsString() || handle->IsSmi()) {
- return false;
- } else if (handle->IsHeapNumber()) {
- double double_value = HeapNumber::cast(*handle)->value();
- if (double_value != 0) {
- return false;
- }
- }
- }
- BinaryOperation* binary = node->AsBinaryOperation();
- if (binary != NULL && Token::IsBitOp(binary->op())) {
- return false;
- }
- return true;
-}
-
-
-static bool CouldBePositiveZero(AstNode* node) {
- Literal* literal = node->AsLiteral();
- if (literal != NULL) {
- Handle<Object> handle = literal->handle();
- if (handle->IsSmi()) {
- if (Smi::cast(*handle) != Smi::FromInt(0)) {
- return false;
- }
- } else if (handle->IsHeapNumber()) {
- // Heap number literal can't be +0, because that's a Smi.
- return false;
- }
- }
- return true;
-}
-
-
-void AstOptimizer::VisitBinaryOperation(BinaryOperation* node) {
- // Depending on the operation we can propagate this node's type down the
- // AST nodes.
- Token::Value op = node->op();
- switch (op) {
- case Token::COMMA:
- case Token::OR:
- node->left()->set_no_negative_zero(true);
- node->right()->set_no_negative_zero(node->no_negative_zero());
- break;
- case Token::AND:
- node->left()->set_no_negative_zero(node->no_negative_zero());
- node->right()->set_no_negative_zero(node->no_negative_zero());
- break;
- case Token::BIT_OR:
- case Token::BIT_XOR:
- case Token::BIT_AND:
- case Token::SHL:
- case Token::SAR:
- case Token::SHR:
- node->type()->SetAsLikelySmiIfUnknown();
- node->left()->type()->SetAsLikelySmiIfUnknown();
- node->right()->type()->SetAsLikelySmiIfUnknown();
- node->left()->set_to_int32(true);
- node->right()->set_to_int32(true);
- node->left()->set_no_negative_zero(true);
- node->right()->set_no_negative_zero(true);
- break;
- case Token::MUL: {
- VariableProxy* lvar_proxy = node->left()->AsVariableProxy();
- VariableProxy* rvar_proxy = node->right()->AsVariableProxy();
- if (lvar_proxy != NULL && rvar_proxy != NULL) {
- Variable* lvar = lvar_proxy->AsVariable();
- Variable* rvar = rvar_proxy->AsVariable();
- if (lvar != NULL && rvar != NULL) {
- if (lvar->mode() == Variable::VAR && rvar->mode() == Variable::VAR) {
- Slot* lslot = lvar->AsSlot();
- Slot* rslot = rvar->AsSlot();
- if (lslot->type() == rslot->type() &&
- (lslot->type() == Slot::PARAMETER ||
- lslot->type() == Slot::LOCAL) &&
- lslot->index() == rslot->index()) {
- // A number squared doesn't give negative zero.
- node->set_no_negative_zero(true);
- }
- }
- }
- }
- }
- case Token::ADD:
- case Token::SUB:
- case Token::DIV:
- case Token::MOD: {
- if (node->type()->IsLikelySmi()) {
- node->left()->type()->SetAsLikelySmiIfUnknown();
- node->right()->type()->SetAsLikelySmiIfUnknown();
- }
- if (op == Token::ADD && (!CouldBeNegativeZero(node->left()) ||
- !CouldBeNegativeZero(node->right()))) {
- node->left()->set_no_negative_zero(true);
- node->right()->set_no_negative_zero(true);
- } else if (op == Token::SUB && (!CouldBeNegativeZero(node->left()) ||
- !CouldBePositiveZero(node->right()))) {
- node->left()->set_no_negative_zero(true);
- node->right()->set_no_negative_zero(true);
- } else {
- node->left()->set_no_negative_zero(node->no_negative_zero());
- node->right()->set_no_negative_zero(node->no_negative_zero());
- }
- if (node->op() == Token::DIV) {
- node->right()->set_no_negative_zero(false);
- } else if (node->op() == Token::MOD) {
- node->right()->set_no_negative_zero(true);
- }
- break;
- }
- default:
- UNREACHABLE();
- break;
- }
-
- Visit(node->left());
- Visit(node->right());
-
- // After visiting the operand nodes we have to check if this node's type
- // can be updated. If it does, then we can push that information down
- // towards the leaves again if the new information is an upgrade over the
- // previous type of the operand nodes.
- if (node->type()->IsUnknown()) {
- if (node->left()->type()->IsLikelySmi() ||
- node->right()->type()->IsLikelySmi()) {
- node->type()->SetAsLikelySmi();
- }
- if (node->type()->IsLikelySmi()) {
- // The type of this node changed to LIKELY_SMI. Propagate this knowledge
- // down through the nodes.
- if (node->left()->type()->IsUnknown()) {
- node->left()->type()->SetAsLikelySmi();
- Visit(node->left());
- }
- if (node->right()->type()->IsUnknown()) {
- node->right()->type()->SetAsLikelySmi();
- Visit(node->right());
- }
- }
- }
-
- if (FLAG_safe_int32_compiler) {
- switch (node->op()) {
- case Token::COMMA:
- case Token::OR:
- case Token::AND:
- break;
- case Token::BIT_OR:
- case Token::BIT_XOR:
- case Token::BIT_AND:
- case Token::SHL:
- case Token::SAR:
- case Token::SHR:
- // Add one to the number of bit operations in this expression.
- node->set_num_bit_ops(1);
- // Fall through.
- case Token::ADD:
- case Token::SUB:
- case Token::MUL:
- case Token::DIV:
- case Token::MOD:
- node->set_side_effect_free(node->left()->side_effect_free() &&
- node->right()->side_effect_free());
- node->set_num_bit_ops(node->num_bit_ops() +
- node->left()->num_bit_ops() +
- node->right()->num_bit_ops());
- if (!node->no_negative_zero() && node->op() == Token::MUL) {
- node->set_side_effect_free(false);
- }
- break;
- default:
- UNREACHABLE();
- break;
- }
- }
-}
-
-
-void AstOptimizer::VisitCompareOperation(CompareOperation* node) {
- if (node->type()->IsKnown()) {
- // Propagate useful information down towards the leaves.
- node->left()->type()->SetAsLikelySmiIfUnknown();
- node->right()->type()->SetAsLikelySmiIfUnknown();
- }
-
- node->left()->set_no_negative_zero(true);
- // Only [[HasInstance]] has the right argument passed unchanged to it.
- node->right()->set_no_negative_zero(true);
-
- Visit(node->left());
- Visit(node->right());
-
- // After visiting the operand nodes we have to check if this node's type
- // can be updated. If it does, then we can push that information down
- // towards the leaves again if the new information is an upgrade over the
- // previous type of the operand nodes.
- if (node->type()->IsUnknown()) {
- if (node->left()->type()->IsLikelySmi() ||
- node->right()->type()->IsLikelySmi()) {
- node->type()->SetAsLikelySmi();
- }
- if (node->type()->IsLikelySmi()) {
- // The type of this node changed to LIKELY_SMI. Propagate this knowledge
- // down through the nodes.
- if (node->left()->type()->IsUnknown()) {
- node->left()->type()->SetAsLikelySmi();
- Visit(node->left());
- }
- if (node->right()->type()->IsUnknown()) {
- node->right()->type()->SetAsLikelySmi();
- Visit(node->right());
- }
- }
- }
-}
-
-
-void AstOptimizer::VisitCompareToNull(CompareToNull* node) {
- Visit(node->expression());
-}
-
-
-void AstOptimizer::VisitThisFunction(ThisFunction* node) {
- USE(node);
-}
-
-
class Processor: public AstVisitor {
public:
explicit Processor(Variable* result)
@@ -943,11 +300,6 @@
}
-void Processor::VisitIncrementOperation(IncrementOperation* node) {
- UNREACHABLE();
-}
-
-
void Processor::VisitCountOperation(CountOperation* node) {
USE(node);
UNREACHABLE();
@@ -1005,20 +357,4 @@
}
-// Assumes code has been parsed and scopes have been analyzed. Mutates the
-// AST, so the AST should not continue to be used in the case of failure.
-bool Rewriter::Analyze(CompilationInfo* info) {
- FunctionLiteral* function = info->function();
- ASSERT(function != NULL && function->scope() != NULL);
-
- ZoneList<Statement*>* body = function->body();
- if (FLAG_optimize_ast && !body->is_empty()) {
- AstOptimizer optimizer;
- optimizer.Optimize(body);
- if (optimizer.HasStackOverflow()) return false;
- }
- return true;
-}
-
-
} } // namespace v8::internal
diff --git a/src/rewriter.h b/src/rewriter.h
index 62e1b7f..59914d9 100644
--- a/src/rewriter.h
+++ b/src/rewriter.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -42,15 +42,6 @@
// Assumes code has been parsed and scopes have been analyzed. Mutates the
// AST, so the AST should not continue to be used in the case of failure.
static bool Rewrite(CompilationInfo* info);
-
- // Perform a suite of simple non-iterative analyses of the AST. Mark
- // expressions that are likely smis, expressions without side effects,
- // expressions whose value will be converted to Int32, and expressions in a
- // context where +0 and -0 are treated the same.
- //
- // Assumes code has been parsed and scopes have been analyzed. Mutates the
- // AST, so the AST should not continue to be used in the case of failure.
- static bool Analyze(CompilationInfo* info);
};
diff --git a/src/runtime-profiler.cc b/src/runtime-profiler.cc
index c6e2b46..8d258ac 100644
--- a/src/runtime-profiler.cc
+++ b/src/runtime-profiler.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -107,12 +107,6 @@
}
-static bool IsOptimizable(JSFunction* function) {
- Code* code = function->code();
- return code->kind() == Code::FUNCTION && code->optimizable();
-}
-
-
Atomic32 RuntimeProfiler::state_ = 0;
// TODO(isolates): Create the semaphore lazily and clean it up when no
// longer required.
@@ -120,6 +114,11 @@
Semaphore* RuntimeProfiler::semaphore_ = OS::CreateSemaphore(0);
#endif
+#ifdef DEBUG
+bool RuntimeProfiler::has_been_globally_setup_ = false;
+#endif
+bool RuntimeProfiler::enabled_ = false;
+
RuntimeProfiler::RuntimeProfiler(Isolate* isolate)
: isolate_(isolate),
@@ -130,24 +129,31 @@
js_ratio_(0),
sampler_window_position_(0),
optimize_soon_list_(NULL),
- state_window_position_(0) {
- state_counts_[0] = kStateWindowSize;
- state_counts_[1] = 0;
+ state_window_position_(0),
+ state_window_ticks_(0) {
+ state_counts_[IN_NON_JS_STATE] = kStateWindowSize;
+ state_counts_[IN_JS_STATE] = 0;
+ STATIC_ASSERT(IN_NON_JS_STATE == 0);
memset(state_window_, 0, sizeof(state_window_));
ClearSampleBuffer();
}
-bool RuntimeProfiler::IsEnabled() {
- return V8::UseCrankshaft() && FLAG_opt;
+void RuntimeProfiler::GlobalSetup() {
+ ASSERT(!has_been_globally_setup_);
+ enabled_ = V8::UseCrankshaft() && FLAG_opt;
+#ifdef DEBUG
+ has_been_globally_setup_ = true;
+#endif
}
void RuntimeProfiler::Optimize(JSFunction* function, bool eager, int delay) {
- ASSERT(IsOptimizable(function));
+ ASSERT(function->IsOptimizable());
if (FLAG_trace_opt) {
PrintF("[marking (%s) ", eager ? "eagerly" : "lazily");
function->PrintName();
+ PrintF(" 0x%" V8PRIxPTR, reinterpret_cast<intptr_t>(function->address()));
PrintF(" for recompilation");
if (delay > 0) {
PrintF(" (delayed %0.3f ms)", static_cast<double>(delay) / 1000);
@@ -243,7 +249,7 @@
if (current->IsValid()) {
Handle<JSFunction> function = current->function();
int delay = current->Delay();
- if (IsOptimizable(*function)) {
+ if (function->IsOptimizable()) {
Optimize(*function, true, delay);
}
}
@@ -258,7 +264,7 @@
JSFunction* samples[kSamplerFrameCount];
int sample_count = 0;
int frame_count = 0;
- for (JavaScriptFrameIterator it;
+ for (JavaScriptFrameIterator it(isolate_);
frame_count++ < kSamplerFrameCount && !it.done();
it.Advance()) {
JavaScriptFrame* frame = it.frame();
@@ -288,7 +294,7 @@
}
// Do not record non-optimizable functions.
- if (!IsOptimizable(function)) continue;
+ if (!function->IsOptimizable()) continue;
samples[sample_count++] = function;
int function_size = function->shared()->SourceSize();
@@ -328,7 +334,7 @@
void RuntimeProfiler::OptimizeSoon(JSFunction* function) {
- if (!IsOptimizable(function)) return;
+ if (!function->IsOptimizable()) return;
PendingListNode* node = new PendingListNode(function);
node->set_next(optimize_soon_list_);
optimize_soon_list_ = node;
@@ -344,8 +350,12 @@
ASSERT(IsPowerOf2(kStateWindowSize));
state_window_position_ = (state_window_position_ + 1) &
(kStateWindowSize - 1);
+ // Note: to calculate correct ratio we have to track how many valid
+ // ticks are actually in the state window, because on profiler
+ // startup this number can be less than the window size.
+ state_window_ticks_ = Min(kStateWindowSize, state_window_ticks_ + 1);
NoBarrier_Store(&js_ratio_, state_counts_[IN_JS_STATE] * 100 /
- kStateWindowSize);
+ state_window_ticks_);
}
#endif
@@ -363,6 +373,7 @@
void RuntimeProfiler::Setup() {
+ ASSERT(has_been_globally_setup_);
ClearSampleBuffer();
// If the ticker hasn't already started, make sure to do so to get
// the ticks for the runtime profiler.
diff --git a/src/runtime-profiler.h b/src/runtime-profiler.h
index 8074035..692b4ff 100644
--- a/src/runtime-profiler.h
+++ b/src/runtime-profiler.h
@@ -40,18 +40,16 @@
class PendingListNode;
class Semaphore;
-
-enum SamplerState {
- IN_NON_JS_STATE = 0,
- IN_JS_STATE = 1
-};
-
-
class RuntimeProfiler {
public:
explicit RuntimeProfiler(Isolate* isolate);
- static bool IsEnabled();
+ static void GlobalSetup();
+
+ static inline bool IsEnabled() {
+ ASSERT(has_been_globally_setup_);
+ return enabled_;
+ }
void OptimizeNow();
void OptimizeSoon(JSFunction* function);
@@ -101,6 +99,11 @@
static const int kSamplerWindowSize = 16;
static const int kStateWindowSize = 128;
+ enum SamplerState {
+ IN_NON_JS_STATE = 0,
+ IN_JS_STATE = 1
+ };
+
static void HandleWakeUp(Isolate* isolate);
void Optimize(JSFunction* function, bool eager, int delay);
@@ -137,6 +140,7 @@
SamplerState state_window_[kStateWindowSize];
int state_window_position_;
+ int state_window_ticks_;
int state_counts_[2];
// Possible state values:
@@ -144,6 +148,11 @@
// 0 or positive => the number of isolates running JavaScript code.
static Atomic32 state_;
static Semaphore* semaphore_;
+
+#ifdef DEBUG
+ static bool has_been_globally_setup_;
+#endif
+ static bool enabled_;
};
diff --git a/src/runtime.cc b/src/runtime.cc
index c979849..53c048e 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -224,17 +224,13 @@
}
-static MaybeObject* Runtime_CloneLiteralBoilerplate(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CloneLiteralBoilerplate) {
CONVERT_CHECKED(JSObject, boilerplate, args[0]);
return DeepCopyBoilerplate(isolate, boilerplate);
}
-static MaybeObject* Runtime_CloneShallowLiteralBoilerplate(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CloneShallowLiteralBoilerplate) {
CONVERT_CHECKED(JSObject, boilerplate, args[0]);
return isolate->heap()->CopyJSObject(boilerplate);
}
@@ -475,9 +471,7 @@
}
-static MaybeObject* Runtime_CreateArrayLiteralBoilerplate(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateArrayLiteralBoilerplate) {
// Takes a FixedArray of elements containing the literal elements of
// the array literal and produces JSArray with those elements.
// Additionally takes the literals array of the surrounding function
@@ -499,8 +493,7 @@
}
-static MaybeObject* Runtime_CreateObjectLiteral(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateObjectLiteral) {
HandleScope scope(isolate);
ASSERT(args.length() == 4);
CONVERT_ARG_CHECKED(FixedArray, literals, 0);
@@ -526,9 +519,7 @@
}
-static MaybeObject* Runtime_CreateObjectLiteralShallow(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateObjectLiteralShallow) {
HandleScope scope(isolate);
ASSERT(args.length() == 4);
CONVERT_ARG_CHECKED(FixedArray, literals, 0);
@@ -554,8 +545,7 @@
}
-static MaybeObject* Runtime_CreateArrayLiteral(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateArrayLiteral) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
CONVERT_ARG_CHECKED(FixedArray, literals, 0);
@@ -574,9 +564,7 @@
}
-static MaybeObject* Runtime_CreateArrayLiteralShallow(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateArrayLiteralShallow) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
CONVERT_ARG_CHECKED(FixedArray, literals, 0);
@@ -599,9 +587,7 @@
}
-static MaybeObject* Runtime_CreateCatchExtensionObject(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateCatchExtensionObject) {
ASSERT(args.length() == 2);
CONVERT_CHECKED(String, key, args[0]);
Object* value = args[1];
@@ -625,8 +611,7 @@
}
-static MaybeObject* Runtime_ClassOf(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ClassOf) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
Object* obj = args[0];
@@ -635,8 +620,7 @@
}
-static MaybeObject* Runtime_IsInPrototypeChain(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsInPrototypeChain) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
// See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
@@ -652,8 +636,7 @@
// Inserts an object as the hidden prototype of another object.
-static MaybeObject* Runtime_SetHiddenPrototype(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetHiddenPrototype) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
CONVERT_CHECKED(JSObject, jsobject, args[0]);
@@ -695,11 +678,10 @@
}
-static MaybeObject* Runtime_IsConstructCall(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsConstructCall) {
NoHandleAllocation ha;
ASSERT(args.length() == 0);
- JavaScriptFrameIterator it;
+ JavaScriptFrameIterator it(isolate);
return isolate->heap()->ToBoolean(it.frame()->IsConstructor());
}
@@ -824,8 +806,7 @@
// [false, value, Writeable, Enumerable, Configurable]
// if args[1] is an accessor on args[0]
// [true, GetFunction, SetFunction, Enumerable, Configurable]
-static MaybeObject* Runtime_GetOwnProperty(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOwnProperty) {
ASSERT(args.length() == 2);
Heap* heap = isolate->heap();
HandleScope scope(isolate);
@@ -962,16 +943,14 @@
}
-static MaybeObject* Runtime_PreventExtensions(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_PreventExtensions) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(JSObject, obj, args[0]);
return obj->PreventExtensions();
}
-static MaybeObject* Runtime_IsExtensible(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsExtensible) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(JSObject, obj, args[0]);
if (obj->IsJSGlobalProxy()) {
@@ -985,8 +964,7 @@
}
-static MaybeObject* Runtime_RegExpCompile(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpCompile) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
CONVERT_ARG_CHECKED(JSRegExp, re, 0);
@@ -998,8 +976,7 @@
}
-static MaybeObject* Runtime_CreateApiFunction(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateApiFunction) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
@@ -1007,8 +984,7 @@
}
-static MaybeObject* Runtime_IsTemplate(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsTemplate) {
ASSERT(args.length() == 1);
Object* arg = args[0];
bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
@@ -1016,8 +992,7 @@
}
-static MaybeObject* Runtime_GetTemplateField(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetTemplateField) {
ASSERT(args.length() == 2);
CONVERT_CHECKED(HeapObject, templ, args[0]);
CONVERT_CHECKED(Smi, field, args[1]);
@@ -1036,8 +1011,7 @@
}
-static MaybeObject* Runtime_DisableAccessChecks(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DisableAccessChecks) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(HeapObject, object, args[0]);
Map* old_map = object->map();
@@ -1057,8 +1031,7 @@
}
-static MaybeObject* Runtime_EnableAccessChecks(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_EnableAccessChecks) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(HeapObject, object, args[0]);
Map* old_map = object->map();
@@ -1089,8 +1062,7 @@
}
-static MaybeObject* Runtime_DeclareGlobals(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) {
ASSERT(args.length() == 4);
HandleScope scope(isolate);
Handle<GlobalObject> global = Handle<GlobalObject>(
@@ -1233,8 +1205,7 @@
}
-static MaybeObject* Runtime_DeclareContextSlot(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareContextSlot) {
HandleScope scope(isolate);
ASSERT(args.length() == 4);
@@ -1340,8 +1311,7 @@
}
-static MaybeObject* Runtime_InitializeVarGlobal(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeVarGlobal) {
NoHandleAllocation nha;
// args[0] == name
// args[1] == strict_mode
@@ -1436,8 +1406,7 @@
}
-static MaybeObject* Runtime_InitializeConstGlobal(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeConstGlobal) {
// All constants are declared with an initial value. The name
// of the constant is the first argument and the initial value
// is the second.
@@ -1527,9 +1496,7 @@
}
-static MaybeObject* Runtime_InitializeConstContextSlot(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeConstContextSlot) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
@@ -1636,9 +1603,8 @@
}
-static MaybeObject* Runtime_OptimizeObjectForAddingMultipleProperties(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*,
+ Runtime_OptimizeObjectForAddingMultipleProperties) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_CHECKED(JSObject, object, 0);
@@ -1650,8 +1616,7 @@
}
-static MaybeObject* Runtime_RegExpExec(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpExec) {
HandleScope scope(isolate);
ASSERT(args.length() == 4);
CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
@@ -1673,8 +1638,7 @@
}
-static MaybeObject* Runtime_RegExpConstructResult(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpConstructResult) {
ASSERT(args.length() == 3);
CONVERT_SMI_CHECKED(elements_count, args[0]);
if (elements_count > JSArray::kMaxFastElementsLength) {
@@ -1707,8 +1671,7 @@
}
-static MaybeObject* Runtime_RegExpInitializeObject(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpInitializeObject) {
AssertNoAllocation no_alloc;
ASSERT(args.length() == 5);
CONVERT_CHECKED(JSRegExp, regexp, args[0]);
@@ -1774,9 +1737,7 @@
}
-static MaybeObject* Runtime_FinishArrayPrototypeSetup(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FinishArrayPrototypeSetup) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSArray, prototype, 0);
@@ -1805,8 +1766,7 @@
}
-static MaybeObject* Runtime_SpecialArrayFunctions(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SpecialArrayFunctions) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSObject, holder, 0);
@@ -1823,8 +1783,7 @@
}
-static MaybeObject* Runtime_GetGlobalReceiver(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetGlobalReceiver) {
// Returns a real global receiver, not one of builtins object.
Context* global_context =
isolate->context()->global()->global_context();
@@ -1832,9 +1791,7 @@
}
-static MaybeObject* Runtime_MaterializeRegExpLiteral(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MaterializeRegExpLiteral) {
HandleScope scope(isolate);
ASSERT(args.length() == 4);
CONVERT_ARG_CHECKED(FixedArray, literals, 0);
@@ -1864,8 +1821,7 @@
}
-static MaybeObject* Runtime_FunctionGetName(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetName) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -1874,8 +1830,7 @@
}
-static MaybeObject* Runtime_FunctionSetName(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetName) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -1886,9 +1841,7 @@
}
-static MaybeObject* Runtime_FunctionRemovePrototype(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionRemovePrototype) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -1900,8 +1853,7 @@
}
-static MaybeObject* Runtime_FunctionGetScript(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetScript) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
@@ -1913,8 +1865,7 @@
}
-static MaybeObject* Runtime_FunctionGetSourceCode(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetSourceCode) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -1923,9 +1874,7 @@
}
-static MaybeObject* Runtime_FunctionGetScriptSourcePosition(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetScriptSourcePosition) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -1935,9 +1884,7 @@
}
-static MaybeObject* Runtime_FunctionGetPositionForOffset(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetPositionForOffset) {
ASSERT(args.length() == 2);
CONVERT_CHECKED(Code, code, args[0]);
@@ -1950,9 +1897,7 @@
}
-static MaybeObject* Runtime_FunctionSetInstanceClassName(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetInstanceClassName) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -1963,8 +1908,7 @@
}
-static MaybeObject* Runtime_FunctionSetLength(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetLength) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -1975,8 +1919,7 @@
}
-static MaybeObject* Runtime_FunctionSetPrototype(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetPrototype) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -1991,8 +1934,7 @@
}
-static MaybeObject* Runtime_FunctionIsAPIFunction(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionIsAPIFunction) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -2002,8 +1944,7 @@
}
-static MaybeObject* Runtime_FunctionIsBuiltin(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionIsBuiltin) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -2013,8 +1954,7 @@
}
-static MaybeObject* Runtime_SetCode(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetCode) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
@@ -2077,9 +2017,7 @@
}
-static MaybeObject* Runtime_SetExpectedNumberOfProperties(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetExpectedNumberOfProperties) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_CHECKED(JSFunction, function, 0);
@@ -2102,8 +2040,7 @@
}
-static MaybeObject* Runtime_StringCharCodeAt(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringCharCodeAt) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -2139,8 +2076,7 @@
}
-static MaybeObject* Runtime_CharFromCode(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CharFromCode) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
return CharFromCode(isolate, args[0]);
@@ -2874,9 +2810,7 @@
}
-static MaybeObject* Runtime_StringReplaceRegExpWithString(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringReplaceRegExpWithString) {
ASSERT(args.length() == 4);
CONVERT_CHECKED(String, subject, args[0]);
@@ -2978,8 +2912,7 @@
}
-static MaybeObject* Runtime_StringIndexOf(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringIndexOf) {
HandleScope scope(isolate); // create a new handle scope
ASSERT(args.length() == 3);
@@ -3031,8 +2964,7 @@
return -1;
}
-static MaybeObject* Runtime_StringLastIndexOf(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringLastIndexOf) {
HandleScope scope(isolate); // create a new handle scope
ASSERT(args.length() == 3);
@@ -3089,8 +3021,7 @@
}
-static MaybeObject* Runtime_StringLocaleCompare(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringLocaleCompare) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -3138,8 +3069,7 @@
}
-static MaybeObject* Runtime_SubString(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SubString) {
NoHandleAllocation ha;
ASSERT(args.length() == 3);
@@ -3166,8 +3096,7 @@
}
-static MaybeObject* Runtime_StringMatch(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringMatch) {
ASSERT_EQ(3, args.length());
CONVERT_ARG_CHECKED(String, subject, 0);
@@ -3533,8 +3462,7 @@
}
-static MaybeObject* Runtime_RegExpExecMultiple(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpExecMultiple) {
ASSERT(args.length() == 4);
HandleScope handles(isolate);
@@ -3589,8 +3517,7 @@
}
-static MaybeObject* Runtime_NumberToRadixString(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToRadixString) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -3629,8 +3556,7 @@
}
-static MaybeObject* Runtime_NumberToFixed(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToFixed) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -3655,8 +3581,7 @@
}
-static MaybeObject* Runtime_NumberToExponential(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToExponential) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -3681,8 +3606,7 @@
}
-static MaybeObject* Runtime_NumberToPrecision(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToPrecision) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -3792,8 +3716,7 @@
}
-static MaybeObject* Runtime_GetProperty(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetProperty) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -3805,8 +3728,7 @@
// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
-static MaybeObject* Runtime_KeyedGetProperty(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_KeyedGetProperty) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -3880,9 +3802,7 @@
// Steps 9c & 12 - replace an existing data property with an accessor property.
// Step 12 - update an existing accessor property with an accessor or generic
// descriptor.
-static MaybeObject* Runtime_DefineOrRedefineAccessorProperty(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineAccessorProperty) {
ASSERT(args.length() == 5);
HandleScope scope(isolate);
CONVERT_ARG_CHECKED(JSObject, obj, 0);
@@ -3919,9 +3839,7 @@
// Steps 9b & 12 - replace an existing accessor property with a data property.
// Step 12 - update an existing data property with a data or generic
// descriptor.
-static MaybeObject* Runtime_DefineOrRedefineDataProperty(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineDataProperty) {
ASSERT(args.length() == 4);
HandleScope scope(isolate);
CONVERT_ARG_CHECKED(JSObject, js_object, 0);
@@ -4157,8 +4075,7 @@
}
-static MaybeObject* Runtime_SetProperty(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetProperty) {
NoHandleAllocation ha;
RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
@@ -4191,9 +4108,7 @@
// Set a local property, even if it is READ_ONLY. If the property does not
// exist, it will be added with attributes NONE.
-static MaybeObject* Runtime_IgnoreAttributesAndSetProperty(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IgnoreAttributesAndSetProperty) {
NoHandleAllocation ha;
RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
CONVERT_CHECKED(JSObject, object, args[0]);
@@ -4214,8 +4129,7 @@
}
-static MaybeObject* Runtime_DeleteProperty(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DeleteProperty) {
NoHandleAllocation ha;
ASSERT(args.length() == 3);
@@ -4246,8 +4160,7 @@
}
-static MaybeObject* Runtime_HasLocalProperty(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_HasLocalProperty) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
CONVERT_CHECKED(String, key, args[1]);
@@ -4277,8 +4190,7 @@
}
-static MaybeObject* Runtime_HasProperty(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_HasProperty) {
NoHandleAllocation na;
ASSERT(args.length() == 2);
@@ -4292,8 +4204,7 @@
}
-static MaybeObject* Runtime_HasElement(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_HasElement) {
NoHandleAllocation na;
ASSERT(args.length() == 2);
@@ -4308,8 +4219,7 @@
}
-static MaybeObject* Runtime_IsPropertyEnumerable(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsPropertyEnumerable) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -4326,8 +4236,7 @@
}
-static MaybeObject* Runtime_GetPropertyNames(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetPropertyNames) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSObject, object, 0);
@@ -4340,8 +4249,7 @@
// all enumerable properties of the object and its prototypes
// have none, the map of the object. This is used to speed up
// the check for deletions during a for-in.
-static MaybeObject* Runtime_GetPropertyNamesFast(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetPropertyNamesFast) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(JSObject, raw_object, args[0]);
@@ -4377,8 +4285,7 @@
// Return the names of the local named properties.
// args[0]: object
-static MaybeObject* Runtime_GetLocalPropertyNames(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLocalPropertyNames) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
if (!args[0]->IsJSObject()) {
@@ -4464,8 +4371,7 @@
// Return the names of the local indexed properties.
// args[0]: object
-static MaybeObject* Runtime_GetLocalElementNames(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLocalElementNames) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
if (!args[0]->IsJSObject()) {
@@ -4482,8 +4388,7 @@
// Return information on whether an object has a named or indexed interceptor.
// args[0]: object
-static MaybeObject* Runtime_GetInterceptorInfo(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetInterceptorInfo) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
if (!args[0]->IsJSObject()) {
@@ -4501,9 +4406,7 @@
// Return property names from named interceptor.
// args[0]: object
-static MaybeObject* Runtime_GetNamedInterceptorPropertyNames(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetNamedInterceptorPropertyNames) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSObject, obj, 0);
@@ -4518,9 +4421,7 @@
// Return element names from indexed interceptor.
// args[0]: object
-static MaybeObject* Runtime_GetIndexedInterceptorElementNames(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetIndexedInterceptorElementNames) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSObject, obj, 0);
@@ -4533,8 +4434,7 @@
}
-static MaybeObject* Runtime_LocalKeys(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LocalKeys) {
ASSERT_EQ(args.length(), 1);
CONVERT_CHECKED(JSObject, raw_object, args[0]);
HandleScope scope(isolate);
@@ -4579,13 +4479,12 @@
}
-static MaybeObject* Runtime_GetArgumentsProperty(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetArgumentsProperty) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
// Compute the frame holding the arguments.
- JavaScriptFrameIterator it;
+ JavaScriptFrameIterator it(isolate);
it.AdvanceToArgumentsFrame();
JavaScriptFrame* frame = it.frame();
@@ -4633,8 +4532,7 @@
}
-static MaybeObject* Runtime_ToFastProperties(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ToFastProperties) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
@@ -4650,8 +4548,7 @@
}
-static MaybeObject* Runtime_ToSlowProperties(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ToSlowProperties) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
@@ -4664,8 +4561,7 @@
}
-static MaybeObject* Runtime_ToBool(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ToBool) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -4675,8 +4571,7 @@
// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
// Possible optimizations: put the type string into the oddballs.
-static MaybeObject* Runtime_Typeof(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Typeof) {
NoHandleAllocation ha;
Object* obj = args[0];
@@ -4735,8 +4630,7 @@
}
-static MaybeObject* Runtime_StringToNumber(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringToNumber) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
CONVERT_CHECKED(String, subject, args[0]);
@@ -4786,13 +4680,12 @@
}
// Slower case.
- return isolate->heap()->NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
+ return isolate->heap()->NumberFromDouble(
+ StringToDouble(isolate->unicode_cache(), subject, ALLOW_HEX));
}
-static MaybeObject* Runtime_StringFromCharCodeArray(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringFromCharCodeArray) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -4872,8 +4765,7 @@
}
-static MaybeObject* Runtime_URIEscape(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_URIEscape) {
const char hex_chars[] = "0123456789ABCDEF";
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -4992,8 +4884,7 @@
}
-static MaybeObject* Runtime_URIUnescape(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_URIUnescape) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
CONVERT_CHECKED(String, source, args[0]);
@@ -5237,8 +5128,7 @@
}
-static MaybeObject* Runtime_QuoteJSONString(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_QuoteJSONString) {
NoHandleAllocation ha;
CONVERT_CHECKED(String, str, args[0]);
if (!str->IsFlat()) {
@@ -5260,8 +5150,7 @@
}
-static MaybeObject* Runtime_QuoteJSONStringComma(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_QuoteJSONStringComma) {
NoHandleAllocation ha;
CONVERT_CHECKED(String, str, args[0]);
if (!str->IsFlat()) {
@@ -5282,8 +5171,7 @@
}
}
-static MaybeObject* Runtime_StringParseInt(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringParseInt) {
NoHandleAllocation ha;
CONVERT_CHECKED(String, s, args[0]);
@@ -5292,18 +5180,18 @@
s->TryFlatten();
RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
- double value = StringToInt(s, radix);
+ double value = StringToInt(isolate->unicode_cache(), s, radix);
return isolate->heap()->NumberFromDouble(value);
}
-static MaybeObject* Runtime_StringParseFloat(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringParseFloat) {
NoHandleAllocation ha;
CONVERT_CHECKED(String, str, args[0]);
// ECMA-262 section 15.1.2.3, empty string is NaN
- double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
+ double value = StringToDouble(isolate->unicode_cache(),
+ str, ALLOW_TRAILING_JUNK, OS::nan_value());
// Create a number object from the value.
return isolate->heap()->NumberFromDouble(value);
@@ -5589,15 +5477,13 @@
}
-static MaybeObject* Runtime_StringToLowerCase(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringToLowerCase) {
return ConvertCase<ToLowerTraits>(
args, isolate, isolate->runtime_state()->to_lower_mapping());
}
-static MaybeObject* Runtime_StringToUpperCase(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringToUpperCase) {
return ConvertCase<ToUpperTraits>(
args, isolate, isolate->runtime_state()->to_upper_mapping());
}
@@ -5608,8 +5494,7 @@
}
-static MaybeObject* Runtime_StringTrim(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringTrim) {
NoHandleAllocation ha;
ASSERT(args.length() == 3);
@@ -5659,8 +5544,7 @@
}
-static MaybeObject* Runtime_StringSplit(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringSplit) {
ASSERT(args.length() == 3);
HandleScope handle_scope(isolate);
CONVERT_ARG_CHECKED(String, subject, 0);
@@ -5791,8 +5675,7 @@
// Converts a String to JSArray.
// For example, "foo" => ["f", "o", "o"].
-static MaybeObject* Runtime_StringToArray(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringToArray) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_CHECKED(String, s, 0);
@@ -5840,8 +5723,7 @@
}
-static MaybeObject* Runtime_NewStringWrapper(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NewStringWrapper) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
CONVERT_CHECKED(String, value, args[0]);
@@ -5856,8 +5738,7 @@
}
-static MaybeObject* Runtime_NumberToString(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToString) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -5868,9 +5749,7 @@
}
-static MaybeObject* Runtime_NumberToStringSkipCache(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToStringSkipCache) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -5881,8 +5760,7 @@
}
-static MaybeObject* Runtime_NumberToInteger(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToInteger) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -5896,9 +5774,7 @@
}
-static MaybeObject* Runtime_NumberToIntegerMapMinusZero(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToIntegerMapMinusZero) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -5917,8 +5793,7 @@
}
-static MaybeObject* Runtime_NumberToJSUint32(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToJSUint32) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -5927,8 +5802,7 @@
}
-static MaybeObject* Runtime_NumberToJSInt32(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToJSInt32) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -5944,8 +5818,7 @@
// Converts a Number to a Smi, if possible. Returns NaN if the number is not
// a small integer.
-static MaybeObject* Runtime_NumberToSmi(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToSmi) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -5964,16 +5837,14 @@
}
-static MaybeObject* Runtime_AllocateHeapNumber(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_AllocateHeapNumber) {
NoHandleAllocation ha;
ASSERT(args.length() == 0);
return isolate->heap()->AllocateHeapNumber(0);
}
-static MaybeObject* Runtime_NumberAdd(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberAdd) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -5983,8 +5854,7 @@
}
-static MaybeObject* Runtime_NumberSub(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberSub) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -5994,8 +5864,7 @@
}
-static MaybeObject* Runtime_NumberMul(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberMul) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -6005,8 +5874,7 @@
}
-static MaybeObject* Runtime_NumberUnaryMinus(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberUnaryMinus) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -6015,8 +5883,7 @@
}
-static MaybeObject* Runtime_NumberAlloc(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberAlloc) {
NoHandleAllocation ha;
ASSERT(args.length() == 0);
@@ -6024,8 +5891,7 @@
}
-static MaybeObject* Runtime_NumberDiv(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberDiv) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -6035,8 +5901,7 @@
}
-static MaybeObject* Runtime_NumberMod(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberMod) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -6049,8 +5914,7 @@
}
-static MaybeObject* Runtime_StringAdd(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringAdd) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
CONVERT_CHECKED(String, str1, args[0]);
@@ -6099,8 +5963,7 @@
}
-static MaybeObject* Runtime_StringBuilderConcat(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) {
NoHandleAllocation ha;
ASSERT(args.length() == 3);
CONVERT_CHECKED(JSArray, array, args[0]);
@@ -6213,8 +6076,7 @@
}
-static MaybeObject* Runtime_StringBuilderJoin(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderJoin) {
NoHandleAllocation ha;
ASSERT(args.length() == 3);
CONVERT_CHECKED(JSArray, array, args[0]);
@@ -6298,8 +6160,7 @@
}
-static MaybeObject* Runtime_NumberOr(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberOr) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -6309,8 +6170,7 @@
}
-static MaybeObject* Runtime_NumberAnd(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberAnd) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -6320,8 +6180,7 @@
}
-static MaybeObject* Runtime_NumberXor(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberXor) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -6331,8 +6190,7 @@
}
-static MaybeObject* Runtime_NumberNot(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberNot) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -6341,8 +6199,7 @@
}
-static MaybeObject* Runtime_NumberShl(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberShl) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -6352,8 +6209,7 @@
}
-static MaybeObject* Runtime_NumberShr(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberShr) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -6363,8 +6219,7 @@
}
-static MaybeObject* Runtime_NumberSar(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberSar) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -6374,8 +6229,7 @@
}
-static MaybeObject* Runtime_NumberEquals(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberEquals) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -6394,8 +6248,7 @@
}
-static MaybeObject* Runtime_StringEquals(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringEquals) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -6413,8 +6266,7 @@
}
-static MaybeObject* Runtime_NumberCompare(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberCompare) {
NoHandleAllocation ha;
ASSERT(args.length() == 3);
@@ -6429,9 +6281,7 @@
// Compare two Smis as if they were converted to strings and then
// compared lexicographically.
-static MaybeObject* Runtime_SmiLexicographicCompare(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SmiLexicographicCompare) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -6554,8 +6404,7 @@
}
-static MaybeObject* Runtime_StringCompare(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringCompare) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -6590,8 +6439,7 @@
}
-static MaybeObject* Runtime_Math_acos(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_acos) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
isolate->counters()->math_acos()->Increment();
@@ -6601,8 +6449,7 @@
}
-static MaybeObject* Runtime_Math_asin(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_asin) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
isolate->counters()->math_asin()->Increment();
@@ -6612,8 +6459,7 @@
}
-static MaybeObject* Runtime_Math_atan(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_atan) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
isolate->counters()->math_atan()->Increment();
@@ -6626,8 +6472,7 @@
static const double kPiDividedBy4 = 0.78539816339744830962;
-static MaybeObject* Runtime_Math_atan2(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_atan2) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
isolate->counters()->math_atan2()->Increment();
@@ -6650,8 +6495,7 @@
}
-static MaybeObject* Runtime_Math_ceil(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_ceil) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
isolate->counters()->math_ceil()->Increment();
@@ -6661,8 +6505,7 @@
}
-static MaybeObject* Runtime_Math_cos(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_cos) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
isolate->counters()->math_cos()->Increment();
@@ -6672,8 +6515,7 @@
}
-static MaybeObject* Runtime_Math_exp(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_exp) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
isolate->counters()->math_exp()->Increment();
@@ -6683,8 +6525,7 @@
}
-static MaybeObject* Runtime_Math_floor(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_floor) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
isolate->counters()->math_floor()->Increment();
@@ -6694,8 +6535,7 @@
}
-static MaybeObject* Runtime_Math_log(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_log) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
isolate->counters()->math_log()->Increment();
@@ -6705,8 +6545,7 @@
}
-static MaybeObject* Runtime_Math_pow(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_pow) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
isolate->counters()->math_pow()->Increment();
@@ -6726,8 +6565,7 @@
// Fast version of Math.pow if we know that y is not an integer and
// y is not -0.5 or 0.5. Used as slowcase from codegen.
-static MaybeObject* Runtime_Math_pow_cfunction(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_pow_cfunction) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
CONVERT_DOUBLE_CHECKED(x, args[0]);
@@ -6742,8 +6580,7 @@
}
-static MaybeObject* Runtime_RoundNumber(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_RoundNumber) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
isolate->counters()->math_round()->Increment();
@@ -6760,9 +6597,16 @@
int exponent = number->get_exponent();
int sign = number->get_sign();
- // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
- // should be rounded to 2^30, which is not smi.
- if (!sign && exponent <= kSmiValueSize - 3) {
+ if (exponent < -1) {
+ // Number in range ]-0.5..0.5[. These always round to +/-zero.
+ if (sign) return isolate->heap()->minus_zero_value();
+ return Smi::FromInt(0);
+ }
+
+ // We compare with kSmiValueSize - 2 because (2^30 - 0.1) has exponent 29 and
+ // should be rounded to 2^30, which is not smi (for 31-bit smis, similar
+ // agument holds for 32-bit smis).
+ if (!sign && exponent < kSmiValueSize - 2) {
return Smi::FromInt(static_cast<int>(value + 0.5));
}
@@ -6779,8 +6623,7 @@
}
-static MaybeObject* Runtime_Math_sin(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_sin) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
isolate->counters()->math_sin()->Increment();
@@ -6790,8 +6633,7 @@
}
-static MaybeObject* Runtime_Math_sqrt(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_sqrt) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
isolate->counters()->math_sqrt()->Increment();
@@ -6801,8 +6643,7 @@
}
-static MaybeObject* Runtime_Math_tan(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_tan) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
isolate->counters()->math_tan()->Increment();
@@ -6857,8 +6698,7 @@
}
-static MaybeObject* Runtime_DateMakeDay(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DateMakeDay) {
NoHandleAllocation ha;
ASSERT(args.length() == 3);
@@ -7157,8 +6997,7 @@
}
-static MaybeObject* Runtime_DateYMDFromTime(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DateYMDFromTime) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -7181,8 +7020,7 @@
}
-static MaybeObject* Runtime_NewArgumentsFast(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NewArgumentsFast) {
NoHandleAllocation ha;
ASSERT(args.length() == 3);
@@ -7218,8 +7056,7 @@
}
-static MaybeObject* Runtime_NewClosure(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NewClosure) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
CONVERT_ARG_CHECKED(Context, context, 0);
@@ -7238,42 +7075,69 @@
return *result;
}
-static MaybeObject* Runtime_NewObjectFromBound(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+
+static SmartPointer<Object**> GetNonBoundArguments(int bound_argc,
+ int* total_argc) {
+ // Find frame containing arguments passed to the caller.
+ JavaScriptFrameIterator it;
+ JavaScriptFrame* frame = it.frame();
+ List<JSFunction*> functions(2);
+ frame->GetFunctions(&functions);
+ if (functions.length() > 1) {
+ int inlined_frame_index = functions.length() - 1;
+ JSFunction* inlined_function = functions[inlined_frame_index];
+ int args_count = inlined_function->shared()->formal_parameter_count();
+ ScopedVector<SlotRef> args_slots(args_count);
+ SlotRef::ComputeSlotMappingForArguments(frame,
+ inlined_frame_index,
+ &args_slots);
+
+ *total_argc = bound_argc + args_count;
+ SmartPointer<Object**> param_data(NewArray<Object**>(*total_argc));
+ for (int i = 0; i < args_count; i++) {
+ Handle<Object> val = args_slots[i].GetValue();
+ param_data[bound_argc + i] = val.location();
+ }
+ return param_data;
+ } else {
+ it.AdvanceToArgumentsFrame();
+ frame = it.frame();
+ int args_count = frame->ComputeParametersCount();
+
+ *total_argc = bound_argc + args_count;
+ SmartPointer<Object**> param_data(NewArray<Object**>(*total_argc));
+ for (int i = 0; i < args_count; i++) {
+ Handle<Object> val = Handle<Object>(frame->GetParameter(i));
+ param_data[bound_argc + i] = val.location();
+ }
+ return param_data;
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NewObjectFromBound) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
// First argument is a function to use as a constructor.
CONVERT_ARG_CHECKED(JSFunction, function, 0);
// Second argument is either null or an array of bound arguments.
- FixedArray* bound_args = NULL;
+ Handle<FixedArray> bound_args;
int bound_argc = 0;
if (!args[1]->IsNull()) {
CONVERT_ARG_CHECKED(JSArray, params, 1);
RUNTIME_ASSERT(params->HasFastElements());
- bound_args = FixedArray::cast(params->elements());
+ bound_args = Handle<FixedArray>(FixedArray::cast(params->elements()));
bound_argc = Smi::cast(params->length())->value();
}
- // Find frame containing arguments passed to the caller.
- JavaScriptFrameIterator it;
- JavaScriptFrame* frame = it.frame();
- ASSERT(!frame->is_optimized());
- it.AdvanceToArgumentsFrame();
- frame = it.frame();
- int argc = frame->ComputeParametersCount();
-
- // Prepend bound arguments to caller's arguments.
- int total_argc = bound_argc + argc;
- SmartPointer<Object**> param_data(NewArray<Object**>(total_argc));
+ int total_argc = 0;
+ SmartPointer<Object**> param_data =
+ GetNonBoundArguments(bound_argc, &total_argc);
for (int i = 0; i < bound_argc; i++) {
Handle<Object> val = Handle<Object>(bound_args->get(i));
param_data[i] = val.location();
}
- for (int i = 0; i < argc; i++) {
- Handle<Object> val = Handle<Object>(frame->GetParameter(i));
- param_data[bound_argc + i] = val.location();
- }
bool exception = false;
Handle<Object> result =
@@ -7304,8 +7168,7 @@
}
-static MaybeObject* Runtime_NewObject(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NewObject) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
@@ -7385,8 +7248,7 @@
}
-static MaybeObject* Runtime_FinalizeInstanceSize(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FinalizeInstanceSize) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
@@ -7398,8 +7260,7 @@
}
-static MaybeObject* Runtime_LazyCompile(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LazyCompile) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
@@ -7430,8 +7291,7 @@
}
-static MaybeObject* Runtime_LazyRecompile(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LazyRecompile) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
Handle<JSFunction> function = args.at<JSFunction>(0);
@@ -7462,8 +7322,7 @@
}
-static MaybeObject* Runtime_NotifyDeoptimized(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NotifyDeoptimized) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
RUNTIME_ASSERT(args[0]->IsSmi());
@@ -7473,15 +7332,14 @@
ASSERT(isolate->heap()->IsAllocationAllowed());
int frames = deoptimizer->output_count();
- JavaScriptFrameIterator it;
- JavaScriptFrame* frame = NULL;
- for (int i = 0; i < frames; i++) {
- if (i != 0) it.Advance();
- frame = it.frame();
- deoptimizer->InsertHeapNumberValues(frames - i - 1, frame);
- }
+ deoptimizer->MaterializeHeapNumbers();
delete deoptimizer;
+ JavaScriptFrameIterator it(isolate);
+ JavaScriptFrame* frame = NULL;
+ for (int i = 0; i < frames - 1; i++) it.Advance();
+ frame = it.frame();
+
RUNTIME_ASSERT(frame->function()->IsJSFunction());
Handle<JSFunction> function(JSFunction::cast(frame->function()), isolate);
Handle<Object> arguments;
@@ -7537,16 +7395,14 @@
}
-static MaybeObject* Runtime_NotifyOSR(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NotifyOSR) {
Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
delete deoptimizer;
return isolate->heap()->undefined_value();
}
-static MaybeObject* Runtime_DeoptimizeFunction(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DeoptimizeFunction) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSFunction, function, 0);
@@ -7558,9 +7414,17 @@
}
-static MaybeObject* Runtime_CompileForOnStackReplacement(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_OptimizeFunctionOnNextCall) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSFunction, function, 0);
+ if (!function->IsOptimizable()) return isolate->heap()->undefined_value();
+ function->MarkForLazyRecompilation();
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CompileForOnStackReplacement) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSFunction, function, 0);
@@ -7579,7 +7443,7 @@
// indirectly recursive and (b) an optimized invocation has been
// deoptimized so that we are currently in an unoptimized activation.
// Check for optimized activations of this function.
- JavaScriptFrameIterator it;
+ JavaScriptFrameIterator it(isolate);
while (succeeded && !it.done()) {
JavaScriptFrame* frame = it.frame();
succeeded = !frame->is_optimized() || frame->function() != *function;
@@ -7591,10 +7455,10 @@
if (succeeded) {
// The top JS function is this one, the PC is somewhere in the
// unoptimized code.
- JavaScriptFrameIterator it;
+ JavaScriptFrameIterator it(isolate);
JavaScriptFrame* frame = it.frame();
ASSERT(frame->function() == *function);
- ASSERT(frame->LookupCode(isolate) == *unoptimized);
+ ASSERT(frame->LookupCode() == *unoptimized);
ASSERT(unoptimized->contains(frame->pc()));
// Use linear search of the unoptimized code's stack check table to find
@@ -7674,8 +7538,7 @@
}
-static MaybeObject* Runtime_GetFunctionDelegate(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFunctionDelegate) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
RUNTIME_ASSERT(!args[0]->IsJSFunction());
@@ -7683,8 +7546,7 @@
}
-static MaybeObject* Runtime_GetConstructorDelegate(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetConstructorDelegate) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
RUNTIME_ASSERT(!args[0]->IsJSFunction());
@@ -7692,8 +7554,7 @@
}
-static MaybeObject* Runtime_NewContext(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NewContext) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -7744,24 +7605,21 @@
}
-static MaybeObject* Runtime_PushContext(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_PushContext) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
return PushContextHelper(isolate, args[0], false);
}
-static MaybeObject* Runtime_PushCatchContext(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_PushCatchContext) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
return PushContextHelper(isolate, args[0], true);
}
-static MaybeObject* Runtime_DeleteContextSlot(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DeleteContextSlot) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
@@ -7921,21 +7779,17 @@
}
-static ObjectPair Runtime_LoadContextSlot(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(ObjectPair, Runtime_LoadContextSlot) {
return LoadContextSlotHelper(args, isolate, true);
}
-static ObjectPair Runtime_LoadContextSlotNoReferenceError(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(ObjectPair, Runtime_LoadContextSlotNoReferenceError) {
return LoadContextSlotHelper(args, isolate, false);
}
-static MaybeObject* Runtime_StoreContextSlot(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StoreContextSlot) {
HandleScope scope(isolate);
ASSERT(args.length() == 4);
@@ -7985,8 +7839,17 @@
// The property exists in the extension context.
context_ext = Handle<JSObject>::cast(holder);
} else {
- // The property was not found. It needs to be stored in the global context.
+ // The property was not found.
ASSERT(attributes == ABSENT);
+
+ if (strict_mode == kStrictMode) {
+ // Throw in strict mode (assignment to undefined variable).
+ Handle<Object> error =
+ isolate->factory()->NewReferenceError(
+ "not_defined", HandleVector(&name, 1));
+ return isolate->Throw(*error);
+ }
+ // In non-strict mode, the property is stored in the global context.
attributes = NONE;
context_ext = Handle<JSObject>(isolate->context()->global());
}
@@ -8009,8 +7872,7 @@
}
-static MaybeObject* Runtime_Throw(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Throw) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
@@ -8018,8 +7880,7 @@
}
-static MaybeObject* Runtime_ReThrow(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ReThrow) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
@@ -8027,16 +7888,13 @@
}
-static MaybeObject* Runtime_PromoteScheduledException(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_PromoteScheduledException) {
ASSERT_EQ(0, args.length());
return isolate->PromoteScheduledException();
}
-static MaybeObject* Runtime_ThrowReferenceError(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ThrowReferenceError) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
@@ -8048,8 +7906,7 @@
}
-static MaybeObject* Runtime_StackGuard(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StackGuard) {
ASSERT(args.length() == 0);
// First check if this is a real stack overflow.
@@ -8148,8 +8005,7 @@
}
-static MaybeObject* Runtime_TraceEnter(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_TraceEnter) {
ASSERT(args.length() == 0);
NoHandleAllocation ha;
PrintTransition(NULL);
@@ -8157,16 +8013,14 @@
}
-static MaybeObject* Runtime_TraceExit(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_TraceExit) {
NoHandleAllocation ha;
PrintTransition(args[0]);
return args[0]; // return TOS
}
-static MaybeObject* Runtime_DebugPrint(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugPrint) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -8174,7 +8028,7 @@
if (args[0]->IsString()) {
// If we have a string, assume it's a code "marker"
// and print some interesting cpu debugging info.
- JavaScriptFrameIterator it;
+ JavaScriptFrameIterator it(isolate);
JavaScriptFrame* frame = it.frame();
PrintF("fp = %p, sp = %p, caller_sp = %p: ",
frame->fp(), frame->sp(), frame->caller_sp());
@@ -8197,8 +8051,7 @@
}
-static MaybeObject* Runtime_DebugTrace(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugTrace) {
ASSERT(args.length() == 0);
NoHandleAllocation ha;
isolate->PrintStack();
@@ -8206,8 +8059,7 @@
}
-static MaybeObject* Runtime_DateCurrentTime(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DateCurrentTime) {
NoHandleAllocation ha;
ASSERT(args.length() == 0);
@@ -8220,8 +8072,7 @@
}
-static MaybeObject* Runtime_DateParseString(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DateParseString) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
@@ -8237,10 +8088,14 @@
RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
bool result;
if (str->IsAsciiRepresentation()) {
- result = DateParser::Parse(str->ToAsciiVector(), output_array);
+ result = DateParser::Parse(str->ToAsciiVector(),
+ output_array,
+ isolate->unicode_cache());
} else {
ASSERT(str->IsTwoByteRepresentation());
- result = DateParser::Parse(str->ToUC16Vector(), output_array);
+ result = DateParser::Parse(str->ToUC16Vector(),
+ output_array,
+ isolate->unicode_cache());
}
if (result) {
@@ -8251,8 +8106,7 @@
}
-static MaybeObject* Runtime_DateLocalTimezone(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DateLocalTimezone) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -8262,8 +8116,7 @@
}
-static MaybeObject* Runtime_DateLocalTimeOffset(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DateLocalTimeOffset) {
NoHandleAllocation ha;
ASSERT(args.length() == 0);
@@ -8271,9 +8124,7 @@
}
-static MaybeObject* Runtime_DateDaylightSavingsOffset(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DateDaylightSavingsOffset) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -8282,8 +8133,7 @@
}
-static MaybeObject* Runtime_GlobalReceiver(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GlobalReceiver) {
ASSERT(args.length() == 1);
Object* global = args[0];
if (!global->IsJSGlobalObject()) return isolate->heap()->null_value();
@@ -8291,7 +8141,7 @@
}
-static MaybeObject* Runtime_ParseJson(RUNTIME_CALLING_CONVENTION) {
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ParseJson) {
HandleScope scope(isolate);
ASSERT_EQ(1, args.length());
CONVERT_ARG_CHECKED(String, source, 0);
@@ -8306,8 +8156,7 @@
}
-static MaybeObject* Runtime_CompileString(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CompileString) {
HandleScope scope(isolate);
ASSERT_EQ(1, args.length());
CONVERT_ARG_CHECKED(String, source, 0);
@@ -8346,9 +8195,7 @@
}
-static ObjectPair Runtime_ResolvePossiblyDirectEval(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(ObjectPair, Runtime_ResolvePossiblyDirectEval) {
ASSERT(args.length() == 4);
HandleScope scope(isolate);
@@ -8424,9 +8271,7 @@
}
-static ObjectPair Runtime_ResolvePossiblyDirectEvalNoLookup(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(ObjectPair, Runtime_ResolvePossiblyDirectEvalNoLookup) {
ASSERT(args.length() == 4);
HandleScope scope(isolate);
@@ -8449,9 +8294,7 @@
}
-static MaybeObject* Runtime_SetNewFunctionAttributes(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetNewFunctionAttributes) {
// This utility adjusts the property attributes for newly created Function
// object ("new Function(...)") by changing the map.
// All it does is changing the prototype property to enumerable
@@ -8471,8 +8314,7 @@
}
-static MaybeObject* Runtime_AllocateInNewSpace(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_AllocateInNewSpace) {
// Allocate a block of memory in NewSpace (filled with a filler).
// Use as fallback for allocation in generated code when NewSpace
// is full.
@@ -8497,8 +8339,7 @@
// Push an object unto an array of objects if it is not already in the
// array. Returns true if the element was pushed on the stack and
// false otherwise.
-static MaybeObject* Runtime_PushIfAbsent(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_PushIfAbsent) {
ASSERT(args.length() == 2);
CONVERT_CHECKED(JSArray, array, args[0]);
CONVERT_CHECKED(JSObject, element, args[1]);
@@ -8947,8 +8788,7 @@
* TODO(581): Fix non-compliance for very large concatenations and update to
* following the ECMAScript 5 specification.
*/
-static MaybeObject* Runtime_ArrayConcat(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayConcat) {
ASSERT(args.length() == 1);
HandleScope handle_scope(isolate);
@@ -9036,8 +8876,7 @@
// This will not allocate (flatten the string), but it may run
// very slowly for very deeply nested ConsStrings. For debugging use only.
-static MaybeObject* Runtime_GlobalPrint(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GlobalPrint) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -9055,8 +8894,7 @@
// and are followed by non-existing element. Does not change the length
// property.
// Returns the number of non-undefined elements collected.
-static MaybeObject* Runtime_RemoveArrayHoles(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_RemoveArrayHoles) {
ASSERT(args.length() == 2);
CONVERT_CHECKED(JSObject, object, args[0]);
CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
@@ -9065,8 +8903,7 @@
// Move contents of argument 0 (an array) to argument 1 (an array)
-static MaybeObject* Runtime_MoveArrayContents(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MoveArrayContents) {
ASSERT(args.length() == 2);
CONVERT_CHECKED(JSArray, from, args[0]);
CONVERT_CHECKED(JSArray, to, args[1]);
@@ -9093,9 +8930,7 @@
// How many elements does this object/array have?
-static MaybeObject* Runtime_EstimateNumberOfElements(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_EstimateNumberOfElements) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(JSObject, object, args[0]);
HeapObject* elements = object->elements();
@@ -9109,8 +8944,7 @@
}
-static MaybeObject* Runtime_SwapElements(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SwapElements) {
HandleScope handle_scope(isolate);
ASSERT_EQ(3, args.length());
@@ -9145,8 +8979,7 @@
// intervals (pair of a negative integer (-start-1) followed by a
// positive (length)) or undefined values.
// Intervals can span over some keys that are not in the object.
-static MaybeObject* Runtime_GetArrayKeys(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetArrayKeys) {
ASSERT(args.length() == 2);
HandleScope scope(isolate);
CONVERT_ARG_CHECKED(JSObject, array, 0);
@@ -9186,8 +9019,7 @@
// to the way accessors are implemented, it is set for both the getter
// and setter on the first call to DefineAccessor and ignored on
// subsequent calls.
-static MaybeObject* Runtime_DefineAccessor(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineAccessor) {
RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
// Compute attributes.
PropertyAttributes attributes = NONE;
@@ -9207,8 +9039,7 @@
}
-static MaybeObject* Runtime_LookupAccessor(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LookupAccessor) {
ASSERT(args.length() == 3);
CONVERT_CHECKED(JSObject, obj, args[0]);
CONVERT_CHECKED(String, name, args[1]);
@@ -9218,8 +9049,7 @@
#ifdef ENABLE_DEBUGGER_SUPPORT
-static MaybeObject* Runtime_DebugBreak(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugBreak) {
ASSERT(args.length() == 0);
return Execution::DebugBreakHelper();
}
@@ -9241,8 +9071,7 @@
// args[0]: debug event listener function to set or null or undefined for
// clearing the event listener function
// args[1]: object supplied during callback
-static MaybeObject* Runtime_SetDebugEventListener(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetDebugEventListener) {
ASSERT(args.length() == 2);
RUNTIME_ASSERT(args[0]->IsJSFunction() ||
args[0]->IsUndefined() ||
@@ -9255,8 +9084,7 @@
}
-static MaybeObject* Runtime_Break(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Break) {
ASSERT(args.length() == 0);
isolate->stack_guard()->DebugBreak();
return isolate->heap()->undefined_value();
@@ -9332,9 +9160,7 @@
// 4: Setter function if defined
// Items 2-4 are only filled if the property has either a getter or a setter
// defined through __defineGetter__ and/or __defineSetter__.
-static MaybeObject* Runtime_DebugGetPropertyDetails(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugGetPropertyDetails) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
@@ -9434,8 +9260,7 @@
}
-static MaybeObject* Runtime_DebugGetProperty(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugGetProperty) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
@@ -9454,9 +9279,7 @@
// Return the property type calculated from the property details.
// args[0]: smi with property details.
-static MaybeObject* Runtime_DebugPropertyTypeFromDetails(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugPropertyTypeFromDetails) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(Smi, details, args[0]);
PropertyType type = PropertyDetails(details).type();
@@ -9466,9 +9289,7 @@
// Return the property attribute calculated from the property details.
// args[0]: smi with property details.
-static MaybeObject* Runtime_DebugPropertyAttributesFromDetails(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugPropertyAttributesFromDetails) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(Smi, details, args[0]);
PropertyAttributes attributes = PropertyDetails(details).attributes();
@@ -9478,9 +9299,7 @@
// Return the property insertion index calculated from the property details.
// args[0]: smi with property details.
-static MaybeObject* Runtime_DebugPropertyIndexFromDetails(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugPropertyIndexFromDetails) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(Smi, details, args[0]);
int index = PropertyDetails(details).index();
@@ -9491,9 +9310,7 @@
// Return property value from named interceptor.
// args[0]: object
// args[1]: property name
-static MaybeObject* Runtime_DebugNamedInterceptorPropertyValue(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugNamedInterceptorPropertyValue) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_CHECKED(JSObject, obj, 0);
@@ -9508,9 +9325,7 @@
// Return element value from indexed interceptor.
// args[0]: object
// args[1]: index
-static MaybeObject* Runtime_DebugIndexedInterceptorElementValue(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugIndexedInterceptorElementValue) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_CHECKED(JSObject, obj, 0);
@@ -9521,8 +9336,7 @@
}
-static MaybeObject* Runtime_CheckExecutionState(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CheckExecutionState) {
ASSERT(args.length() >= 1);
CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
// Check that the break id is valid.
@@ -9536,14 +9350,14 @@
}
-static MaybeObject* Runtime_GetFrameCount(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameCount) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
// Check arguments.
Object* result;
- { MaybeObject* maybe_result = Runtime_CheckExecutionState(args, isolate);
+ { MaybeObject* maybe_result = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
if (!maybe_result->ToObject(&result)) return maybe_result;
}
@@ -9554,7 +9368,7 @@
// If there is no JavaScript stack frame count is 0.
return Smi::FromInt(0);
}
- for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
+ for (JavaScriptFrameIterator it(isolate, id); !it.done(); it.Advance()) n++;
return Smi::FromInt(n);
}
@@ -9587,14 +9401,14 @@
// Arguments name, value
// Locals name, value
// Return value if any
-static MaybeObject* Runtime_GetFrameDetails(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
// Check arguments.
Object* check;
- { MaybeObject* maybe_check = Runtime_CheckExecutionState(args, isolate);
+ { MaybeObject* maybe_check = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
if (!maybe_check->ToObject(&check)) return maybe_check;
}
CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
@@ -9607,7 +9421,7 @@
return heap->undefined_value();
}
int count = 0;
- JavaScriptFrameIterator it(id);
+ JavaScriptFrameIterator it(isolate, id);
for (; !it.done(); it.Advance()) {
if (count == index) break;
count++;
@@ -9615,7 +9429,7 @@
if (it.done()) return heap->undefined_value();
bool is_optimized_frame =
- it.frame()->LookupCode(isolate)->kind() == Code::OPTIMIZED_FUNCTION;
+ it.frame()->LookupCode()->kind() == Code::OPTIMIZED_FUNCTION;
// Traverse the saved contexts chain to find the active context for the
// selected frame.
@@ -9630,7 +9444,7 @@
// Find source position.
int position =
- it.frame()->LookupCode(isolate)->SourcePosition(it.frame()->pc());
+ it.frame()->LookupCode()->SourcePosition(it.frame()->pc());
// Check for constructor frame.
bool constructor = it.frame()->IsConstructor();
@@ -9692,7 +9506,7 @@
// to the frame information.
Handle<Object> return_value = isolate->factory()->undefined_value();
if (at_return) {
- StackFrameIterator it2;
+ StackFrameIterator it2(isolate);
Address internal_frame_sp = NULL;
while (!it2.done()) {
if (it2.frame()->is_internal()) {
@@ -10038,6 +9852,10 @@
at_local_ = index < 0;
} else if (context_->is_function_context()) {
at_local_ = true;
+ } else if (context_->closure() != *function_) {
+ // The context_ is a with block from the outer function.
+ ASSERT(context_->has_extension());
+ at_local_ = true;
}
}
@@ -10211,21 +10029,21 @@
};
-static MaybeObject* Runtime_GetScopeCount(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetScopeCount) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
// Check arguments.
Object* check;
- { MaybeObject* maybe_check = Runtime_CheckExecutionState(args, isolate);
+ { MaybeObject* maybe_check = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
if (!maybe_check->ToObject(&check)) return maybe_check;
}
CONVERT_CHECKED(Smi, wrapped_id, args[1]);
// Get the frame where the debugging is performed.
StackFrame::Id id = UnwrapFrameId(wrapped_id);
- JavaScriptFrameIterator it(id);
+ JavaScriptFrameIterator it(isolate, id);
JavaScriptFrame* frame = it.frame();
// Count the visible scopes.
@@ -10250,14 +10068,14 @@
// The array returned contains the following information:
// 0: Scope type
// 1: Scope object
-static MaybeObject* Runtime_GetScopeDetails(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetScopeDetails) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
// Check arguments.
Object* check;
- { MaybeObject* maybe_check = Runtime_CheckExecutionState(args, isolate);
+ { MaybeObject* maybe_check = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
if (!maybe_check->ToObject(&check)) return maybe_check;
}
CONVERT_CHECKED(Smi, wrapped_id, args[1]);
@@ -10265,7 +10083,7 @@
// Get the frame where the debugging is performed.
StackFrame::Id id = UnwrapFrameId(wrapped_id);
- JavaScriptFrameIterator frame_it(id);
+ JavaScriptFrameIterator frame_it(isolate, id);
JavaScriptFrame* frame = frame_it.frame();
// Find the requested scope.
@@ -10292,8 +10110,7 @@
}
-static MaybeObject* Runtime_DebugPrintScopes(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugPrintScopes) {
HandleScope scope(isolate);
ASSERT(args.length() == 0);
@@ -10309,14 +10126,14 @@
}
-static MaybeObject* Runtime_GetThreadCount(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetThreadCount) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
// Check arguments.
Object* result;
- { MaybeObject* maybe_result = Runtime_CheckExecutionState(args, isolate);
+ { MaybeObject* maybe_result = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
if (!maybe_result->ToObject(&result)) return maybe_result;
}
@@ -10345,14 +10162,14 @@
// The array returned contains the following information:
// 0: Is current thread?
// 1: Thread id
-static MaybeObject* Runtime_GetThreadDetails(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetThreadDetails) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
// Check arguments.
Object* check;
- { MaybeObject* maybe_check = Runtime_CheckExecutionState(args, isolate);
+ { MaybeObject* maybe_check = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
if (!maybe_check->ToObject(&check)) return maybe_check;
}
CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
@@ -10367,8 +10184,7 @@
details->set(kThreadDetailsCurrentThreadIndex,
isolate->heap()->true_value());
details->set(kThreadDetailsThreadIdIndex,
- Smi::FromInt(
- isolate->thread_manager()->CurrentId()));
+ Smi::FromInt(ThreadId::Current().ToInteger()));
} else {
// Find the thread with the requested index.
int n = 1;
@@ -10385,7 +10201,8 @@
// Fill the details.
details->set(kThreadDetailsCurrentThreadIndex,
isolate->heap()->false_value());
- details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
+ details->set(kThreadDetailsThreadIdIndex,
+ Smi::FromInt(thread->id().ToInteger()));
}
// Convert to JS array and return.
@@ -10395,8 +10212,7 @@
// Sets the disable break state
// args[0]: disable break state
-static MaybeObject* Runtime_SetDisableBreak(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetDisableBreak) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_BOOLEAN_CHECKED(disable_break, args[0]);
@@ -10405,8 +10221,7 @@
}
-static MaybeObject* Runtime_GetBreakLocations(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetBreakLocations) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
@@ -10425,8 +10240,7 @@
// args[0]: function
// args[1]: number: break source position (within the function source)
// args[2]: number: break point object
-static MaybeObject* Runtime_SetFunctionBreakPoint(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetFunctionBreakPoint) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
CONVERT_ARG_CHECKED(JSFunction, fun, 0);
@@ -10527,8 +10341,7 @@
// args[0]: script to set break point in
// args[1]: number: break source position (within the script source)
// args[2]: number: break point object
-static MaybeObject* Runtime_SetScriptBreakPoint(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetScriptBreakPoint) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
@@ -10562,8 +10375,7 @@
// Clear a break point
// args[0]: number: break point object
-static MaybeObject* Runtime_ClearBreakPoint(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ClearBreakPoint) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
Handle<Object> break_point_object_arg = args.at<Object>(0);
@@ -10578,8 +10390,7 @@
// Change the state of break on exceptions.
// args[0]: Enum value indicating whether to affect caught/uncaught exceptions.
// args[1]: Boolean indicating on/off.
-static MaybeObject* Runtime_ChangeBreakOnException(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ChangeBreakOnException) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
RUNTIME_ASSERT(args[0]->IsNumber());
@@ -10597,8 +10408,7 @@
// Returns the state of break on exceptions
// args[0]: boolean indicating uncaught exceptions
-static MaybeObject* Runtime_IsBreakOnException(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsBreakOnException) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
RUNTIME_ASSERT(args[0]->IsNumber());
@@ -10615,13 +10425,13 @@
// args[1]: step action from the enumeration StepAction
// args[2]: number of times to perform the step, for step out it is the number
// of frames to step down.
-static MaybeObject* Runtime_PrepareStep(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_PrepareStep) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
// Check arguments.
Object* check;
- { MaybeObject* maybe_check = Runtime_CheckExecutionState(args, isolate);
+ { MaybeObject* maybe_check = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
if (!maybe_check->ToObject(&check)) return maybe_check;
}
if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
@@ -10655,8 +10465,7 @@
// Clear all stepping set by PrepareStep.
-static MaybeObject* Runtime_ClearStepping(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ClearStepping) {
HandleScope scope(isolate);
ASSERT(args.length() == 0);
isolate->debug()->ClearStepping();
@@ -10676,7 +10485,7 @@
// Recursively copy the with contexts.
Handle<Context> previous(context_chain->previous());
Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
- Handle<Context> context = CopyWithContextChain(function_context, previous);
+ Handle<Context> context = CopyWithContextChain(previous, function_context);
return context->GetIsolate()->factory()->NewWithContext(
context, extension, context_chain->IsCatchContext());
}
@@ -10739,16 +10548,15 @@
// stack frame presenting the same view of the values of parameters and
// local variables as if the piece of JavaScript was evaluated at the point
// where the function on the stack frame is currently stopped.
-static MaybeObject* Runtime_DebugEvaluate(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) {
HandleScope scope(isolate);
// Check the execution state and decode arguments frame and source to be
// evaluated.
ASSERT(args.length() == 5);
Object* check_result;
- { MaybeObject* maybe_check_result = Runtime_CheckExecutionState(args,
- isolate);
+ { MaybeObject* maybe_check_result = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
if (!maybe_check_result->ToObject(&check_result)) {
return maybe_check_result;
}
@@ -10763,7 +10571,7 @@
// Get the frame where the debugging is performed.
StackFrame::Id id = UnwrapFrameId(wrapped_id);
- JavaScriptFrameIterator it(id);
+ JavaScriptFrameIterator it(isolate, id);
JavaScriptFrame* frame = it.frame();
Handle<JSFunction> function(JSFunction::cast(frame->function()));
Handle<SerializedScopeInfo> scope_info(function->shared()->scope_info());
@@ -10867,16 +10675,15 @@
}
-static MaybeObject* Runtime_DebugEvaluateGlobal(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluateGlobal) {
HandleScope scope(isolate);
// Check the execution state and decode arguments frame and source to be
// evaluated.
ASSERT(args.length() == 4);
Object* check_result;
- { MaybeObject* maybe_check_result = Runtime_CheckExecutionState(args,
- isolate);
+ { MaybeObject* maybe_check_result = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
if (!maybe_check_result->ToObject(&check_result)) {
return maybe_check_result;
}
@@ -10939,8 +10746,7 @@
}
-static MaybeObject* Runtime_DebugGetLoadedScripts(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugGetLoadedScripts) {
HandleScope scope(isolate);
ASSERT(args.length() == 0);
@@ -11041,8 +10847,7 @@
// args[0]: the object to find references to
// args[1]: constructor function for instances to exclude (Mirror)
// args[2]: the the maximum number of objects to return
-static MaybeObject* Runtime_DebugReferencedBy(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugReferencedBy) {
ASSERT(args.length() == 3);
// First perform a full GC in order to avoid references from dead objects.
@@ -11122,8 +10927,7 @@
// Scan the heap for objects constructed by a specific function.
// args[0]: the constructor to find instances of
// args[1]: the the maximum number of objects to return
-static MaybeObject* Runtime_DebugConstructedBy(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugConstructedBy) {
ASSERT(args.length() == 2);
// First perform a full GC in order to avoid dead objects.
@@ -11161,8 +10965,7 @@
// Find the effective prototype object as returned by __proto__.
// args[0]: the object to find the prototype for.
-static MaybeObject* Runtime_DebugGetPrototype(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugGetPrototype) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(JSObject, obj, args[0]);
@@ -11172,17 +10975,14 @@
}
-static MaybeObject* Runtime_SystemBreak(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SystemBreak) {
ASSERT(args.length() == 0);
CPU::DebugBreak();
return isolate->heap()->undefined_value();
}
-static MaybeObject* Runtime_DebugDisassembleFunction(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugDisassembleFunction) {
#ifdef DEBUG
HandleScope scope(isolate);
ASSERT(args.length() == 1);
@@ -11198,9 +10998,7 @@
}
-static MaybeObject* Runtime_DebugDisassembleConstructor(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugDisassembleConstructor) {
#ifdef DEBUG
HandleScope scope(isolate);
ASSERT(args.length() == 1);
@@ -11216,9 +11014,7 @@
}
-static MaybeObject* Runtime_FunctionGetInferredName(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetInferredName) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -11254,9 +11050,8 @@
// For a script finds all SharedFunctionInfo's in the heap that points
// to this script. Returns JSArray of SharedFunctionInfo wrapped
// in OpaqueReferences.
-static MaybeObject* Runtime_LiveEditFindSharedFunctionInfosForScript(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*,
+ Runtime_LiveEditFindSharedFunctionInfosForScript) {
ASSERT(args.length() == 1);
HandleScope scope(isolate);
CONVERT_CHECKED(JSValue, script_value, args[0]);
@@ -11288,9 +11083,7 @@
// Returns a JSArray of compilation infos. The array is ordered so that
// each function with all its descendant is always stored in a continues range
// with the function itself going first. The root function is a script function.
-static MaybeObject* Runtime_LiveEditGatherCompileInfo(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LiveEditGatherCompileInfo) {
ASSERT(args.length() == 2);
HandleScope scope(isolate);
CONVERT_CHECKED(JSValue, script, args[0]);
@@ -11309,8 +11102,7 @@
// Changes the source of the script to a new_source.
// If old_script_name is provided (i.e. is a String), also creates a copy of
// the script with its original source and sends notification to debugger.
-static MaybeObject* Runtime_LiveEditReplaceScript(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LiveEditReplaceScript) {
ASSERT(args.length() == 3);
HandleScope scope(isolate);
CONVERT_CHECKED(JSValue, original_script_value, args[0]);
@@ -11334,9 +11126,7 @@
}
-static MaybeObject* Runtime_LiveEditFunctionSourceUpdated(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LiveEditFunctionSourceUpdated) {
ASSERT(args.length() == 1);
HandleScope scope(isolate);
CONVERT_ARG_CHECKED(JSArray, shared_info, 0);
@@ -11345,9 +11135,7 @@
// Replaces code of SharedFunctionInfo with a new one.
-static MaybeObject* Runtime_LiveEditReplaceFunctionCode(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LiveEditReplaceFunctionCode) {
ASSERT(args.length() == 2);
HandleScope scope(isolate);
CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
@@ -11357,9 +11145,7 @@
}
// Connects SharedFunctionInfo to another script.
-static MaybeObject* Runtime_LiveEditFunctionSetScript(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LiveEditFunctionSetScript) {
ASSERT(args.length() == 2);
HandleScope scope(isolate);
Handle<Object> function_object(args[0], isolate);
@@ -11384,9 +11170,7 @@
// In a code of a parent function replaces original function as embedded object
// with a substitution one.
-static MaybeObject* Runtime_LiveEditReplaceRefToNestedFunction(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LiveEditReplaceRefToNestedFunction) {
ASSERT(args.length() == 3);
HandleScope scope(isolate);
@@ -11406,9 +11190,7 @@
// array of groups of 3 numbers:
// (change_begin, change_end, change_end_new_position).
// Each group describes a change in text; groups are sorted by change_begin.
-static MaybeObject* Runtime_LiveEditPatchFunctionPositions(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LiveEditPatchFunctionPositions) {
ASSERT(args.length() == 2);
HandleScope scope(isolate);
CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
@@ -11422,9 +11204,7 @@
// checks that none of them have activations on stacks (of any thread).
// Returns array of the same length with corresponding results of
// LiveEdit::FunctionPatchabilityStatus type.
-static MaybeObject* Runtime_LiveEditCheckAndDropActivations(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LiveEditCheckAndDropActivations) {
ASSERT(args.length() == 2);
HandleScope scope(isolate);
CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
@@ -11436,8 +11216,7 @@
// Compares 2 strings line-by-line, then token-wise and returns diff in form
// of JSArray of triplets (pos1, pos1_end, pos2_end) describing list
// of diff chunks.
-static MaybeObject* Runtime_LiveEditCompareStrings(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LiveEditCompareStrings) {
ASSERT(args.length() == 2);
HandleScope scope(isolate);
CONVERT_ARG_CHECKED(String, s1, 0);
@@ -11449,9 +11228,7 @@
// A testing entry. Returns statement position which is the closest to
// source_position.
-static MaybeObject* Runtime_GetFunctionCodePositionFromSource(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFunctionCodePositionFromSource) {
ASSERT(args.length() == 2);
HandleScope scope(isolate);
CONVERT_ARG_CHECKED(JSFunction, function, 0);
@@ -11488,8 +11265,7 @@
// Calls specified function with or without entering the debugger.
// This is used in unit tests to run code as if debugger is entered or simply
// to have a stack with C++ frame in the middle.
-static MaybeObject* Runtime_ExecuteInDebugContext(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ExecuteInDebugContext) {
ASSERT(args.length() == 2);
HandleScope scope(isolate);
CONVERT_ARG_CHECKED(JSFunction, function, 0);
@@ -11516,8 +11292,7 @@
// Sets a v8 flag.
-static MaybeObject* Runtime_SetFlags(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetFlags) {
CONVERT_CHECKED(String, arg, args[0]);
SmartPointer<char> flags =
arg->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
@@ -11528,16 +11303,14 @@
// Performs a GC.
// Presently, it only does a full GC.
-static MaybeObject* Runtime_CollectGarbage(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CollectGarbage) {
isolate->heap()->CollectAllGarbage(true);
return isolate->heap()->undefined_value();
}
// Gets the current heap usage.
-static MaybeObject* Runtime_GetHeapUsage(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetHeapUsage) {
int usage = static_cast<int>(isolate->heap()->SizeOfObjects());
if (!Smi::IsValid(usage)) {
return *isolate->factory()->NewNumberFromInt(usage);
@@ -11547,8 +11320,7 @@
// Captures a live object list from the present heap.
-static MaybeObject* Runtime_HasLOLEnabled(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_HasLOLEnabled) {
#ifdef LIVE_OBJECT_LIST
return isolate->heap()->true_value();
#else
@@ -11558,8 +11330,7 @@
// Captures a live object list from the present heap.
-static MaybeObject* Runtime_CaptureLOL(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CaptureLOL) {
#ifdef LIVE_OBJECT_LIST
return LiveObjectList::Capture();
#else
@@ -11569,8 +11340,7 @@
// Deletes the specified live object list.
-static MaybeObject* Runtime_DeleteLOL(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DeleteLOL) {
#ifdef LIVE_OBJECT_LIST
CONVERT_SMI_CHECKED(id, args[0]);
bool success = LiveObjectList::Delete(id);
@@ -11587,8 +11357,7 @@
// specified by id1 and id2.
// If id1 is 0 (i.e. not a valid lol), then the whole of lol id2 will be
// dumped.
-static MaybeObject* Runtime_DumpLOL(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DumpLOL) {
#ifdef LIVE_OBJECT_LIST
HandleScope scope;
CONVERT_SMI_CHECKED(id1, args[0]);
@@ -11606,8 +11375,7 @@
// Gets the specified object as requested by the debugger.
// This is only used for obj ids shown in live object lists.
-static MaybeObject* Runtime_GetLOLObj(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLOLObj) {
#ifdef LIVE_OBJECT_LIST
CONVERT_SMI_CHECKED(obj_id, args[0]);
Object* result = LiveObjectList::GetObj(obj_id);
@@ -11620,8 +11388,7 @@
// Gets the obj id for the specified address if valid.
// This is only used for obj ids shown in live object lists.
-static MaybeObject* Runtime_GetLOLObjId(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLOLObjId) {
#ifdef LIVE_OBJECT_LIST
HandleScope scope;
CONVERT_ARG_CHECKED(String, address, 0);
@@ -11634,8 +11401,7 @@
// Gets the retainers that references the specified object alive.
-static MaybeObject* Runtime_GetLOLObjRetainers(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLOLObjRetainers) {
#ifdef LIVE_OBJECT_LIST
HandleScope scope;
CONVERT_SMI_CHECKED(obj_id, args[0]);
@@ -11675,8 +11441,7 @@
// Gets the reference path between 2 objects.
-static MaybeObject* Runtime_GetLOLPath(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLOLPath) {
#ifdef LIVE_OBJECT_LIST
HandleScope scope;
CONVERT_SMI_CHECKED(obj_id1, args[0]);
@@ -11699,8 +11464,7 @@
// Generates the response to a debugger request for a list of all
// previously captured live object lists.
-static MaybeObject* Runtime_InfoLOL(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_InfoLOL) {
#ifdef LIVE_OBJECT_LIST
CONVERT_SMI_CHECKED(start, args[0]);
CONVERT_SMI_CHECKED(count, args[1]);
@@ -11713,8 +11477,7 @@
// Gets a dump of the specified object as requested by the debugger.
// This is only used for obj ids shown in live object lists.
-static MaybeObject* Runtime_PrintLOLObj(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_PrintLOLObj) {
#ifdef LIVE_OBJECT_LIST
HandleScope scope;
CONVERT_SMI_CHECKED(obj_id, args[0]);
@@ -11727,8 +11490,7 @@
// Resets and releases all previously captured live object lists.
-static MaybeObject* Runtime_ResetLOL(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ResetLOL) {
#ifdef LIVE_OBJECT_LIST
LiveObjectList::Reset();
return isolate->heap()->undefined_value();
@@ -11743,8 +11505,7 @@
// specified by id1 and id2.
// If id1 is 0 (i.e. not a valid lol), then the whole of lol id2 will be
// summarized.
-static MaybeObject* Runtime_SummarizeLOL(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SummarizeLOL) {
#ifdef LIVE_OBJECT_LIST
HandleScope scope;
CONVERT_SMI_CHECKED(id1, args[0]);
@@ -11762,8 +11523,7 @@
#ifdef ENABLE_LOGGING_AND_PROFILING
-static MaybeObject* Runtime_ProfilerResume(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ProfilerResume) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -11774,8 +11534,7 @@
}
-static MaybeObject* Runtime_ProfilerPause(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ProfilerPause) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -11822,8 +11581,7 @@
// Get the script object from script data. NOTE: Regarding performance
// see the NOTE for GetScriptFromScriptData.
// args[0]: script data for the script to find the source for
-static MaybeObject* Runtime_GetScript(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetScript) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
@@ -11868,8 +11626,7 @@
// Collect the raw data for a stack trace. Returns an array of 4
// element segments each containing a receiver, function, code and
// native code offset.
-static MaybeObject* Runtime_CollectStackTrace(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CollectStackTrace) {
ASSERT_EQ(args.length(), 2);
Handle<Object> caller = args.at<Object>(0);
CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
@@ -11882,7 +11639,7 @@
Handle<FixedArray> elements =
factory->NewFixedArrayWithHoles(initial_size * 4);
- StackFrameIterator iter;
+ StackFrameIterator iter(isolate);
// If the caller parameter is a function we skip frames until we're
// under it before starting to collect.
bool seen_caller = !caller->IsJSFunction();
@@ -11893,7 +11650,9 @@
if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
frames_seen++;
JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
- List<FrameSummary> frames(3); // Max 2 levels of inlining.
+ // Set initial size to the maximum inlining level + 1 for the outermost
+ // function.
+ List<FrameSummary> frames(Compiler::kMaxInliningLevels + 1);
frame->Summarize(&frames);
for (int i = frames.length() - 1; i >= 0; i--) {
if (cursor + 4 > elements->length()) {
@@ -11926,8 +11685,7 @@
// Returns V8 version as a string.
-static MaybeObject* Runtime_GetV8Version(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetV8Version) {
ASSERT_EQ(args.length(), 0);
NoHandleAllocation ha;
@@ -11939,8 +11697,7 @@
}
-static MaybeObject* Runtime_Abort(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Abort) {
ASSERT(args.length() == 2);
OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
Smi::cast(args[1])->value());
@@ -11951,8 +11708,7 @@
}
-static MaybeObject* Runtime_GetFromCache(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFromCache) {
// This is only called from codegen, so checks might be more lax.
CONVERT_CHECKED(JSFunctionResultCache, cache, args[0]);
Object* key = args[1];
@@ -12044,8 +11800,7 @@
}
-static MaybeObject* Runtime_NewMessageObject(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NewMessageObject) {
HandleScope scope(isolate);
CONVERT_ARG_CHECKED(String, type, 0);
CONVERT_ARG_CHECKED(JSArray, arguments, 1);
@@ -12060,30 +11815,25 @@
}
-static MaybeObject* Runtime_MessageGetType(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MessageGetType) {
CONVERT_CHECKED(JSMessageObject, message, args[0]);
return message->type();
}
-static MaybeObject* Runtime_MessageGetArguments(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MessageGetArguments) {
CONVERT_CHECKED(JSMessageObject, message, args[0]);
return message->arguments();
}
-static MaybeObject* Runtime_MessageGetStartPosition(
- RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MessageGetStartPosition) {
CONVERT_CHECKED(JSMessageObject, message, args[0]);
return Smi::FromInt(message->start_position());
}
-static MaybeObject* Runtime_MessageGetScript(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MessageGetScript) {
CONVERT_CHECKED(JSMessageObject, message, args[0]);
return message->script();
}
@@ -12092,8 +11842,7 @@
#ifdef DEBUG
// ListNatives is ONLY used by the fuzz-natives.js in debug mode
// Exclude the code in release mode.
-static MaybeObject* Runtime_ListNatives(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ListNatives) {
ASSERT(args.length() == 0);
HandleScope scope;
#define COUNT_ENTRY(Name, argc, ressize) + 1
@@ -12137,8 +11886,7 @@
#endif
-static MaybeObject* Runtime_Log(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Log) {
ASSERT(args.length() == 2);
CONVERT_CHECKED(String, format, args[0]);
CONVERT_CHECKED(JSArray, elms, args[1]);
@@ -12148,7 +11896,7 @@
}
-static MaybeObject* Runtime_IS_VAR(RUNTIME_CALLING_CONVENTION) {
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IS_VAR) {
UNREACHABLE(); // implemented as macro in the parser
return NULL;
}
diff --git a/src/runtime.h b/src/runtime.h
index 58062ca..bf1ba68 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -84,7 +84,8 @@
F(LazyRecompile, 1, 1) \
F(NotifyDeoptimized, 1, 1) \
F(NotifyOSR, 0, 1) \
- F(DeoptimizeFunction, 1, 1) \
+ F(DeoptimizeFunction, 1, 1) \
+ F(OptimizeFunctionOnNextCall, 1, 1) \
F(CompileForOnStackReplacement, 1, 1) \
F(SetNewFunctionAttributes, 1, 1) \
F(AllocateInNewSpace, 1, 1) \
diff --git a/src/scanner-base.cc b/src/scanner-base.cc
index 2066b5a..9715ca9 100644
--- a/src/scanner-base.cc
+++ b/src/scanner-base.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -35,29 +35,11 @@
namespace internal {
// ----------------------------------------------------------------------------
-// Compound predicates.
-
-bool ScannerConstants::IsIdentifier(unibrow::CharacterStream* buffer) {
- // Checks whether the buffer contains an identifier (no escape).
- if (!buffer->has_more()) return false;
- if (!kIsIdentifierStart.get(buffer->GetNext())) {
- return false;
- }
- while (buffer->has_more()) {
- if (!kIsIdentifierPart.get(buffer->GetNext())) {
- return false;
- }
- }
- return true;
-}
-
-// ----------------------------------------------------------------------------
// Scanner
-Scanner::Scanner(ScannerConstants* scanner_constants)
- : scanner_constants_(scanner_constants),
- octal_pos_(kNoOctalLocation) {
-}
+Scanner::Scanner(UnicodeCache* unicode_cache)
+ : unicode_cache_(unicode_cache),
+ octal_pos_(kNoOctalLocation) { }
uc32 Scanner::ScanHexEscape(uc32 c, int length) {
@@ -114,7 +96,7 @@
// ----------------------------------------------------------------------------
// JavaScriptScanner
-JavaScriptScanner::JavaScriptScanner(ScannerConstants* scanner_contants)
+JavaScriptScanner::JavaScriptScanner(UnicodeCache* scanner_contants)
: Scanner(scanner_contants) { }
@@ -144,9 +126,9 @@
while (true) {
// We treat byte-order marks (BOMs) as whitespace for better
// compatibility with Spidermonkey and other JavaScript engines.
- while (scanner_constants_->IsWhiteSpace(c0_) || IsByteOrderMark(c0_)) {
+ while (unicode_cache_->IsWhiteSpace(c0_) || IsByteOrderMark(c0_)) {
// IsWhiteSpace() includes line terminators!
- if (scanner_constants_->IsLineTerminator(c0_)) {
+ if (unicode_cache_->IsLineTerminator(c0_)) {
// Ignore line terminators, but remember them. This is necessary
// for automatic semicolon insertion.
has_line_terminator_before_next_ = true;
@@ -186,7 +168,7 @@
// separately by the lexical grammar and becomes part of the
// stream of input elements for the syntactic grammar (see
// ECMA-262, section 7.4, page 12).
- while (c0_ >= 0 && !scanner_constants_->IsLineTerminator(c0_)) {
+ while (c0_ >= 0 && !unicode_cache_->IsLineTerminator(c0_)) {
Advance();
}
@@ -451,7 +433,7 @@
break;
default:
- if (scanner_constants_->IsIdentifierStart(c0_)) {
+ if (unicode_cache_->IsIdentifierStart(c0_)) {
token = ScanIdentifierOrKeyword();
} else if (IsDecimalDigit(c0_)) {
token = ScanNumber(false);
@@ -499,7 +481,7 @@
Advance();
// Skip escaped newlines.
- if (scanner_constants_->IsLineTerminator(c)) {
+ if (unicode_cache_->IsLineTerminator(c)) {
// Allow CR+LF newlines in multiline string literals.
if (IsCarriageReturn(c) && IsLineFeed(c0_)) Advance();
// Allow LF+CR newlines in multiline string literals.
@@ -542,7 +524,7 @@
LiteralScope literal(this);
while (c0_ != quote && c0_ >= 0
- && !scanner_constants_->IsLineTerminator(c0_)) {
+ && !unicode_cache_->IsLineTerminator(c0_)) {
uc32 c = c0_;
Advance();
if (c == '\\') {
@@ -641,7 +623,7 @@
// not be an identifier start or a decimal digit; see ECMA-262
// section 7.8.3, page 17 (note that we read only one decimal digit
// if the value is 0).
- if (IsDecimalDigit(c0_) || scanner_constants_->IsIdentifierStart(c0_))
+ if (IsDecimalDigit(c0_) || unicode_cache_->IsIdentifierStart(c0_))
return Token::ILLEGAL;
literal.Complete();
@@ -663,14 +645,14 @@
Token::Value JavaScriptScanner::ScanIdentifierOrKeyword() {
- ASSERT(scanner_constants_->IsIdentifierStart(c0_));
+ ASSERT(unicode_cache_->IsIdentifierStart(c0_));
LiteralScope literal(this);
KeywordMatcher keyword_match;
// Scan identifier start character.
if (c0_ == '\\') {
uc32 c = ScanIdentifierUnicodeEscape();
// Only allow legal identifier start characters.
- if (!scanner_constants_->IsIdentifierStart(c)) return Token::ILLEGAL;
+ if (!unicode_cache_->IsIdentifierStart(c)) return Token::ILLEGAL;
AddLiteralChar(c);
return ScanIdentifierSuffix(&literal);
}
@@ -683,7 +665,7 @@
}
// Scan the rest of the identifier characters.
- while (scanner_constants_->IsIdentifierPart(c0_)) {
+ while (unicode_cache_->IsIdentifierPart(c0_)) {
if (c0_ != '\\') {
uc32 next_char = c0_;
Advance();
@@ -701,11 +683,11 @@
Token::Value JavaScriptScanner::ScanIdentifierSuffix(LiteralScope* literal) {
// Scan the rest of the identifier characters.
- while (scanner_constants_->IsIdentifierPart(c0_)) {
+ while (unicode_cache_->IsIdentifierPart(c0_)) {
if (c0_ == '\\') {
uc32 c = ScanIdentifierUnicodeEscape();
// Only allow legal identifier part characters.
- if (!scanner_constants_->IsIdentifierPart(c)) return Token::ILLEGAL;
+ if (!unicode_cache_->IsIdentifierPart(c)) return Token::ILLEGAL;
AddLiteralChar(c);
} else {
AddLiteralChar(c0_);
@@ -735,10 +717,10 @@
AddLiteralChar('=');
while (c0_ != '/' || in_character_class) {
- if (scanner_constants_->IsLineTerminator(c0_) || c0_ < 0) return false;
+ if (unicode_cache_->IsLineTerminator(c0_) || c0_ < 0) return false;
if (c0_ == '\\') { // Escape sequence.
AddLiteralCharAdvance();
- if (scanner_constants_->IsLineTerminator(c0_) || c0_ < 0) return false;
+ if (unicode_cache_->IsLineTerminator(c0_) || c0_ < 0) return false;
AddLiteralCharAdvance();
// If the escape allows more characters, i.e., \x??, \u????, or \c?,
// only "safe" characters are allowed (letters, digits, underscore),
@@ -764,7 +746,7 @@
bool JavaScriptScanner::ScanRegExpFlags() {
// Scan regular expression flags.
LiteralScope literal(this);
- while (scanner_constants_->IsIdentifierPart(c0_)) {
+ while (unicode_cache_->IsIdentifierPart(c0_)) {
if (c0_ == '\\') {
uc32 c = ScanIdentifierUnicodeEscape();
if (c != static_cast<uc32>(unibrow::Utf8::kBadChar)) {
diff --git a/src/scanner-base.h b/src/scanner-base.h
index 552f387..60b97d2 100644
--- a/src/scanner-base.h
+++ b/src/scanner-base.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -119,11 +119,11 @@
};
-class ScannerConstants {
+class UnicodeCache {
// ---------------------------------------------------------------------
-// Constants used by scanners.
+// Caching predicates used by scanners.
public:
- ScannerConstants() {}
+ UnicodeCache() {}
typedef unibrow::Utf8InputBuffer<1024> Utf8Decoder;
StaticResource<Utf8Decoder>* utf8_decoder() {
@@ -135,8 +135,6 @@
bool IsLineTerminator(unibrow::uchar c) { return kIsLineTerminator.get(c); }
bool IsWhiteSpace(unibrow::uchar c) { return kIsWhiteSpace.get(c); }
- bool IsIdentifier(unibrow::CharacterStream* buffer);
-
private:
unibrow::Predicate<IdentifierStart, 128> kIsIdentifierStart;
@@ -145,9 +143,10 @@
unibrow::Predicate<unibrow::WhiteSpace, 128> kIsWhiteSpace;
StaticResource<Utf8Decoder> utf8_decoder_;
- DISALLOW_COPY_AND_ASSIGN(ScannerConstants);
+ DISALLOW_COPY_AND_ASSIGN(UnicodeCache);
};
+
// ----------------------------------------------------------------------------
// LiteralBuffer - Collector of chars of literals.
@@ -272,7 +271,7 @@
bool complete_;
};
- explicit Scanner(ScannerConstants* scanner_contants);
+ explicit Scanner(UnicodeCache* scanner_contants);
// Returns the current token again.
Token::Value current_token() { return current_.token; }
@@ -427,7 +426,7 @@
return source_->pos() - kCharacterLookaheadBufferSize;
}
- ScannerConstants* scanner_constants_;
+ UnicodeCache* unicode_cache_;
// Buffers collecting literal strings, numbers, etc.
LiteralBuffer literal_buffer1_;
@@ -473,7 +472,7 @@
bool complete_;
};
- explicit JavaScriptScanner(ScannerConstants* scanner_contants);
+ explicit JavaScriptScanner(UnicodeCache* scanner_contants);
// Returns the next token.
Token::Value Next();
diff --git a/src/scanner.cc b/src/scanner.cc
index d9c2188..666818e 100755
--- a/src/scanner.cc
+++ b/src/scanner.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -345,8 +345,8 @@
// ----------------------------------------------------------------------------
// JsonScanner
-JsonScanner::JsonScanner(ScannerConstants* scanner_constants)
- : Scanner(scanner_constants) { }
+JsonScanner::JsonScanner(UnicodeCache* unicode_cache)
+ : Scanner(unicode_cache) { }
void JsonScanner::Initialize(UC16CharacterStream* source) {
@@ -560,7 +560,8 @@
}
literal.Complete();
ASSERT_NOT_NULL(next_.literal_chars);
- number_ = StringToDouble(next_.literal_chars->ascii_literal(),
+ number_ = StringToDouble(unicode_cache_,
+ next_.literal_chars->ascii_literal(),
NO_FLAGS, // Hex, octal or trailing junk.
OS::nan_value());
return Token::NUMBER;
@@ -575,7 +576,7 @@
Advance();
text++;
}
- if (scanner_constants_->IsIdentifierPart(c0_)) return Token::ILLEGAL;
+ if (unicode_cache_->IsIdentifierPart(c0_)) return Token::ILLEGAL;
literal.Complete();
return token;
}
diff --git a/src/scanner.h b/src/scanner.h
index 776ba53..871c69b 100644
--- a/src/scanner.h
+++ b/src/scanner.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -134,8 +134,8 @@
class V8JavaScriptScanner : public JavaScriptScanner {
public:
- explicit V8JavaScriptScanner(ScannerConstants* scanner_constants)
- : JavaScriptScanner(scanner_constants) {}
+ explicit V8JavaScriptScanner(UnicodeCache* unicode_cache)
+ : JavaScriptScanner(unicode_cache) {}
void Initialize(UC16CharacterStream* source);
};
@@ -143,7 +143,7 @@
class JsonScanner : public Scanner {
public:
- explicit JsonScanner(ScannerConstants* scanner_constants);
+ explicit JsonScanner(UnicodeCache* unicode_cache);
void Initialize(UC16CharacterStream* source);
diff --git a/src/scopeinfo.h b/src/scopeinfo.h
index cc9f816..2552af2 100644
--- a/src/scopeinfo.h
+++ b/src/scopeinfo.h
@@ -220,7 +220,7 @@
ASSERT(index == this->index());
}
- inline Value(uint32_t value) : value_(value) {}
+ explicit inline Value(uint32_t value) : value_(value) {}
uint32_t raw() { return value_; }
diff --git a/src/scopes.cc b/src/scopes.cc
index f4bcaa8..7d9bce5 100644
--- a/src/scopes.cc
+++ b/src/scopes.cc
@@ -120,7 +120,7 @@
params_(0),
unresolved_(0),
decls_(0) {
- SetDefaults(type, NULL, NULL);
+ SetDefaults(type, NULL, Handle<SerializedScopeInfo>::null());
ASSERT(!resolved());
}
@@ -132,7 +132,7 @@
params_(4),
unresolved_(16),
decls_(4) {
- SetDefaults(type, outer_scope, NULL);
+ SetDefaults(type, outer_scope, Handle<SerializedScopeInfo>::null());
// At some point we might want to provide outer scopes to
// eval scopes (by walking the stack and reading the scope info).
// In that case, the ASSERT below needs to be adjusted.
@@ -142,14 +142,14 @@
}
-Scope::Scope(Scope* inner_scope, SerializedScopeInfo* scope_info)
+Scope::Scope(Scope* inner_scope, Handle<SerializedScopeInfo> scope_info)
: inner_scopes_(4),
variables_(),
temps_(4),
params_(4),
unresolved_(16),
decls_(4) {
- ASSERT(scope_info != NULL);
+ ASSERT(!scope_info.is_null());
SetDefaults(FUNCTION_SCOPE, NULL, scope_info);
ASSERT(resolved());
if (scope_info->HasHeapAllocatedLocals()) {
@@ -181,6 +181,33 @@
}
+void Scope::SetDefaults(Type type,
+ Scope* outer_scope,
+ Handle<SerializedScopeInfo> scope_info) {
+ outer_scope_ = outer_scope;
+ type_ = type;
+ scope_name_ = FACTORY->empty_symbol();
+ dynamics_ = NULL;
+ receiver_ = NULL;
+ function_ = NULL;
+ arguments_ = NULL;
+ arguments_shadow_ = NULL;
+ illegal_redecl_ = NULL;
+ scope_inside_with_ = false;
+ scope_contains_with_ = false;
+ scope_calls_eval_ = false;
+ // Inherit the strict mode from the parent scope.
+ strict_mode_ = (outer_scope != NULL) && outer_scope->strict_mode_;
+ outer_scope_calls_eval_ = false;
+ inner_scope_calls_eval_ = false;
+ outer_scope_is_eval_scope_ = false;
+ force_eager_compilation_ = false;
+ num_stack_slots_ = 0;
+ num_heap_slots_ = 0;
+ scope_info_ = scope_info;
+}
+
+
Scope* Scope::DeserializeScopeChain(CompilationInfo* info,
Scope* global_scope) {
ASSERT(!info->closure().is_null());
@@ -193,8 +220,8 @@
JSFunction* current = *info->closure();
do {
current = current->context()->closure();
- SerializedScopeInfo* scope_info = current->shared()->scope_info();
- if (scope_info != SerializedScopeInfo::Empty()) {
+ Handle<SerializedScopeInfo> scope_info(current->shared()->scope_info());
+ if (*scope_info != SerializedScopeInfo::Empty()) {
scope = new Scope(scope, scope_info);
if (innermost_scope == NULL) innermost_scope = scope;
} else {
@@ -361,12 +388,14 @@
}
-VariableProxy* Scope::NewUnresolved(Handle<String> name, bool inside_with) {
+VariableProxy* Scope::NewUnresolved(Handle<String> name,
+ bool inside_with,
+ int position) {
// Note that we must not share the unresolved variables with
// the same name because they may be removed selectively via
// RemoveUnresolved().
ASSERT(!resolved());
- VariableProxy* proxy = new VariableProxy(name, false, inside_with);
+ VariableProxy* proxy = new VariableProxy(name, false, inside_with, position);
unresolved_.Add(proxy);
return proxy;
}
diff --git a/src/scopes.h b/src/scopes.h
index 24622b4..18db0cd 100644
--- a/src/scopes.h
+++ b/src/scopes.h
@@ -149,7 +149,9 @@
void AddParameter(Variable* var);
// Create a new unresolved variable.
- virtual VariableProxy* NewUnresolved(Handle<String> name, bool inside_with);
+ virtual VariableProxy* NewUnresolved(Handle<String> name,
+ bool inside_with,
+ int position = RelocInfo::kNoPosition);
// Remove a unresolved variable. During parsing, an unresolved variable
// may have been added optimistically, but then only the variable name
@@ -376,8 +378,8 @@
int num_heap_slots_;
// Serialized scopes support.
- SerializedScopeInfo* scope_info_;
- bool resolved() { return scope_info_ != NULL; }
+ Handle<SerializedScopeInfo> scope_info_;
+ bool resolved() { return !scope_info_.is_null(); }
// Create a non-local variable with a given name.
// These variables are looked up dynamically at runtime.
@@ -412,7 +414,7 @@
void AllocateVariablesRecursively();
private:
- Scope(Scope* inner_scope, SerializedScopeInfo* scope_info);
+ Scope(Scope* inner_scope, Handle<SerializedScopeInfo> scope_info);
void AddInnerScope(Scope* inner_scope) {
if (inner_scope != NULL) {
@@ -423,29 +425,7 @@
void SetDefaults(Type type,
Scope* outer_scope,
- SerializedScopeInfo* scope_info) {
- outer_scope_ = outer_scope;
- type_ = type;
- scope_name_ = FACTORY->empty_symbol();
- dynamics_ = NULL;
- receiver_ = NULL;
- function_ = NULL;
- arguments_ = NULL;
- arguments_shadow_ = NULL;
- illegal_redecl_ = NULL;
- scope_inside_with_ = false;
- scope_contains_with_ = false;
- scope_calls_eval_ = false;
- // Inherit the strict mode from the parent scope.
- strict_mode_ = (outer_scope != NULL) && outer_scope->strict_mode_;
- outer_scope_calls_eval_ = false;
- inner_scope_calls_eval_ = false;
- outer_scope_is_eval_scope_ = false;
- force_eager_compilation_ = false;
- num_stack_slots_ = 0;
- num_heap_slots_ = 0;
- scope_info_ = scope_info;
- }
+ Handle<SerializedScopeInfo> scope_info);
};
@@ -479,7 +459,9 @@
virtual Variable* Lookup(Handle<String> name) { return NULL; }
- virtual VariableProxy* NewUnresolved(Handle<String> name, bool inside_with) {
+ virtual VariableProxy* NewUnresolved(Handle<String> name,
+ bool inside_with,
+ int position = RelocInfo::kNoPosition) {
return NULL;
}
diff --git a/src/spaces.cc b/src/spaces.cc
index 20700e1..674078c 100644
--- a/src/spaces.cc
+++ b/src/spaces.cc
@@ -1570,7 +1570,6 @@
CASE(KEYED_EXTERNAL_ARRAY_STORE_IC);
CASE(CALL_IC);
CASE(KEYED_CALL_IC);
- CASE(BINARY_OP_IC);
CASE(TYPE_RECORDING_BINARY_OP_IC);
CASE(COMPARE_IC);
}
@@ -3014,7 +3013,8 @@
}
// Free the chunk.
- heap()->mark_compact_collector()->ReportDeleteIfNeeded(object);
+ heap()->mark_compact_collector()->ReportDeleteIfNeeded(
+ object, heap()->isolate());
LiveObjectList::ProcessNonLive(object);
size_ -= static_cast<int>(chunk_size);
diff --git a/src/stub-cache.cc b/src/stub-cache.cc
index 435e71d..0c6a7f7 100644
--- a/src/stub-cache.cc
+++ b/src/stub-cache.cc
@@ -1278,8 +1278,7 @@
// StubCompiler implementation.
-MaybeObject* LoadCallbackProperty(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) {
ASSERT(args[0]->IsJSObject());
ASSERT(args[1]->IsJSObject());
AccessorInfo* callback = AccessorInfo::cast(args[3]);
@@ -1301,8 +1300,7 @@
}
-MaybeObject* StoreCallbackProperty(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) {
JSObject* recv = JSObject::cast(args[0]);
AccessorInfo* callback = AccessorInfo::cast(args[1]);
Address setter_address = v8::ToCData<Address>(callback->setter());
@@ -1335,8 +1333,7 @@
* Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't
* provide any value for the given name.
*/
-MaybeObject* LoadPropertyWithInterceptorOnly(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) {
Handle<String> name_handle = args.at<String>(0);
Handle<InterceptorInfo> interceptor_info = args.at<InterceptorInfo>(1);
ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2);
@@ -1435,8 +1432,7 @@
* Loads a property with an interceptor performing post interceptor
* lookup if interceptor failed.
*/
-MaybeObject* LoadPropertyWithInterceptorForLoad(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) {
PropertyAttributes attr = NONE;
Object* result;
{ MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr);
@@ -1449,8 +1445,7 @@
}
-MaybeObject* LoadPropertyWithInterceptorForCall(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) {
PropertyAttributes attr;
MaybeObject* result = LoadWithInterceptor(&args, &attr);
RETURN_IF_SCHEDULED_EXCEPTION(isolate);
@@ -1461,8 +1456,7 @@
}
-MaybeObject* StoreInterceptorProperty(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) {
ASSERT(args.length() == 4);
JSObject* recv = JSObject::cast(args[0]);
String* name = String::cast(args[1]);
@@ -1478,8 +1472,7 @@
}
-MaybeObject* KeyedLoadPropertyWithInterceptor(RUNTIME_CALLING_CONVENTION) {
- RUNTIME_GET_ISOLATE;
+RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) {
JSObject* receiver = JSObject::cast(args[0]);
ASSERT(Smi::cast(args[1])->value() >= 0);
uint32_t index = Smi::cast(args[1])->value();
diff --git a/src/stub-cache.h b/src/stub-cache.h
index 793f581..c5dcf36 100644
--- a/src/stub-cache.h
+++ b/src/stub-cache.h
@@ -392,23 +392,24 @@
// Support functions for IC stubs for callbacks.
-MaybeObject* LoadCallbackProperty(RUNTIME_CALLING_CONVENTION);
-MaybeObject* StoreCallbackProperty(RUNTIME_CALLING_CONVENTION);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty);
// Support functions for IC stubs for interceptors.
-MaybeObject* LoadPropertyWithInterceptorOnly(RUNTIME_CALLING_CONVENTION);
-MaybeObject* LoadPropertyWithInterceptorForLoad(RUNTIME_CALLING_CONVENTION);
-MaybeObject* LoadPropertyWithInterceptorForCall(RUNTIME_CALLING_CONVENTION);
-MaybeObject* StoreInterceptorProperty(RUNTIME_CALLING_CONVENTION);
-MaybeObject* CallInterceptorProperty(RUNTIME_CALLING_CONVENTION);
-MaybeObject* KeyedLoadPropertyWithInterceptor(RUNTIME_CALLING_CONVENTION);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, CallInterceptorProperty);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor);
// The stub compiler compiles stubs for the stub cache.
class StubCompiler BASE_EMBEDDED {
public:
- StubCompiler() : scope_(), masm_(NULL, 256), failure_(NULL) { }
+ StubCompiler()
+ : scope_(), masm_(Isolate::Current(), NULL, 256), failure_(NULL) { }
MUST_USE_RESULT MaybeObject* CompileCallInitialize(Code::Flags flags);
MUST_USE_RESULT MaybeObject* CompileCallPreMonomorphic(Code::Flags flags);
diff --git a/src/top.cc b/src/top.cc
index 4a2a00b..a8dba71 100644
--- a/src/top.cc
+++ b/src/top.cc
@@ -29,6 +29,7 @@
#include "api.h"
#include "bootstrapper.h"
+#include "compiler.h"
#include "debug.h"
#include "execution.h"
#include "messages.h"
@@ -37,26 +38,23 @@
#include "string-stream.h"
#include "vm-state-inl.h"
+
// TODO(isolates): move to isolate.cc. This stuff is kept here to
// simplify merging.
namespace v8 {
namespace internal {
-v8::TryCatch* ThreadLocalTop::TryCatchHandler() {
- return TRY_CATCH_FROM_ADDRESS(try_catch_handler_address());
+ThreadLocalTop::ThreadLocalTop() {
+ InitializeInternal();
}
-void ThreadLocalTop::Initialize() {
+void ThreadLocalTop::InitializeInternal() {
c_entry_fp_ = 0;
handler_ = 0;
#ifdef USE_SIMULATOR
-#ifdef V8_TARGET_ARCH_ARM
- simulator_ = Simulator::current(Isolate::Current());
-#elif V8_TARGET_ARCH_MIPS
- simulator_ = Simulator::current(Isolate::Current());
-#endif
+ simulator_ = NULL;
#endif
#ifdef ENABLE_LOGGING_AND_PROFILING
js_entry_sp_ = NULL;
@@ -67,8 +65,7 @@
#endif
try_catch_handler_address_ = NULL;
context_ = NULL;
- int id = Isolate::Current()->thread_manager()->CurrentId();
- thread_id_ = (id == 0) ? ThreadManager::kInvalidId : id;
+ thread_id_ = ThreadId::Invalid();
external_caught_exception_ = false;
failed_access_check_callback_ = NULL;
save_context_ = NULL;
@@ -76,6 +73,24 @@
}
+void ThreadLocalTop::Initialize() {
+ InitializeInternal();
+#ifdef USE_SIMULATOR
+#ifdef V8_TARGET_ARCH_ARM
+ simulator_ = Simulator::current(Isolate::Current());
+#elif V8_TARGET_ARCH_MIPS
+ simulator_ = Simulator::current(Isolate::Current());
+#endif
+#endif
+ thread_id_ = ThreadId::Current();
+}
+
+
+v8::TryCatch* ThreadLocalTop::TryCatchHandler() {
+ return TRY_CATCH_FROM_ADDRESS(try_catch_handler_address());
+}
+
+
Address Isolate::get_address_from_id(Isolate::AddressId id) {
return isolate_addresses_[id];
}
@@ -89,13 +104,13 @@
void Isolate::IterateThread(ThreadVisitor* v) {
- v->VisitThread(thread_local_top());
+ v->VisitThread(this, thread_local_top());
}
void Isolate::IterateThread(ThreadVisitor* v, char* t) {
ThreadLocalTop* thread = reinterpret_cast<ThreadLocalTop*>(t);
- v->VisitThread(thread);
+ v->VisitThread(this, thread);
}
@@ -125,7 +140,7 @@
}
// Iterate over pointers on native execution stack.
- for (StackFrameIterator it(thread); !it.done(); it.Advance()) {
+ for (StackFrameIterator it(this, thread); !it.done(); it.Advance()) {
it.frame()->Iterate(v);
}
}
@@ -204,12 +219,13 @@
Handle<String> constructor_key =
factory()->LookupAsciiSymbol("isConstructor");
- StackTraceFrameIterator it;
+ StackTraceFrameIterator it(this);
int frames_seen = 0;
while (!it.done() && (frames_seen < limit)) {
JavaScriptFrame* frame = it.frame();
-
- List<FrameSummary> frames(3); // Max 2 levels of inlining.
+ // Set initial size to the maximum inlining level + 1 for the outermost
+ // function.
+ List<FrameSummary> frames(Compiler::kMaxInliningLevels + 1);
frame->Summarize(&frames);
for (int i = frames.length() - 1; i >= 0 && frames_seen < limit; i--) {
// Create a JSObject to hold the information for the StackFrame.
@@ -530,19 +546,19 @@
// the message for stack overflow exceptions which is very likely to
// double fault with another stack overflow exception, we use a
// precomputed message.
- DoThrow(*exception, NULL, kStackOverflowMessage);
+ DoThrow(*exception, NULL);
return Failure::Exception();
}
Failure* Isolate::TerminateExecution() {
- DoThrow(heap_.termination_exception(), NULL, NULL);
+ DoThrow(heap_.termination_exception(), NULL);
return Failure::Exception();
}
Failure* Isolate::Throw(Object* exception, MessageLocation* location) {
- DoThrow(exception, location, NULL);
+ DoThrow(exception, location);
return Failure::Exception();
}
@@ -584,12 +600,12 @@
void Isolate::PrintCurrentStackTrace(FILE* out) {
- StackTraceFrameIterator it;
+ StackTraceFrameIterator it(this);
while (!it.done()) {
HandleScope scope;
// Find code position if recorded in relocation info.
JavaScriptFrame* frame = it.frame();
- int pos = frame->LookupCode(this)->SourcePosition(frame->pc());
+ int pos = frame->LookupCode()->SourcePosition(frame->pc());
Handle<Object> pos_obj(Smi::FromInt(pos));
// Fetch function and receiver.
Handle<JSFunction> fun(JSFunction::cast(frame->function()));
@@ -613,14 +629,14 @@
void Isolate::ComputeLocation(MessageLocation* target) {
*target = MessageLocation(Handle<Script>(heap_.empty_script()), -1, -1);
- StackTraceFrameIterator it;
+ StackTraceFrameIterator it(this);
if (!it.done()) {
JavaScriptFrame* frame = it.frame();
JSFunction* fun = JSFunction::cast(frame->function());
Object* script = fun->shared()->script();
if (script->IsScript() &&
!(Script::cast(script)->source()->IsUndefined())) {
- int pos = frame->LookupCode(this)->SourcePosition(frame->pc());
+ int pos = frame->LookupCode()->SourcePosition(frame->pc());
// Compute the location from the function and the reloc info.
Handle<Script> casted_script(Script::cast(script));
*target = MessageLocation(casted_script, pos, pos + 1);
@@ -660,9 +676,7 @@
}
-void Isolate::DoThrow(MaybeObject* exception,
- MessageLocation* location,
- const char* message) {
+void Isolate::DoThrow(MaybeObject* exception, MessageLocation* location) {
ASSERT(!has_pending_exception());
HandleScope scope;
@@ -719,7 +733,6 @@
// Save the message for reporting if the the exception remains uncaught.
thread_local_top()->has_pending_message_ = report_exception;
- thread_local_top()->pending_message_ = message;
if (!message_obj.is_null()) {
thread_local_top()->pending_message_obj_ = *message_obj;
if (location != NULL) {
@@ -791,55 +804,36 @@
void Isolate::ReportPendingMessages() {
ASSERT(has_pending_exception());
+ PropagatePendingExceptionToExternalTryCatch();
+
// If the pending exception is OutOfMemoryException set out_of_memory in
// the global context. Note: We have to mark the global context here
// since the GenerateThrowOutOfMemory stub cannot make a RuntimeCall to
// set it.
- bool external_caught = IsExternallyCaught();
- thread_local_top()->external_caught_exception_ = external_caught;
- HandleScope scope(this);
- if (thread_local_top()->pending_exception_ ==
- Failure::OutOfMemoryException()) {
+ HandleScope scope;
+ if (thread_local_top_.pending_exception_ == Failure::OutOfMemoryException()) {
context()->mark_out_of_memory();
- } else if (thread_local_top()->pending_exception_ ==
- heap_.termination_exception()) {
- if (external_caught) {
- try_catch_handler()->can_continue_ = false;
- try_catch_handler()->exception_ = heap_.null_value();
- }
+ } else if (thread_local_top_.pending_exception_ ==
+ heap()->termination_exception()) {
+ // Do nothing: if needed, the exception has been already propagated to
+ // v8::TryCatch.
} else {
- // At this point all non-object (failure) exceptions have
- // been dealt with so this shouldn't fail.
- Object* pending_exception_object = pending_exception()->ToObjectUnchecked();
- Handle<Object> exception(pending_exception_object);
- thread_local_top()->external_caught_exception_ = false;
- if (external_caught) {
- try_catch_handler()->can_continue_ = true;
- try_catch_handler()->exception_ = thread_local_top()->pending_exception_;
- if (!thread_local_top()->pending_message_obj_->IsTheHole()) {
- try_catch_handler()->message_ =
- thread_local_top()->pending_message_obj_;
- }
- }
- if (thread_local_top()->has_pending_message_) {
- thread_local_top()->has_pending_message_ = false;
- if (thread_local_top()->pending_message_ != NULL) {
- MessageHandler::ReportMessage(thread_local_top()->pending_message_);
- } else if (!thread_local_top()->pending_message_obj_->IsTheHole()) {
- Handle<Object> message_obj(thread_local_top()->pending_message_obj_);
- if (thread_local_top()->pending_message_script_ != NULL) {
- Handle<Script> script(thread_local_top()->pending_message_script_);
- int start_pos = thread_local_top()->pending_message_start_pos_;
- int end_pos = thread_local_top()->pending_message_end_pos_;
+ if (thread_local_top_.has_pending_message_) {
+ thread_local_top_.has_pending_message_ = false;
+ if (!thread_local_top_.pending_message_obj_->IsTheHole()) {
+ HandleScope scope;
+ Handle<Object> message_obj(thread_local_top_.pending_message_obj_);
+ if (thread_local_top_.pending_message_script_ != NULL) {
+ Handle<Script> script(thread_local_top_.pending_message_script_);
+ int start_pos = thread_local_top_.pending_message_start_pos_;
+ int end_pos = thread_local_top_.pending_message_end_pos_;
MessageLocation location(script, start_pos, end_pos);
- MessageHandler::ReportMessage(&location, message_obj);
+ MessageHandler::ReportMessage(this, &location, message_obj);
} else {
- MessageHandler::ReportMessage(NULL, message_obj);
+ MessageHandler::ReportMessage(this, NULL, message_obj);
}
}
}
- thread_local_top()->external_caught_exception_ = external_caught;
- set_pending_exception(*exception);
}
clear_pending_message();
}
@@ -851,6 +845,9 @@
bool Isolate::OptionalRescheduleException(bool is_bottom_call) {
+ ASSERT(has_pending_exception());
+ PropagatePendingExceptionToExternalTryCatch();
+
// Allways reschedule out of memory exceptions.
if (!is_out_of_memory()) {
bool is_termination_exception =
@@ -965,7 +962,7 @@
memcpy(reinterpret_cast<char*>(thread_local_top()), from,
sizeof(ThreadLocalTop));
// This might be just paranoia, but it seems to be needed in case a
- // thread_local_ is restored on a separate OS thread.
+ // thread_local_top_ is restored on a separate OS thread.
#ifdef USE_SIMULATOR
#ifdef V8_TARGET_ARCH_ARM
thread_local_top()->simulator_ = Simulator::current(this);
diff --git a/src/type-info.cc b/src/type-info.cc
index 78f693c..1940601 100644
--- a/src/type-info.cc
+++ b/src/type-info.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -89,7 +89,7 @@
}
-bool TypeFeedbackOracle::StoreIsMonomorphic(Assignment* expr) {
+bool TypeFeedbackOracle::StoreIsMonomorphic(Expression* expr) {
Handle<Object> map_or_code(GetInfo(expr->position()));
if (map_or_code->IsMap()) return true;
if (map_or_code->IsCode()) {
@@ -119,7 +119,7 @@
}
-Handle<Map> TypeFeedbackOracle::StoreMonomorphicReceiverType(Assignment* expr) {
+Handle<Map> TypeFeedbackOracle::StoreMonomorphicReceiverType(Expression* expr) {
ASSERT(StoreIsMonomorphic(expr));
Handle<HeapObject> map_or_code(
Handle<HeapObject>::cast(GetInfo(expr->position())));
@@ -178,7 +178,7 @@
}
ExternalArrayType TypeFeedbackOracle::GetKeyedStoreExternalArrayType(
- Assignment* expr) {
+ Expression* expr) {
Handle<Object> stub = GetInfo(expr->position());
ASSERT(stub->IsCode());
return Code::cast(*stub)->external_array_type();
@@ -244,22 +244,7 @@
TypeInfo unknown = TypeInfo::Unknown();
if (!object->IsCode()) return unknown;
Handle<Code> code = Handle<Code>::cast(object);
- if (code->is_binary_op_stub()) {
- BinaryOpIC::TypeInfo type = static_cast<BinaryOpIC::TypeInfo>(
- code->binary_op_type());
- switch (type) {
- case BinaryOpIC::UNINIT_OR_SMI:
- return TypeInfo::Smi();
- case BinaryOpIC::DEFAULT:
- return (expr->op() == Token::DIV || expr->op() == Token::MUL)
- ? TypeInfo::Double()
- : TypeInfo::Integer32();
- case BinaryOpIC::HEAP_NUMBERS:
- return TypeInfo::Double();
- default:
- return unknown;
- }
- } else if (code->is_type_recording_binary_op_stub()) {
+ if (code->is_type_recording_binary_op_stub()) {
TRBinaryOpIC::TypeInfo type = static_cast<TRBinaryOpIC::TypeInfo>(
code->type_recording_binary_op_type());
TRBinaryOpIC::TypeInfo result_type = static_cast<TRBinaryOpIC::TypeInfo>(
@@ -290,6 +275,8 @@
return TypeInfo::Integer32();
case TRBinaryOpIC::HEAP_NUMBER:
return TypeInfo::Double();
+ case TRBinaryOpIC::BOTH_STRING:
+ return TypeInfo::String();
case TRBinaryOpIC::STRING:
case TRBinaryOpIC::GENERIC:
return unknown;
@@ -355,6 +342,18 @@
}
+void TypeFeedbackOracle::SetInfo(int position, Object* target) {
+ MaybeObject* maybe_result = dictionary_->AtNumberPut(position, target);
+ USE(maybe_result);
+#ifdef DEBUG
+ Object* result;
+ // Dictionary has been allocated with sufficient size for all elements.
+ ASSERT(maybe_result->ToObject(&result));
+ ASSERT(*dictionary_ == result);
+#endif
+}
+
+
void TypeFeedbackOracle::PopulateMap(Handle<Code> code) {
Isolate* isolate = Isolate::Current();
HandleScope scope(isolate);
@@ -371,51 +370,43 @@
int length = code_positions.length();
ASSERT(source_positions.length() == length);
for (int i = 0; i < length; i++) {
- HandleScope loop_scope(isolate);
+ AssertNoAllocation no_allocation;
RelocInfo info(code->instruction_start() + code_positions[i],
RelocInfo::CODE_TARGET, 0);
- Handle<Code> target(Code::GetCodeFromTargetAddress(info.target_address()));
+ Code* target = Code::GetCodeFromTargetAddress(info.target_address());
int position = source_positions[i];
InlineCacheState state = target->ic_state();
Code::Kind kind = target->kind();
- Handle<Object> value;
- if (kind == Code::BINARY_OP_IC ||
- kind == Code::TYPE_RECORDING_BINARY_OP_IC ||
+
+ if (kind == Code::TYPE_RECORDING_BINARY_OP_IC ||
kind == Code::COMPARE_IC) {
// TODO(kasperl): Avoid having multiple ICs with the same
// position by making sure that we have position information
// recorded for all binary ICs.
int entry = dictionary_->FindEntry(position);
if (entry == NumberDictionary::kNotFound) {
- value = target;
+ SetInfo(position, target);
}
} else if (state == MONOMORPHIC) {
if (kind == Code::KEYED_EXTERNAL_ARRAY_LOAD_IC ||
kind == Code::KEYED_EXTERNAL_ARRAY_STORE_IC) {
- value = target;
+ SetInfo(position, target);
} else if (target->kind() != Code::CALL_IC ||
target->check_type() == RECEIVER_MAP_CHECK) {
Map* map = target->FindFirstMap();
if (map == NULL) {
- value = target;
+ SetInfo(position, target);
} else {
- value = Handle<Map>(map);
+ SetInfo(position, map);
}
} else {
ASSERT(target->kind() == Code::CALL_IC);
CheckType check = target->check_type();
ASSERT(check != RECEIVER_MAP_CHECK);
- value = Handle<Object>(Smi::FromInt(check));
+ SetInfo(position, Smi::FromInt(check));
}
} else if (state == MEGAMORPHIC) {
- value = target;
- }
-
- if (!value.is_null()) {
- Handle<NumberDictionary> new_dict =
- isolate->factory()->DictionaryAtNumberPut(
- dictionary_, position, value);
- dictionary_ = loop_scope.CloseAndEscape(new_dict);
+ SetInfo(position, target);
}
}
// Allocate handle in the parent scope.
@@ -441,9 +432,7 @@
if (target->is_inline_cache_stub()) {
InlineCacheState state = target->ic_state();
Code::Kind kind = target->kind();
- if (kind == Code::BINARY_OP_IC) {
- if (target->binary_op_type() == BinaryOpIC::GENERIC) continue;
- } else if (kind == Code::TYPE_RECORDING_BINARY_OP_IC) {
+ if (kind == Code::TYPE_RECORDING_BINARY_OP_IC) {
if (target->type_recording_binary_op_type() ==
TRBinaryOpIC::GENERIC) {
continue;
diff --git a/src/type-info.h b/src/type-info.h
index c068489..f6e6729 100644
--- a/src/type-info.h
+++ b/src/type-info.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -239,18 +239,18 @@
TypeFeedbackOracle(Handle<Code> code, Handle<Context> global_context);
bool LoadIsMonomorphic(Property* expr);
- bool StoreIsMonomorphic(Assignment* expr);
+ bool StoreIsMonomorphic(Expression* expr);
bool CallIsMonomorphic(Call* expr);
Handle<Map> LoadMonomorphicReceiverType(Property* expr);
- Handle<Map> StoreMonomorphicReceiverType(Assignment* expr);
+ Handle<Map> StoreMonomorphicReceiverType(Expression* expr);
ZoneMapList* LoadReceiverTypes(Property* expr, Handle<String> name);
ZoneMapList* StoreReceiverTypes(Assignment* expr, Handle<String> name);
ZoneMapList* CallReceiverTypes(Call* expr, Handle<String> name);
ExternalArrayType GetKeyedLoadExternalArrayType(Property* expr);
- ExternalArrayType GetKeyedStoreExternalArrayType(Assignment* expr);
+ ExternalArrayType GetKeyedStoreExternalArrayType(Expression* expr);
CheckType GetCallCheckType(Call* expr);
Handle<JSObject> GetPrototypeForPrimitiveCheck(CheckType check);
@@ -267,6 +267,8 @@
Handle<String> name,
Code::Flags flags);
+ void SetInfo(int position, Object* target);
+
void PopulateMap(Handle<Code> code);
void CollectPositions(Code* code,
diff --git a/src/v8-counters.h b/src/v8-counters.h
index 04482e8..5e765b2 100644
--- a/src/v8-counters.h
+++ b/src/v8-counters.h
@@ -202,9 +202,6 @@
SC(array_function_runtime, V8.ArrayFunctionRuntime) \
SC(array_function_native, V8.ArrayFunctionNative) \
SC(for_in, V8.ForIn) \
- SC(memcopy_aligned, V8.MemCopyAligned) \
- SC(memcopy_unaligned, V8.MemCopyUnaligned) \
- SC(memcopy_noxmm, V8.MemCopyNoXMM) \
SC(enum_cache_hits, V8.EnumCacheHits) \
SC(enum_cache_misses, V8.EnumCacheMisses) \
SC(zone_segment_bytes, V8.ZoneSegmentBytes) \
diff --git a/src/v8.cc b/src/v8.cc
index 8153372..0b562fc 100644
--- a/src/v8.cc
+++ b/src/v8.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2009 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -41,6 +41,9 @@
namespace v8 {
namespace internal {
+static Mutex* init_once_mutex = OS::CreateMutex();
+static bool init_once_called = false;
+
bool V8::is_running_ = false;
bool V8::has_been_setup_ = false;
bool V8::has_been_disposed_ = false;
@@ -49,6 +52,8 @@
bool V8::Initialize(Deserializer* des) {
+ InitializeOncePerProcess();
+
// The current thread may not yet had entered an isolate to run.
// Note the Isolate::Current() may be non-null because for various
// initialization purposes an initializing thread may be assigned an isolate
@@ -58,8 +63,8 @@
}
ASSERT(i::Isolate::CurrentPerIsolateThreadData() != NULL);
- ASSERT(i::Isolate::CurrentPerIsolateThreadData()->thread_id() ==
- i::Thread::GetThreadLocalInt(i::Isolate::thread_id_key()));
+ ASSERT(i::Isolate::CurrentPerIsolateThreadData()->thread_id().Equals(
+ i::ThreadId::Current()));
ASSERT(i::Isolate::CurrentPerIsolateThreadData()->isolate() ==
i::Isolate::Current());
@@ -68,15 +73,6 @@
Isolate* isolate = Isolate::Current();
if (isolate->IsInitialized()) return true;
-#if defined(V8_TARGET_ARCH_ARM) && !defined(USE_ARM_EABI)
- use_crankshaft_ = false;
-#else
- use_crankshaft_ = FLAG_crankshaft;
-#endif
-
- // Peephole optimization might interfere with deoptimization.
- FLAG_peephole_optimization = !use_crankshaft_;
-
is_running_ = true;
has_been_setup_ = true;
has_fatal_error_ = false;
@@ -171,8 +167,8 @@
} double_int_union;
-Object* V8::FillHeapNumberWithRandom(Object* heap_number) {
- uint64_t random_bits = Random(Isolate::Current());
+Object* V8::FillHeapNumberWithRandom(Object* heap_number, Isolate* isolate) {
+ uint64_t random_bits = Random(isolate);
// Make a double* from address (heap_number + sizeof(double)).
double_int_union* r = reinterpret_cast<double_int_union*>(
reinterpret_cast<char*>(heap_number) +
@@ -188,4 +184,30 @@
return heap_number;
}
+
+void V8::InitializeOncePerProcess() {
+ ScopedLock lock(init_once_mutex);
+ if (init_once_called) return;
+ init_once_called = true;
+
+ // Setup the platform OS support.
+ OS::Setup();
+
+ use_crankshaft_ = FLAG_crankshaft;
+
+ if (Serializer::enabled()) {
+ use_crankshaft_ = false;
+ }
+
+ CPU::Setup();
+ if (!CPU::SupportsCrankshaft()) {
+ use_crankshaft_ = false;
+ }
+
+ RuntimeProfiler::GlobalSetup();
+
+ // Peephole optimization might interfere with deoptimization.
+ FLAG_peephole_optimization = !use_crankshaft_;
+}
+
} } // namespace v8::internal
diff --git a/src/v8.h b/src/v8.h
index e7ca0d2..776fa9c 100644
--- a/src/v8.h
+++ b/src/v8.h
@@ -84,7 +84,6 @@
static void TearDown();
static bool IsRunning() { return is_running_; }
static bool UseCrankshaft() { return use_crankshaft_; }
- static void DisableCrankshaft() { use_crankshaft_ = false; }
// To be dead you have to have lived
// TODO(isolates): move IsDead to Isolate.
static bool IsDead() { return has_fatal_error_ || has_been_disposed_; }
@@ -101,12 +100,15 @@
// use a separate random state for internal random number
// generation.
static uint32_t RandomPrivate(Isolate* isolate);
- static Object* FillHeapNumberWithRandom(Object* heap_number);
+ static Object* FillHeapNumberWithRandom(Object* heap_number,
+ Isolate* isolate);
// Idle notification directly from the API.
static bool IdleNotification();
private:
+ static void InitializeOncePerProcess();
+
// True if engine is currently running
static bool is_running_;
// True if V8 has ever been run
diff --git a/src/v8threads.cc b/src/v8threads.cc
index cecafaa..4b033fc 100644
--- a/src/v8threads.cc
+++ b/src/v8threads.cc
@@ -147,11 +147,11 @@
// First check whether the current thread has been 'lazily archived', ie
// not archived at all. If that is the case we put the state storage we
// had prepared back in the free list, since we didn't need it after all.
- if (lazily_archived_thread_.IsSelf()) {
- lazily_archived_thread_.Initialize(ThreadHandle::INVALID);
+ if (lazily_archived_thread_.Equals(ThreadId::Current())) {
+ lazily_archived_thread_ = ThreadId::Invalid();
ASSERT(Isolate::CurrentPerIsolateThreadData()->thread_state() ==
lazily_archived_thread_state_);
- lazily_archived_thread_state_->set_id(kInvalidId);
+ lazily_archived_thread_state_->set_id(ThreadId::Invalid());
lazily_archived_thread_state_->LinkInto(ThreadState::FREE_LIST);
lazily_archived_thread_state_ = NULL;
Isolate::CurrentPerIsolateThreadData()->set_thread_state(NULL);
@@ -190,7 +190,7 @@
isolate_->stack_guard()->TerminateExecution();
state->set_terminate_on_restore(false);
}
- state->set_id(kInvalidId);
+ state->set_id(ThreadId::Invalid());
state->Unlink();
state->LinkInto(ThreadState::FREE_LIST);
return true;
@@ -199,13 +199,13 @@
void ThreadManager::Lock() {
mutex_->Lock();
- mutex_owner_.Initialize(ThreadHandle::SELF);
+ mutex_owner_ = ThreadId::Current();
ASSERT(IsLockedByCurrentThread());
}
void ThreadManager::Unlock() {
- mutex_owner_.Initialize(ThreadHandle::INVALID);
+ mutex_owner_ = ThreadId::Invalid();
mutex_->Unlock();
}
@@ -224,7 +224,7 @@
ThreadState::ThreadState(ThreadManager* thread_manager)
- : id_(ThreadManager::kInvalidId),
+ : id_(ThreadId::Invalid()),
terminate_on_restore_(false),
next_(this),
previous_(this),
@@ -282,8 +282,8 @@
// defined as 0.)
ThreadManager::ThreadManager()
: mutex_(OS::CreateMutex()),
- mutex_owner_(ThreadHandle::INVALID),
- lazily_archived_thread_(ThreadHandle::INVALID),
+ mutex_owner_(ThreadId::Invalid()),
+ lazily_archived_thread_(ThreadId::Invalid()),
lazily_archived_thread_state_(NULL),
free_anchor_(NULL),
in_use_anchor_(NULL) {
@@ -298,16 +298,16 @@
void ThreadManager::ArchiveThread() {
- ASSERT(!lazily_archived_thread_.IsValid());
+ ASSERT(lazily_archived_thread_.Equals(ThreadId::Invalid()));
ASSERT(!IsArchived());
ThreadState* state = GetFreeThreadState();
state->Unlink();
Isolate::CurrentPerIsolateThreadData()->set_thread_state(state);
- lazily_archived_thread_.Initialize(ThreadHandle::SELF);
+ lazily_archived_thread_ = ThreadId::Current();
lazily_archived_thread_state_ = state;
- ASSERT(state->id() == kInvalidId);
+ ASSERT(state->id().Equals(ThreadId::Invalid()));
state->set_id(CurrentId());
- ASSERT(state->id() != kInvalidId);
+ ASSERT(!state->id().Equals(ThreadId::Invalid()));
}
@@ -326,7 +326,7 @@
to = isolate_->stack_guard()->ArchiveStackGuard(to);
to = isolate_->regexp_stack()->ArchiveStack(to);
to = isolate_->bootstrapper()->ArchiveState(to);
- lazily_archived_thread_.Initialize(ThreadHandle::INVALID);
+ lazily_archived_thread_ = ThreadId::Invalid();
lazily_archived_thread_state_ = NULL;
}
@@ -373,16 +373,16 @@
}
-int ThreadManager::CurrentId() {
- return Thread::GetThreadLocalInt(Isolate::thread_id_key());
+ThreadId ThreadManager::CurrentId() {
+ return ThreadId::Current();
}
-void ThreadManager::TerminateExecution(int thread_id) {
+void ThreadManager::TerminateExecution(ThreadId thread_id) {
for (ThreadState* state = FirstThreadStateInUse();
state != NULL;
state = state->Next()) {
- if (thread_id == state->id()) {
+ if (thread_id.Equals(state->id())) {
state->set_terminate_on_restore(true);
}
}
diff --git a/src/v8threads.h b/src/v8threads.h
index f1992ad..d8a923e 100644
--- a/src/v8threads.h
+++ b/src/v8threads.h
@@ -43,8 +43,8 @@
void Unlink();
// Id of thread.
- void set_id(int id) { id_ = id; }
- int id() { return id_; }
+ void set_id(ThreadId id) { id_ = id; }
+ ThreadId id() { return id_; }
// Should the thread be terminated when it is restored?
bool terminate_on_restore() { return terminate_on_restore_; }
@@ -59,7 +59,7 @@
void AllocateSpace();
- int id_;
+ ThreadId id_;
bool terminate_on_restore_;
char* data_;
ThreadState* next_;
@@ -78,7 +78,7 @@
class ThreadVisitor {
public:
// ThreadLocalTop may be only available during this call.
- virtual void VisitThread(ThreadLocalTop* top) = 0;
+ virtual void VisitThread(Isolate* isolate, ThreadLocalTop* top) = 0;
protected:
virtual ~ThreadVisitor() {}
@@ -97,17 +97,18 @@
void Iterate(ObjectVisitor* v);
void IterateArchivedThreads(ThreadVisitor* v);
- bool IsLockedByCurrentThread() { return mutex_owner_.IsSelf(); }
+ bool IsLockedByCurrentThread() {
+ return mutex_owner_.Equals(ThreadId::Current());
+ }
- int CurrentId();
+ ThreadId CurrentId();
- void TerminateExecution(int thread_id);
+ void TerminateExecution(ThreadId thread_id);
// Iterate over in-use states.
ThreadState* FirstThreadStateInUse();
ThreadState* GetFreeThreadState();
- static const int kInvalidId = -1;
private:
ThreadManager();
~ThreadManager();
@@ -115,8 +116,8 @@
void EagerlyArchiveThread();
Mutex* mutex_;
- ThreadHandle mutex_owner_;
- ThreadHandle lazily_archived_thread_;
+ ThreadId mutex_owner_;
+ ThreadId lazily_archived_thread_;
ThreadState* lazily_archived_thread_state_;
// In the following two lists there is always at least one object on the list.
diff --git a/src/v8utils.h b/src/v8utils.h
index 0aa53ca..93fc1fd 100644
--- a/src/v8utils.h
+++ b/src/v8utils.h
@@ -120,7 +120,9 @@
// Memory
// Copies data from |src| to |dst|. The data spans MUST not overlap.
-inline void CopyWords(Object** dst, Object** src, int num_words) {
+template <typename T>
+inline void CopyWords(T* dst, T* src, int num_words) {
+ STATIC_ASSERT(sizeof(T) == kPointerSize);
ASSERT(Min(dst, src) + num_words <= Max(dst, src));
ASSERT(num_words > 0);
@@ -254,51 +256,14 @@
};
-// Custom memcpy implementation for platforms where the standard version
-// may not be good enough.
-#if defined(V8_TARGET_ARCH_IA32)
-
-// The default memcpy on ia32 architectures is generally not as efficient
-// as possible. (If any further ia32 platforms are introduced where the
-// memcpy function is efficient, exclude them from this branch).
-
-typedef void (*MemCopyFunction)(void* dest, const void* src, size_t size);
-
-// Implemented in codegen-<arch>.cc.
-MemCopyFunction CreateMemCopyFunction();
-
-// Copy memory area to disjoint memory area.
-static inline void MemCopy(void* dest, const void* src, size_t size) {
- static MemCopyFunction memcopy = CreateMemCopyFunction();
- (*memcopy)(dest, src, size);
-#ifdef DEBUG
- CHECK_EQ(0, memcmp(dest, src, size));
-#endif
-}
-
-// Limit below which the extra overhead of the MemCopy function is likely
-// to outweigh the benefits of faster copying.
-static const int kMinComplexMemCopy = 64;
-
-#else // V8_TARGET_ARCH_IA32
-
-static inline void MemCopy(void* dest, const void* src, size_t size) {
- memcpy(dest, src, size);
-}
-
-static const int kMinComplexMemCopy = 256;
-
-#endif // V8_TARGET_ARCH_IA32
-
-
// Copy from ASCII/16bit chars to ASCII/16bit chars.
template <typename sourcechar, typename sinkchar>
static inline void CopyChars(sinkchar* dest, const sourcechar* src, int chars) {
sinkchar* limit = dest + chars;
#ifdef V8_HOST_CAN_READ_UNALIGNED
if (sizeof(*dest) == sizeof(*src)) {
- if (chars >= static_cast<int>(kMinComplexMemCopy / sizeof(*dest))) {
- MemCopy(dest, src, chars * sizeof(*dest));
+ if (chars >= static_cast<int>(OS::kMinComplexMemCopy / sizeof(*dest))) {
+ OS::MemCopy(dest, src, chars * sizeof(*dest));
return;
}
// Number of characters in a uintptr_t.
diff --git a/src/variables.cc b/src/variables.cc
index fa7ce1b..0502722 100644
--- a/src/variables.cc
+++ b/src/variables.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -35,26 +35,8 @@
namespace internal {
// ----------------------------------------------------------------------------
-// Implementation StaticType.
-
-
-const char* StaticType::Type2String(StaticType* type) {
- switch (type->kind_) {
- case UNKNOWN:
- return "UNKNOWN";
- case LIKELY_SMI:
- return "LIKELY_SMI";
- default:
- UNREACHABLE();
- }
- return "UNREACHABLE";
-}
-
-
-// ----------------------------------------------------------------------------
// Implementation Variable.
-
const char* Variable::Mode2String(Mode mode) {
switch (mode) {
case VAR: return "VAR";
diff --git a/src/variables.h b/src/variables.h
index 67e1a18..b1ff0db 100644
--- a/src/variables.h
+++ b/src/variables.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -33,46 +33,6 @@
namespace v8 {
namespace internal {
-// Variables and AST expression nodes can track their "type" to enable
-// optimizations and removal of redundant checks when generating code.
-
-class StaticType {
- public:
- enum Kind {
- UNKNOWN,
- LIKELY_SMI
- };
-
- StaticType() : kind_(UNKNOWN) {}
-
- bool Is(Kind kind) const { return kind_ == kind; }
-
- bool IsKnown() const { return !Is(UNKNOWN); }
- bool IsUnknown() const { return Is(UNKNOWN); }
- bool IsLikelySmi() const { return Is(LIKELY_SMI); }
-
- void CopyFrom(StaticType* other) {
- kind_ = other->kind_;
- }
-
- static const char* Type2String(StaticType* type);
-
- // LIKELY_SMI accessors
- void SetAsLikelySmi() {
- kind_ = LIKELY_SMI;
- }
-
- void SetAsLikelySmiIfUnknown() {
- if (IsUnknown()) {
- SetAsLikelySmi();
- }
- }
-
- private:
- Kind kind_;
-};
-
-
// The AST refers to variables via VariableProxies - placeholders for the actual
// variables. Variables themselves are never directly referred to from the AST,
// they are maintained by scopes, and referred to from VariableProxies and Slots
@@ -181,8 +141,6 @@
Expression* rewrite() const { return rewrite_; }
void set_rewrite(Expression* expr) { rewrite_ = expr; }
- StaticType* type() { return &type_; }
-
private:
Scope* scope_;
Handle<String> name_;
@@ -191,9 +149,6 @@
Variable* local_if_not_shadowed_;
- // Static type information
- StaticType type_;
-
// Code generation.
// rewrite_ is usually a Slot or a Property, but may be any expression.
Expression* rewrite_;
diff --git a/src/version.cc b/src/version.cc
index 6104dac..25939c2 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -33,9 +33,9 @@
// NOTE these macros are used by the SCons build script so their names
// cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 3
-#define MINOR_VERSION 2
-#define BUILD_NUMBER 6
-#define PATCH_LEVEL 0
+#define MINOR_VERSION 3
+#define BUILD_NUMBER 0
+#define PATCH_LEVEL 1
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
#define IS_CANDIDATE_VERSION 0
diff --git a/src/virtual-frame-heavy-inl.h b/src/virtual-frame-heavy-inl.h
deleted file mode 100644
index cf12eca..0000000
--- a/src/virtual-frame-heavy-inl.h
+++ /dev/null
@@ -1,190 +0,0 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_VIRTUAL_FRAME_HEAVY_INL_H_
-#define V8_VIRTUAL_FRAME_HEAVY_INL_H_
-
-#include "type-info.h"
-#include "register-allocator.h"
-#include "scopes.h"
-#include "register-allocator-inl.h"
-#include "codegen-inl.h"
-
-namespace v8 {
-namespace internal {
-
-// On entry to a function, the virtual frame already contains the receiver,
-// the parameters, and a return address. All frame elements are in memory.
-VirtualFrame::VirtualFrame()
- : elements_(parameter_count() + local_count() + kPreallocatedElements),
- stack_pointer_(parameter_count() + 1) { // 0-based index of TOS.
- for (int i = 0; i <= stack_pointer_; i++) {
- elements_.Add(FrameElement::MemoryElement(TypeInfo::Unknown()));
- }
- for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
- register_locations_[i] = kIllegalIndex;
- }
-}
-
-
-// When cloned, a frame is a deep copy of the original.
-VirtualFrame::VirtualFrame(VirtualFrame* original)
- : elements_(original->element_count()),
- stack_pointer_(original->stack_pointer_) {
- elements_.AddAll(original->elements_);
- // Copy register locations from original.
- memcpy(®ister_locations_,
- original->register_locations_,
- sizeof(register_locations_));
-}
-
-
-void VirtualFrame::PushFrameSlotAt(int index) {
- elements_.Add(CopyElementAt(index));
-}
-
-
-void VirtualFrame::Push(Register reg, TypeInfo info) {
- if (is_used(reg)) {
- int index = register_location(reg);
- FrameElement element = CopyElementAt(index, info);
- elements_.Add(element);
- } else {
- Use(reg, element_count());
- FrameElement element =
- FrameElement::RegisterElement(reg, FrameElement::NOT_SYNCED, info);
- elements_.Add(element);
- }
-}
-
-
-bool VirtualFrame::ConstantPoolOverflowed() {
- return FrameElement::ConstantPoolOverflowed();
-}
-
-
-bool VirtualFrame::Equals(VirtualFrame* other) {
-#ifdef DEBUG
- for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
- if (register_location(i) != other->register_location(i)) {
- return false;
- }
- }
- if (element_count() != other->element_count()) return false;
-#endif
- if (stack_pointer_ != other->stack_pointer_) return false;
- for (int i = 0; i < element_count(); i++) {
- if (!elements_[i].Equals(other->elements_[i])) return false;
- }
-
- return true;
-}
-
-
-void VirtualFrame::SetTypeForLocalAt(int index, TypeInfo info) {
- elements_[local0_index() + index].set_type_info(info);
-}
-
-
-// Make the type of all elements be MEMORY.
-void VirtualFrame::SpillAll() {
- for (int i = 0; i < element_count(); i++) {
- SpillElementAt(i);
- }
-}
-
-
-void VirtualFrame::PrepareForReturn() {
- // Spill all locals. This is necessary to make sure all locals have
- // the right value when breaking at the return site in the debugger.
- for (int i = 0; i < expression_base_index(); i++) {
- SpillElementAt(i);
- }
-}
-
-
-void VirtualFrame::SetTypeForParamAt(int index, TypeInfo info) {
- elements_[param0_index() + index].set_type_info(info);
-}
-
-
-void VirtualFrame::Nip(int num_dropped) {
- ASSERT(num_dropped >= 0);
- if (num_dropped == 0) return;
- Result tos = Pop();
- if (num_dropped > 1) {
- Drop(num_dropped - 1);
- }
- SetElementAt(0, &tos);
-}
-
-
-void VirtualFrame::Push(Smi* value) {
- Push(Handle<Object> (value));
-}
-
-
-int VirtualFrame::register_location(Register reg) {
- return register_locations_[RegisterAllocator::ToNumber(reg)];
-}
-
-
-void VirtualFrame::set_register_location(Register reg, int index) {
- register_locations_[RegisterAllocator::ToNumber(reg)] = index;
-}
-
-
-bool VirtualFrame::is_used(Register reg) {
- return register_locations_[RegisterAllocator::ToNumber(reg)]
- != kIllegalIndex;
-}
-
-
-void VirtualFrame::SetElementAt(int index, Handle<Object> value) {
- Result temp(value);
- SetElementAt(index, &temp);
-}
-
-
-Result VirtualFrame::CallStub(CodeStub* stub, int arg_count) {
- PrepareForCall(arg_count, arg_count);
- return RawCallStub(stub);
-}
-
-
-int VirtualFrame::parameter_count() {
- return cgen()->scope()->num_parameters();
-}
-
-
-int VirtualFrame::local_count() {
- return cgen()->scope()->num_stack_slots();
-}
-
-} } // namespace v8::internal
-
-#endif // V8_VIRTUAL_FRAME_HEAVY_INL_H_
diff --git a/src/virtual-frame-heavy.cc b/src/virtual-frame-heavy.cc
deleted file mode 100644
index 7270280..0000000
--- a/src/virtual-frame-heavy.cc
+++ /dev/null
@@ -1,312 +0,0 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "v8.h"
-
-#include "codegen-inl.h"
-#include "register-allocator-inl.h"
-#include "virtual-frame-inl.h"
-
-namespace v8 {
-namespace internal {
-
-void VirtualFrame::SetElementAt(int index, Result* value) {
- int frame_index = element_count() - index - 1;
- ASSERT(frame_index >= 0);
- ASSERT(frame_index < element_count());
- ASSERT(value->is_valid());
- FrameElement original = elements_[frame_index];
-
- // Early exit if the element is the same as the one being set.
- bool same_register = original.is_register()
- && value->is_register()
- && original.reg().is(value->reg());
- bool same_constant = original.is_constant()
- && value->is_constant()
- && original.handle().is_identical_to(value->handle());
- if (same_register || same_constant) {
- value->Unuse();
- return;
- }
-
- InvalidateFrameSlotAt(frame_index);
-
- if (value->is_register()) {
- if (is_used(value->reg())) {
- // The register already appears on the frame. Either the existing
- // register element, or the new element at frame_index, must be made
- // a copy.
- int i = register_location(value->reg());
-
- if (i < frame_index) {
- // The register FrameElement is lower in the frame than the new copy.
- elements_[frame_index] = CopyElementAt(i);
- } else {
- // There was an early bailout for the case of setting a
- // register element to itself.
- ASSERT(i != frame_index);
- elements_[frame_index] = elements_[i];
- elements_[i] = CopyElementAt(frame_index);
- if (elements_[frame_index].is_synced()) {
- elements_[i].set_sync();
- }
- elements_[frame_index].clear_sync();
- set_register_location(value->reg(), frame_index);
- for (int j = i + 1; j < element_count(); j++) {
- if (elements_[j].is_copy() && elements_[j].index() == i) {
- elements_[j].set_index(frame_index);
- }
- }
- }
- } else {
- // The register value->reg() was not already used on the frame.
- Use(value->reg(), frame_index);
- elements_[frame_index] =
- FrameElement::RegisterElement(value->reg(),
- FrameElement::NOT_SYNCED,
- value->type_info());
- }
- } else {
- ASSERT(value->is_constant());
- elements_[frame_index] =
- FrameElement::ConstantElement(value->handle(),
- FrameElement::NOT_SYNCED);
- }
- value->Unuse();
-}
-
-
-// Create a duplicate of an existing valid frame element.
-// We can pass an optional number type information that will override the
-// existing information about the backing element. The new information must
-// not conflict with the existing type information and must be equally or
-// more precise. The default parameter value kUninitialized means that there
-// is no additional information.
-FrameElement VirtualFrame::CopyElementAt(int index, TypeInfo info) {
- ASSERT(index >= 0);
- ASSERT(index < element_count());
-
- FrameElement target = elements_[index];
- FrameElement result;
-
- switch (target.type()) {
- case FrameElement::CONSTANT:
- // We do not copy constants and instead return a fresh unsynced
- // constant.
- result = FrameElement::ConstantElement(target.handle(),
- FrameElement::NOT_SYNCED);
- break;
-
- case FrameElement::COPY:
- // We do not allow copies of copies, so we follow one link to
- // the actual backing store of a copy before making a copy.
- index = target.index();
- ASSERT(elements_[index].is_memory() || elements_[index].is_register());
- // Fall through.
-
- case FrameElement::MEMORY: // Fall through.
- case FrameElement::REGISTER: {
- // All copies are backed by memory or register locations.
- result.set_type(FrameElement::COPY);
- result.clear_copied();
- result.clear_sync();
- result.set_index(index);
- elements_[index].set_copied();
- // Update backing element's number information.
- TypeInfo existing = elements_[index].type_info();
- ASSERT(!existing.IsUninitialized());
- // Assert that the new type information (a) does not conflict with the
- // existing one and (b) is equally or more precise.
- ASSERT((info.ToInt() & existing.ToInt()) == existing.ToInt());
- ASSERT((info.ToInt() | existing.ToInt()) == info.ToInt());
-
- elements_[index].set_type_info(!info.IsUninitialized()
- ? info
- : existing);
- break;
- }
- case FrameElement::INVALID:
- // We should not try to copy invalid elements.
- UNREACHABLE();
- break;
- }
- return result;
-}
-
-
-// Modify the state of the virtual frame to match the actual frame by adding
-// extra in-memory elements to the top of the virtual frame. The extra
-// elements will be externally materialized on the actual frame (eg, by
-// pushing an exception handler). No code is emitted.
-void VirtualFrame::Adjust(int count) {
- ASSERT(count >= 0);
- ASSERT(stack_pointer_ == element_count() - 1);
-
- for (int i = 0; i < count; i++) {
- elements_.Add(FrameElement::MemoryElement(TypeInfo::Unknown()));
- }
- stack_pointer_ += count;
-}
-
-
-void VirtualFrame::ForgetElements(int count) {
- ASSERT(count >= 0);
- ASSERT(element_count() >= count);
-
- for (int i = 0; i < count; i++) {
- FrameElement last = elements_.RemoveLast();
- if (last.is_register()) {
- // A hack to properly count register references for the code
- // generator's current frame and also for other frames. The
- // same code appears in PrepareMergeTo.
- if (cgen()->frame() == this) {
- Unuse(last.reg());
- } else {
- set_register_location(last.reg(), kIllegalIndex);
- }
- }
- }
-}
-
-
-// Make the type of the element at a given index be MEMORY.
-void VirtualFrame::SpillElementAt(int index) {
- if (!elements_[index].is_valid()) return;
-
- SyncElementAt(index);
- // Number type information is preserved.
- // Copies get their number information from their backing element.
- TypeInfo info;
- if (!elements_[index].is_copy()) {
- info = elements_[index].type_info();
- } else {
- info = elements_[elements_[index].index()].type_info();
- }
- // The element is now in memory. Its copied flag is preserved.
- FrameElement new_element = FrameElement::MemoryElement(info);
- if (elements_[index].is_copied()) {
- new_element.set_copied();
- }
- if (elements_[index].is_untagged_int32()) {
- new_element.set_untagged_int32(true);
- }
- if (elements_[index].is_register()) {
- Unuse(elements_[index].reg());
- }
- elements_[index] = new_element;
-}
-
-
-// Clear the dirty bit for the element at a given index.
-void VirtualFrame::SyncElementAt(int index) {
- if (index <= stack_pointer_) {
- if (!elements_[index].is_synced()) SyncElementBelowStackPointer(index);
- } else if (index == stack_pointer_ + 1) {
- SyncElementByPushing(index);
- } else {
- SyncRange(stack_pointer_ + 1, index);
- }
-}
-
-
-void VirtualFrame::PrepareMergeTo(VirtualFrame* expected) {
- // Perform state changes on this frame that will make merge to the
- // expected frame simpler or else increase the likelihood that his
- // frame will match another.
- for (int i = 0; i < element_count(); i++) {
- FrameElement source = elements_[i];
- FrameElement target = expected->elements_[i];
-
- if (!target.is_valid() ||
- (target.is_memory() && !source.is_memory() && source.is_synced())) {
- // No code needs to be generated to invalidate valid elements.
- // No code needs to be generated to move values to memory if
- // they are already synced. We perform those moves here, before
- // merging.
- if (source.is_register()) {
- // If the frame is the code generator's current frame, we have
- // to decrement both the frame-internal and global register
- // counts.
- if (cgen()->frame() == this) {
- Unuse(source.reg());
- } else {
- set_register_location(source.reg(), kIllegalIndex);
- }
- }
- elements_[i] = target;
- } else if (target.is_register() && !target.is_synced() &&
- !source.is_memory()) {
- // If an element's target is a register that doesn't need to be
- // synced, and the element is not in memory, then the sync state
- // of the element is irrelevant. We clear the sync bit.
- ASSERT(source.is_valid());
- elements_[i].clear_sync();
- }
- }
-}
-
-
-void VirtualFrame::PrepareForCall(int spilled_args, int dropped_args) {
- ASSERT(height() >= dropped_args);
- ASSERT(height() >= spilled_args);
- ASSERT(dropped_args <= spilled_args);
-
- SyncRange(0, element_count() - 1);
- // Spill registers.
- for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
- if (is_used(i)) {
- SpillElementAt(register_location(i));
- }
- }
-
- // Spill the arguments.
- for (int i = element_count() - spilled_args; i < element_count(); i++) {
- if (!elements_[i].is_memory()) {
- SpillElementAt(i);
- }
- }
-
- // Forget the frame elements that will be popped by the call.
- Forget(dropped_args);
-}
-
-
-// If there are any registers referenced only by the frame, spill one.
-Register VirtualFrame::SpillAnyRegister() {
- // Find the leftmost (ordered by register number) register whose only
- // reference is in the frame.
- for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
- if (is_used(i) && cgen()->allocator()->count(i) == 1) {
- SpillElementAt(register_location(i));
- ASSERT(!cgen()->allocator()->is_used(i));
- return RegisterAllocator::ToRegister(i);
- }
- }
- return no_reg;
-}
-
-} } // namespace v8::internal
diff --git a/src/virtual-frame-light-inl.h b/src/virtual-frame-light-inl.h
deleted file mode 100644
index 681f93f..0000000
--- a/src/virtual-frame-light-inl.h
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_VIRTUAL_FRAME_LIGHT_INL_H_
-#define V8_VIRTUAL_FRAME_LIGHT_INL_H_
-
-#include "codegen.h"
-#include "register-allocator.h"
-#include "scopes.h"
-#include "type-info.h"
-
-#include "codegen-inl.h"
-#include "jump-target-light-inl.h"
-
-namespace v8 {
-namespace internal {
-
-VirtualFrame::VirtualFrame(InvalidVirtualFrameInitializer* dummy)
- : element_count_(0),
- top_of_stack_state_(NO_TOS_REGISTERS),
- register_allocation_map_(0),
- tos_known_smi_map_(0) { }
-
-
-// On entry to a function, the virtual frame already contains the receiver,
-// the parameters, and a return address. All frame elements are in memory.
-VirtualFrame::VirtualFrame()
- : element_count_(parameter_count() + 2),
- top_of_stack_state_(NO_TOS_REGISTERS),
- register_allocation_map_(0),
- tos_known_smi_map_(0) { }
-
-
-// When cloned, a frame is a deep copy of the original.
-VirtualFrame::VirtualFrame(VirtualFrame* original)
- : element_count_(original->element_count()),
- top_of_stack_state_(original->top_of_stack_state_),
- register_allocation_map_(original->register_allocation_map_),
- tos_known_smi_map_(0) { }
-
-
-bool VirtualFrame::Equals(const VirtualFrame* other) {
- ASSERT(element_count() == other->element_count());
- if (top_of_stack_state_ != other->top_of_stack_state_) return false;
- if (register_allocation_map_ != other->register_allocation_map_) return false;
- if (tos_known_smi_map_ != other->tos_known_smi_map_) return false;
-
- return true;
-}
-
-
-void VirtualFrame::PrepareForReturn() {
- // Don't bother flushing tos registers as returning does not require more
- // access to the expression stack.
- top_of_stack_state_ = NO_TOS_REGISTERS;
-}
-
-
-VirtualFrame::RegisterAllocationScope::RegisterAllocationScope(
- CodeGenerator* cgen)
- : cgen_(cgen),
- old_is_spilled_(
- Isolate::Current()->is_virtual_frame_in_spilled_scope()) {
- Isolate::Current()->set_is_virtual_frame_in_spilled_scope(false);
- if (old_is_spilled_) {
- VirtualFrame* frame = cgen->frame();
- if (frame != NULL) {
- frame->AssertIsSpilled();
- }
- }
-}
-
-
-VirtualFrame::RegisterAllocationScope::~RegisterAllocationScope() {
- Isolate::Current()->set_is_virtual_frame_in_spilled_scope(old_is_spilled_);
- if (old_is_spilled_) {
- VirtualFrame* frame = cgen_->frame();
- if (frame != NULL) {
- frame->SpillAll();
- }
- }
-}
-
-
-CodeGenerator* VirtualFrame::cgen() const {
- return CodeGeneratorScope::Current(Isolate::Current());
-}
-
-
-MacroAssembler* VirtualFrame::masm() { return cgen()->masm(); }
-
-
-void VirtualFrame::CallStub(CodeStub* stub, int arg_count) {
- if (arg_count != 0) Forget(arg_count);
- ASSERT(cgen()->HasValidEntryRegisters());
- masm()->CallStub(stub);
-}
-
-
-int VirtualFrame::parameter_count() const {
- return cgen()->scope()->num_parameters();
-}
-
-
-int VirtualFrame::local_count() const {
- return cgen()->scope()->num_stack_slots();
-}
-
-
-int VirtualFrame::frame_pointer() const { return parameter_count() + 3; }
-
-
-int VirtualFrame::context_index() { return frame_pointer() - 1; }
-
-
-int VirtualFrame::function_index() { return frame_pointer() - 2; }
-
-
-int VirtualFrame::local0_index() const { return frame_pointer() + 2; }
-
-
-int VirtualFrame::fp_relative(int index) {
- ASSERT(index < element_count());
- ASSERT(frame_pointer() < element_count()); // FP is on the frame.
- return (frame_pointer() - index) * kPointerSize;
-}
-
-
-int VirtualFrame::expression_base_index() const {
- return local0_index() + local_count();
-}
-
-
-int VirtualFrame::height() const {
- return element_count() - expression_base_index();
-}
-
-
-MemOperand VirtualFrame::LocalAt(int index) {
- ASSERT(0 <= index);
- ASSERT(index < local_count());
- return MemOperand(fp, kLocal0Offset - index * kPointerSize);
-}
-
-} } // namespace v8::internal
-
-#endif // V8_VIRTUAL_FRAME_LIGHT_INL_H_
diff --git a/src/virtual-frame-light.cc b/src/virtual-frame-light.cc
deleted file mode 100644
index bbaaaf5..0000000
--- a/src/virtual-frame-light.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "v8.h"
-
-#include "codegen-inl.h"
-#include "register-allocator-inl.h"
-#include "virtual-frame-inl.h"
-
-namespace v8 {
-namespace internal {
-
-void VirtualFrame::Adjust(int count) {
- ASSERT(count >= 0);
- RaiseHeight(count, 0);
-}
-
-
-// If there are any registers referenced only by the frame, spill one.
-Register VirtualFrame::SpillAnyRegister() {
- UNIMPLEMENTED();
- return no_reg;
-}
-
-
-InvalidVirtualFrameInitializer* kInvalidVirtualFrameInitializer = NULL;
-
-} } // namespace v8::internal
diff --git a/src/virtual-frame.cc b/src/virtual-frame.cc
deleted file mode 100644
index 310ff59..0000000
--- a/src/virtual-frame.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "v8.h"
-
-#include "codegen-inl.h"
-#include "register-allocator-inl.h"
-#include "virtual-frame-inl.h"
-
-namespace v8 {
-namespace internal {
-
-// -------------------------------------------------------------------------
-// VirtualFrame implementation.
-
-// Specialization of List::ResizeAdd to non-inlined version for FrameElements.
-// The function ResizeAdd becomes a real function, whose implementation is the
-// inlined ResizeAddInternal.
-template <>
-void List<FrameElement,
- FreeStoreAllocationPolicy>::ResizeAdd(const FrameElement& element) {
- ResizeAddInternal(element);
-}
-
-} } // namespace v8::internal
diff --git a/src/virtual-frame.h b/src/virtual-frame.h
deleted file mode 100644
index 65d1009..0000000
--- a/src/virtual-frame.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_VIRTUAL_FRAME_H_
-#define V8_VIRTUAL_FRAME_H_
-
-#include "frame-element.h"
-#include "macro-assembler.h"
-
-#include "list-inl.h"
-#include "utils.h"
-
-#if V8_TARGET_ARCH_IA32
-#include "ia32/virtual-frame-ia32.h"
-#elif V8_TARGET_ARCH_X64
-#include "x64/virtual-frame-x64.h"
-#elif V8_TARGET_ARCH_ARM
-#include "arm/virtual-frame-arm.h"
-#elif V8_TARGET_ARCH_MIPS
-#include "mips/virtual-frame-mips.h"
-#else
-#error Unsupported target architecture.
-#endif
-
-namespace v8 {
-namespace internal {
-
-// Add() on List is inlined, ResizeAdd() called by Add() is inlined except for
-// Lists of FrameElements, and ResizeAddInternal() is inlined in ResizeAdd().
-template <>
-void List<FrameElement,
- FreeStoreAllocationPolicy>::ResizeAdd(const FrameElement& element);
-} } // namespace v8::internal
-
-#endif // V8_VIRTUAL_FRAME_H_
diff --git a/src/x64/assembler-x64-inl.h b/src/x64/assembler-x64-inl.h
index 439236a..9541a58 100644
--- a/src/x64/assembler-x64-inl.h
+++ b/src/x64/assembler-x64-inl.h
@@ -393,9 +393,9 @@
StaticVisitor::VisitPointer(heap, target_object_address());
CPU::FlushICache(pc_, sizeof(Address));
} else if (RelocInfo::IsCodeTarget(mode)) {
- StaticVisitor::VisitCodeTarget(this);
+ StaticVisitor::VisitCodeTarget(heap, this);
} else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) {
- StaticVisitor::VisitGlobalPropertyCell(this);
+ StaticVisitor::VisitGlobalPropertyCell(heap, this);
} else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
StaticVisitor::VisitExternalReference(target_reference_address());
CPU::FlushICache(pc_, sizeof(Address));
@@ -405,7 +405,7 @@
IsPatchedReturnSequence()) ||
(RelocInfo::IsDebugBreakSlot(mode) &&
IsPatchedDebugBreakSlotSequence()))) {
- StaticVisitor::VisitDebugTarget(this);
+ StaticVisitor::VisitDebugTarget(heap, this);
#endif
} else if (mode == RelocInfo::RUNTIME_ENTRY) {
StaticVisitor::VisitRuntimeEntry(this);
diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc
index 0744b8a..c06bc0c 100644
--- a/src/x64/assembler-x64.cc
+++ b/src/x64/assembler-x64.cc
@@ -38,22 +38,38 @@
// -----------------------------------------------------------------------------
// Implementation of CpuFeatures
-CpuFeatures::CpuFeatures()
- : supported_(kDefaultCpuFeatures),
- enabled_(0),
- found_by_runtime_probing_(0) {
-}
+
+#ifdef DEBUG
+bool CpuFeatures::initialized_ = false;
+#endif
+uint64_t CpuFeatures::supported_ = CpuFeatures::kDefaultCpuFeatures;
+uint64_t CpuFeatures::found_by_runtime_probing_ = 0;
-void CpuFeatures::Probe(bool portable) {
- ASSERT(HEAP->HasBeenSetup());
+void CpuFeatures::Probe() {
+ ASSERT(!initialized_);
+#ifdef DEBUG
+ initialized_ = true;
+#endif
supported_ = kDefaultCpuFeatures;
- if (portable && Serializer::enabled()) {
+ if (Serializer::enabled()) {
supported_ |= OS::CpuFeaturesImpliedByPlatform();
return; // No features if we might serialize.
}
- Assembler assm(NULL, 0);
+ const int kBufferSize = 4 * KB;
+ VirtualMemory* memory = new VirtualMemory(kBufferSize);
+ if (!memory->IsReserved()) {
+ delete memory;
+ return;
+ }
+ ASSERT(memory->size() >= static_cast<size_t>(kBufferSize));
+ if (!memory->Commit(memory->address(), kBufferSize, true/*executable*/)) {
+ delete memory;
+ return;
+ }
+
+ Assembler assm(NULL, memory->address(), kBufferSize);
Label cpuid, done;
#define __ assm.
// Save old rsp, since we are going to modify the stack.
@@ -83,7 +99,7 @@
// ecx:edx. Temporarily enable CPUID support because we know it's
// safe here.
__ bind(&cpuid);
- __ movq(rax, Immediate(1));
+ __ movl(rax, Immediate(1));
supported_ = kDefaultCpuFeatures | (1 << CPUID);
{ Scope fscope(CPUID);
__ cpuid();
@@ -117,31 +133,20 @@
__ ret(0);
#undef __
- CodeDesc desc;
- assm.GetCode(&desc);
- Isolate* isolate = Isolate::Current();
- MaybeObject* maybe_code =
- isolate->heap()->CreateCode(desc,
- Code::ComputeFlags(Code::STUB),
- Handle<Object>());
- Object* code;
- if (!maybe_code->ToObject(&code)) return;
- if (!code->IsCode()) return;
- PROFILE(isolate,
- CodeCreateEvent(Logger::BUILTIN_TAG,
- Code::cast(code), "CpuFeatures::Probe"));
typedef uint64_t (*F0)();
- F0 probe = FUNCTION_CAST<F0>(Code::cast(code)->entry());
+ F0 probe = FUNCTION_CAST<F0>(reinterpret_cast<Address>(memory->address()));
supported_ = probe();
found_by_runtime_probing_ = supported_;
found_by_runtime_probing_ &= ~kDefaultCpuFeatures;
uint64_t os_guarantees = OS::CpuFeaturesImpliedByPlatform();
supported_ |= os_guarantees;
- found_by_runtime_probing_ &= portable ? ~os_guarantees : 0;
+ found_by_runtime_probing_ &= ~os_guarantees;
// SSE2 and CMOV must be available on an X64 CPU.
ASSERT(IsSupported(CPUID));
ASSERT(IsSupported(SSE2));
ASSERT(IsSupported(CMOV));
+
+ delete memory;
}
@@ -339,8 +344,8 @@
static void InitCoverageLog();
#endif
-Assembler::Assembler(void* buffer, int buffer_size)
- : AssemblerBase(Isolate::Current()),
+Assembler::Assembler(Isolate* arg_isolate, void* buffer, int buffer_size)
+ : AssemblerBase(arg_isolate),
code_targets_(100),
positions_recorder_(this),
emit_debug_code_(FLAG_debug_code) {
@@ -349,7 +354,7 @@
if (buffer_size <= kMinimalBufferSize) {
buffer_size = kMinimalBufferSize;
- if (isolate()->assembler_spare_buffer() != NULL) {
+ if (isolate() != NULL && isolate()->assembler_spare_buffer() != NULL) {
buffer = isolate()->assembler_spare_buffer();
isolate()->set_assembler_spare_buffer(NULL);
}
@@ -383,7 +388,6 @@
pc_ = buffer_;
reloc_info_writer.Reposition(buffer_ + buffer_size, pc_);
- last_pc_ = NULL;
#ifdef GENERATED_CODE_COVERAGE
InitCoverageLog();
@@ -393,7 +397,8 @@
Assembler::~Assembler() {
if (own_buffer_) {
- if (isolate()->assembler_spare_buffer() == NULL &&
+ if (isolate() != NULL &&
+ isolate()->assembler_spare_buffer() == NULL &&
buffer_size_ == kMinimalBufferSize) {
isolate()->set_assembler_spare_buffer(buffer_);
} else {
@@ -438,7 +443,6 @@
void Assembler::bind_to(Label* L, int pos) {
ASSERT(!L->is_bound()); // Label may only be bound once.
- last_pc_ = NULL;
ASSERT(0 <= pos && pos <= pc_offset()); // Position must be valid.
if (L->is_linked()) {
int current = L->pos();
@@ -465,7 +469,6 @@
void Assembler::bind(NearLabel* L) {
ASSERT(!L->is_bound());
- last_pc_ = NULL;
while (L->unresolved_branches_ > 0) {
int branch_pos = L->unresolved_positions_[L->unresolved_branches_ - 1];
int disp = pc_offset() - branch_pos;
@@ -516,7 +519,8 @@
reloc_info_writer.pos(), desc.reloc_size);
// Switch buffers.
- if (isolate()->assembler_spare_buffer() == NULL &&
+ if (isolate() != NULL &&
+ isolate()->assembler_spare_buffer() == NULL &&
buffer_size_ == kMinimalBufferSize) {
isolate()->set_assembler_spare_buffer(buffer_);
} else {
@@ -525,9 +529,6 @@
buffer_ = desc.buffer;
buffer_size_ = desc.buffer_size;
pc_ += pc_delta;
- if (last_pc_ != NULL) {
- last_pc_ += pc_delta;
- }
reloc_info_writer.Reposition(reloc_info_writer.pos() + rc_delta,
reloc_info_writer.last_pc() + pc_delta);
@@ -565,7 +566,6 @@
void Assembler::arithmetic_op(byte opcode, Register reg, const Operand& op) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(reg, op);
emit(opcode);
emit_operand(reg, op);
@@ -574,7 +574,6 @@
void Assembler::arithmetic_op(byte opcode, Register reg, Register rm_reg) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT((opcode & 0xC6) == 2);
if (rm_reg.low_bits() == 4) { // Forces SIB byte.
// Swap reg and rm_reg and change opcode operand order.
@@ -591,7 +590,6 @@
void Assembler::arithmetic_op_16(byte opcode, Register reg, Register rm_reg) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT((opcode & 0xC6) == 2);
if (rm_reg.low_bits() == 4) { // Forces SIB byte.
// Swap reg and rm_reg and change opcode operand order.
@@ -612,7 +610,6 @@
Register reg,
const Operand& rm_reg) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x66);
emit_optional_rex_32(reg, rm_reg);
emit(opcode);
@@ -622,7 +619,6 @@
void Assembler::arithmetic_op_32(byte opcode, Register reg, Register rm_reg) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT((opcode & 0xC6) == 2);
if (rm_reg.low_bits() == 4) { // Forces SIB byte.
// Swap reg and rm_reg and change opcode operand order.
@@ -641,7 +637,6 @@
Register reg,
const Operand& rm_reg) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(reg, rm_reg);
emit(opcode);
emit_operand(reg, rm_reg);
@@ -652,7 +647,6 @@
Register dst,
Immediate src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst);
if (is_int8(src.value_)) {
emit(0x83);
@@ -672,7 +666,6 @@
const Operand& dst,
Immediate src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst);
if (is_int8(src.value_)) {
emit(0x83);
@@ -690,7 +683,6 @@
Register dst,
Immediate src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x66); // Operand size override prefix.
emit_optional_rex_32(dst);
if (is_int8(src.value_)) {
@@ -712,7 +704,6 @@
const Operand& dst,
Immediate src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x66); // Operand size override prefix.
emit_optional_rex_32(dst);
if (is_int8(src.value_)) {
@@ -731,7 +722,6 @@
Register dst,
Immediate src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst);
if (is_int8(src.value_)) {
emit(0x83);
@@ -752,7 +742,6 @@
const Operand& dst,
Immediate src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst);
if (is_int8(src.value_)) {
emit(0x83);
@@ -770,7 +759,6 @@
const Operand& dst,
Immediate src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst);
ASSERT(is_int8(src.value_) || is_uint8(src.value_));
emit(0x80);
@@ -783,7 +771,6 @@
Register dst,
Immediate src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (dst.code() > 3) {
// Use 64-bit mode byte registers.
emit_rex_64(dst);
@@ -797,7 +784,6 @@
void Assembler::shift(Register dst, Immediate shift_amount, int subcode) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT(is_uint6(shift_amount.value_)); // illegal shift count
if (shift_amount.value_ == 1) {
emit_rex_64(dst);
@@ -814,7 +800,6 @@
void Assembler::shift(Register dst, int subcode) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst);
emit(0xD3);
emit_modrm(subcode, dst);
@@ -823,7 +808,6 @@
void Assembler::shift_32(Register dst, int subcode) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst);
emit(0xD3);
emit_modrm(subcode, dst);
@@ -832,7 +816,6 @@
void Assembler::shift_32(Register dst, Immediate shift_amount, int subcode) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT(is_uint5(shift_amount.value_)); // illegal shift count
if (shift_amount.value_ == 1) {
emit_optional_rex_32(dst);
@@ -849,7 +832,6 @@
void Assembler::bt(const Operand& dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(src, dst);
emit(0x0F);
emit(0xA3);
@@ -859,7 +841,6 @@
void Assembler::bts(const Operand& dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(src, dst);
emit(0x0F);
emit(0xAB);
@@ -870,7 +851,6 @@
void Assembler::call(Label* L) {
positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
// 1110 1000 #32-bit disp.
emit(0xE8);
if (L->is_bound()) {
@@ -892,7 +872,6 @@
void Assembler::call(Handle<Code> target, RelocInfo::Mode rmode) {
positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
// 1110 1000 #32-bit disp.
emit(0xE8);
emit_code_target(target, rmode);
@@ -902,7 +881,6 @@
void Assembler::call(Register adr) {
positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
// Opcode: FF /2 r64.
emit_optional_rex_32(adr);
emit(0xFF);
@@ -913,7 +891,6 @@
void Assembler::call(const Operand& op) {
positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
// Opcode: FF /2 m64.
emit_optional_rex_32(op);
emit(0xFF);
@@ -928,7 +905,6 @@
void Assembler::call(Address target) {
positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
// 1110 1000 #32-bit disp.
emit(0xE8);
Address source = pc_ + 4;
@@ -940,19 +916,16 @@
void Assembler::clc() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF8);
}
void Assembler::cld() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xFC);
}
void Assembler::cdq() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x99);
}
@@ -967,7 +940,6 @@
// 64-bit architecture.
ASSERT(cc >= 0); // Use mov for unconditional moves.
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
// Opcode: REX.W 0f 40 + cc /r.
emit_rex_64(dst, src);
emit(0x0f);
@@ -984,7 +956,6 @@
}
ASSERT(cc >= 0);
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
// Opcode: REX.W 0f 40 + cc /r.
emit_rex_64(dst, src);
emit(0x0f);
@@ -1001,7 +972,6 @@
}
ASSERT(cc >= 0);
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
// Opcode: 0f 40 + cc /r.
emit_optional_rex_32(dst, src);
emit(0x0f);
@@ -1018,7 +988,6 @@
}
ASSERT(cc >= 0);
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
// Opcode: 0f 40 + cc /r.
emit_optional_rex_32(dst, src);
emit(0x0f);
@@ -1030,16 +999,14 @@
void Assembler::cmpb_al(Immediate imm8) {
ASSERT(is_int8(imm8.value_) || is_uint8(imm8.value_));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x3c);
emit(imm8.value_);
}
void Assembler::cpuid() {
- ASSERT(isolate()->cpu_features()->IsEnabled(CPUID));
+ ASSERT(CpuFeatures::IsEnabled(CPUID));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x0F);
emit(0xA2);
}
@@ -1047,7 +1014,6 @@
void Assembler::cqo() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64();
emit(0x99);
}
@@ -1055,7 +1021,6 @@
void Assembler::decq(Register dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst);
emit(0xFF);
emit_modrm(0x1, dst);
@@ -1064,7 +1029,6 @@
void Assembler::decq(const Operand& dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst);
emit(0xFF);
emit_operand(1, dst);
@@ -1073,7 +1037,6 @@
void Assembler::decl(Register dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst);
emit(0xFF);
emit_modrm(0x1, dst);
@@ -1082,7 +1045,6 @@
void Assembler::decl(const Operand& dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst);
emit(0xFF);
emit_operand(1, dst);
@@ -1091,7 +1053,6 @@
void Assembler::decb(Register dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (dst.code() > 3) {
// Register is not one of al, bl, cl, dl. Its encoding needs REX.
emit_rex_32(dst);
@@ -1103,7 +1064,6 @@
void Assembler::decb(const Operand& dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst);
emit(0xFE);
emit_operand(1, dst);
@@ -1112,7 +1072,6 @@
void Assembler::enter(Immediate size) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xC8);
emitw(size.value_); // 16 bit operand, always.
emit(0);
@@ -1121,14 +1080,12 @@
void Assembler::hlt() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF4);
}
void Assembler::idivq(Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(src);
emit(0xF7);
emit_modrm(0x7, src);
@@ -1137,7 +1094,6 @@
void Assembler::idivl(Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(src);
emit(0xF7);
emit_modrm(0x7, src);
@@ -1146,7 +1102,6 @@
void Assembler::imul(Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(src);
emit(0xF7);
emit_modrm(0x5, src);
@@ -1155,7 +1110,6 @@
void Assembler::imul(Register dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst, src);
emit(0x0F);
emit(0xAF);
@@ -1165,7 +1119,6 @@
void Assembler::imul(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst, src);
emit(0x0F);
emit(0xAF);
@@ -1175,7 +1128,6 @@
void Assembler::imul(Register dst, Register src, Immediate imm) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst, src);
if (is_int8(imm.value_)) {
emit(0x6B);
@@ -1191,7 +1143,6 @@
void Assembler::imull(Register dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst, src);
emit(0x0F);
emit(0xAF);
@@ -1201,7 +1152,6 @@
void Assembler::imull(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst, src);
emit(0x0F);
emit(0xAF);
@@ -1211,7 +1161,6 @@
void Assembler::imull(Register dst, Register src, Immediate imm) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst, src);
if (is_int8(imm.value_)) {
emit(0x6B);
@@ -1227,7 +1176,6 @@
void Assembler::incq(Register dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst);
emit(0xFF);
emit_modrm(0x0, dst);
@@ -1236,7 +1184,6 @@
void Assembler::incq(const Operand& dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst);
emit(0xFF);
emit_operand(0, dst);
@@ -1245,7 +1192,6 @@
void Assembler::incl(const Operand& dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst);
emit(0xFF);
emit_operand(0, dst);
@@ -1254,7 +1200,6 @@
void Assembler::incl(Register dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst);
emit(0xFF);
emit_modrm(0, dst);
@@ -1263,7 +1208,6 @@
void Assembler::int3() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xCC);
}
@@ -1276,7 +1220,6 @@
return;
}
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT(is_uint4(cc));
if (L->is_bound()) {
const int short_size = 2;
@@ -1314,7 +1257,6 @@
Handle<Code> target,
RelocInfo::Mode rmode) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT(is_uint4(cc));
// 0000 1111 1000 tttn #32-bit disp.
emit(0x0F);
@@ -1325,7 +1267,6 @@
void Assembler::j(Condition cc, NearLabel* L, Hint hint) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT(0 <= cc && cc < 16);
if (FLAG_emit_branch_hints && hint != no_hint) emit(hint);
if (L->is_bound()) {
@@ -1346,7 +1287,6 @@
void Assembler::jmp(Label* L) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
const int short_size = sizeof(int8_t);
const int long_size = sizeof(int32_t);
if (L->is_bound()) {
@@ -1379,7 +1319,6 @@
void Assembler::jmp(Handle<Code> target, RelocInfo::Mode rmode) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
// 1110 1001 #32-bit disp.
emit(0xE9);
emit_code_target(target, rmode);
@@ -1388,7 +1327,6 @@
void Assembler::jmp(NearLabel* L) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (L->is_bound()) {
const int short_size = sizeof(int8_t);
int offs = L->pos() - pc_offset();
@@ -1407,7 +1345,6 @@
void Assembler::jmp(Register target) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
// Opcode FF/4 r64.
emit_optional_rex_32(target);
emit(0xFF);
@@ -1417,7 +1354,6 @@
void Assembler::jmp(const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
// Opcode FF/4 m64.
emit_optional_rex_32(src);
emit(0xFF);
@@ -1427,7 +1363,6 @@
void Assembler::lea(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst, src);
emit(0x8D);
emit_operand(dst, src);
@@ -1436,7 +1371,6 @@
void Assembler::leal(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst, src);
emit(0x8D);
emit_operand(dst, src);
@@ -1445,7 +1379,6 @@
void Assembler::load_rax(void* value, RelocInfo::Mode mode) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x48); // REX.W
emit(0xA1);
emitq(reinterpret_cast<uintptr_t>(value), mode);
@@ -1459,15 +1392,18 @@
void Assembler::leave() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xC9);
}
void Assembler::movb(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
- emit_rex_32(dst, src);
+ if (dst.code() > 3) {
+ // Register is not one of al, bl, cl, dl. Its encoding needs REX.
+ emit_rex_32(dst, src);
+ } else {
+ emit_optional_rex_32(dst, src);
+ }
emit(0x8A);
emit_operand(dst, src);
}
@@ -1475,18 +1411,21 @@
void Assembler::movb(Register dst, Immediate imm) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
- emit_rex_32(dst);
- emit(0xC6);
- emit_modrm(0x0, dst);
+ if (dst.code() > 3) {
+ emit_rex_32(dst);
+ }
+ emit(0xB0 + dst.low_bits());
emit(imm.value_);
}
void Assembler::movb(const Operand& dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
- emit_rex_32(src, dst);
+ if (src.code() > 3) {
+ emit_rex_32(src, dst);
+ } else {
+ emit_optional_rex_32(src, dst);
+ }
emit(0x88);
emit_operand(src, dst);
}
@@ -1494,7 +1433,6 @@
void Assembler::movw(const Operand& dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x66);
emit_optional_rex_32(src, dst);
emit(0x89);
@@ -1504,7 +1442,6 @@
void Assembler::movl(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst, src);
emit(0x8B);
emit_operand(dst, src);
@@ -1513,7 +1450,6 @@
void Assembler::movl(Register dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (src.low_bits() == 4) {
emit_optional_rex_32(src, dst);
emit(0x89);
@@ -1528,7 +1464,6 @@
void Assembler::movl(const Operand& dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(src, dst);
emit(0x89);
emit_operand(src, dst);
@@ -1537,27 +1472,23 @@
void Assembler::movl(const Operand& dst, Immediate value) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst);
emit(0xC7);
emit_operand(0x0, dst);
- emit(value); // Only 32-bit immediates are possible, not 8-bit immediates.
+ emit(value);
}
void Assembler::movl(Register dst, Immediate value) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst);
- emit(0xC7);
- emit_modrm(0x0, dst);
- emit(value); // Only 32-bit immediates are possible, not 8-bit immediates.
+ emit(0xB8 + dst.low_bits());
+ emit(value);
}
void Assembler::movq(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst, src);
emit(0x8B);
emit_operand(dst, src);
@@ -1566,7 +1497,6 @@
void Assembler::movq(Register dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (src.low_bits() == 4) {
emit_rex_64(src, dst);
emit(0x89);
@@ -1581,7 +1511,6 @@
void Assembler::movq(Register dst, Immediate value) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst);
emit(0xC7);
emit_modrm(0x0, dst);
@@ -1591,7 +1520,6 @@
void Assembler::movq(const Operand& dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(src, dst);
emit(0x89);
emit_operand(src, dst);
@@ -1603,7 +1531,6 @@
// address is not GC safe. Use the handle version instead.
ASSERT(rmode > RelocInfo::LAST_GCED_ENUM);
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst);
emit(0xB8 | dst.low_bits());
emitq(reinterpret_cast<uintptr_t>(value), rmode);
@@ -1625,7 +1552,6 @@
// value.
}
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst);
emit(0xB8 | dst.low_bits());
emitq(value, rmode);
@@ -1640,7 +1566,6 @@
void Assembler::movq(const Operand& dst, Immediate value) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst);
emit(0xC7);
emit_operand(0, dst);
@@ -1652,7 +1577,6 @@
// (as a 32-bit offset sign extended to 64-bit).
void Assembler::movl(const Operand& dst, Label* src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst);
emit(0xC7);
emit_operand(0, dst);
@@ -1682,7 +1606,6 @@
movq(dst, reinterpret_cast<int64_t>(*value), RelocInfo::NONE);
} else {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT(value->IsHeapObject());
ASSERT(!HEAP->InNewSpace(*value));
emit_rex_64(dst);
@@ -1694,7 +1617,6 @@
void Assembler::movsxbq(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst, src);
emit(0x0F);
emit(0xBE);
@@ -1704,7 +1626,6 @@
void Assembler::movsxwq(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst, src);
emit(0x0F);
emit(0xBF);
@@ -1714,7 +1635,6 @@
void Assembler::movsxlq(Register dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst, src);
emit(0x63);
emit_modrm(dst, src);
@@ -1723,7 +1643,6 @@
void Assembler::movsxlq(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst, src);
emit(0x63);
emit_operand(dst, src);
@@ -1732,7 +1651,6 @@
void Assembler::movzxbq(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst, src);
emit(0x0F);
emit(0xB6);
@@ -1742,7 +1660,6 @@
void Assembler::movzxbl(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst, src);
emit(0x0F);
emit(0xB6);
@@ -1752,7 +1669,6 @@
void Assembler::movzxwq(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst, src);
emit(0x0F);
emit(0xB7);
@@ -1762,7 +1678,6 @@
void Assembler::movzxwl(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst, src);
emit(0x0F);
emit(0xB7);
@@ -1772,7 +1687,6 @@
void Assembler::repmovsb() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF3);
emit(0xA4);
}
@@ -1780,7 +1694,6 @@
void Assembler::repmovsw() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x66); // Operand size override.
emit(0xF3);
emit(0xA4);
@@ -1789,7 +1702,6 @@
void Assembler::repmovsl() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF3);
emit(0xA5);
}
@@ -1797,7 +1709,6 @@
void Assembler::repmovsq() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF3);
emit_rex_64();
emit(0xA5);
@@ -1806,7 +1717,6 @@
void Assembler::mul(Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(src);
emit(0xF7);
emit_modrm(0x4, src);
@@ -1815,7 +1725,6 @@
void Assembler::neg(Register dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst);
emit(0xF7);
emit_modrm(0x3, dst);
@@ -1824,7 +1733,6 @@
void Assembler::negl(Register dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst);
emit(0xF7);
emit_modrm(0x3, dst);
@@ -1833,7 +1741,6 @@
void Assembler::neg(const Operand& dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst);
emit(0xF7);
emit_operand(3, dst);
@@ -1842,14 +1749,12 @@
void Assembler::nop() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x90);
}
void Assembler::not_(Register dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst);
emit(0xF7);
emit_modrm(0x2, dst);
@@ -1858,7 +1763,6 @@
void Assembler::not_(const Operand& dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(dst);
emit(0xF7);
emit_operand(2, dst);
@@ -1867,7 +1771,6 @@
void Assembler::notl(Register dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst);
emit(0xF7);
emit_modrm(0x2, dst);
@@ -1892,7 +1795,6 @@
ASSERT(1 <= n);
ASSERT(n <= 9);
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
switch (n) {
case 1:
emit(0x90);
@@ -1963,7 +1865,6 @@
void Assembler::pop(Register dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst);
emit(0x58 | dst.low_bits());
}
@@ -1971,7 +1872,6 @@
void Assembler::pop(const Operand& dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(dst);
emit(0x8F);
emit_operand(0, dst);
@@ -1980,14 +1880,12 @@
void Assembler::popfq() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x9D);
}
void Assembler::push(Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(src);
emit(0x50 | src.low_bits());
}
@@ -1995,7 +1893,6 @@
void Assembler::push(const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(src);
emit(0xFF);
emit_operand(6, src);
@@ -2004,7 +1901,6 @@
void Assembler::push(Immediate value) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (is_int8(value.value_)) {
emit(0x6A);
emit(value.value_); // Emit low byte of value.
@@ -2017,7 +1913,6 @@
void Assembler::push_imm32(int32_t imm32) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x68);
emitl(imm32);
}
@@ -2025,14 +1920,12 @@
void Assembler::pushfq() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x9C);
}
void Assembler::rdtsc() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x0F);
emit(0x31);
}
@@ -2040,7 +1933,6 @@
void Assembler::ret(int imm16) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT(is_uint16(imm16));
if (imm16 == 0) {
emit(0xC3);
@@ -2058,7 +1950,6 @@
return;
}
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT(is_uint4(cc));
if (reg.code() > 3) { // Use x64 byte registers, where different.
emit_rex_32(reg);
@@ -2071,7 +1962,6 @@
void Assembler::shld(Register dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(src, dst);
emit(0x0F);
emit(0xA5);
@@ -2081,7 +1971,6 @@
void Assembler::shrd(Register dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(src, dst);
emit(0x0F);
emit(0xAD);
@@ -2091,7 +1980,6 @@
void Assembler::xchg(Register dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (src.is(rax) || dst.is(rax)) { // Single-byte encoding
Register other = src.is(rax) ? dst : src;
emit_rex_64(other);
@@ -2110,7 +1998,6 @@
void Assembler::store_rax(void* dst, RelocInfo::Mode mode) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x48); // REX.W
emit(0xA3);
emitq(reinterpret_cast<uintptr_t>(dst), mode);
@@ -2124,7 +2011,6 @@
void Assembler::testb(Register dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (src.low_bits() == 4) {
emit_rex_32(src, dst);
emit(0x84);
@@ -2143,7 +2029,6 @@
void Assembler::testb(Register reg, Immediate mask) {
ASSERT(is_int8(mask.value_) || is_uint8(mask.value_));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (reg.is(rax)) {
emit(0xA8);
emit(mask.value_); // Low byte emitted.
@@ -2162,7 +2047,6 @@
void Assembler::testb(const Operand& op, Immediate mask) {
ASSERT(is_int8(mask.value_) || is_uint8(mask.value_));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(rax, op);
emit(0xF6);
emit_operand(rax, op); // Operation code 0
@@ -2172,7 +2056,6 @@
void Assembler::testb(const Operand& op, Register reg) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (reg.code() > 3) {
// Register is not one of al, bl, cl, dl. Its encoding needs REX.
emit_rex_32(reg, op);
@@ -2186,7 +2069,6 @@
void Assembler::testl(Register dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (src.low_bits() == 4) {
emit_optional_rex_32(src, dst);
emit(0x85);
@@ -2206,7 +2088,6 @@
return;
}
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (reg.is(rax)) {
emit(0xA9);
emit(mask);
@@ -2226,7 +2107,6 @@
return;
}
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(rax, op);
emit(0xF7);
emit_operand(rax, op); // Operation code 0
@@ -2236,7 +2116,6 @@
void Assembler::testq(const Operand& op, Register reg) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_rex_64(reg, op);
emit(0x85);
emit_operand(reg, op);
@@ -2245,7 +2124,6 @@
void Assembler::testq(Register dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (src.low_bits() == 4) {
emit_rex_64(src, dst);
emit(0x85);
@@ -2260,7 +2138,6 @@
void Assembler::testq(Register dst, Immediate mask) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (dst.is(rax)) {
emit_rex_64();
emit(0xA9);
@@ -2279,14 +2156,12 @@
void Assembler::fld(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xD9, 0xC0, i);
}
void Assembler::fld1() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xD9);
emit(0xE8);
}
@@ -2294,7 +2169,6 @@
void Assembler::fldz() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xD9);
emit(0xEE);
}
@@ -2302,7 +2176,6 @@
void Assembler::fldpi() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xD9);
emit(0xEB);
}
@@ -2310,7 +2183,6 @@
void Assembler::fldln2() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xD9);
emit(0xED);
}
@@ -2318,7 +2190,6 @@
void Assembler::fld_s(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(adr);
emit(0xD9);
emit_operand(0, adr);
@@ -2327,7 +2198,6 @@
void Assembler::fld_d(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(adr);
emit(0xDD);
emit_operand(0, adr);
@@ -2336,7 +2206,6 @@
void Assembler::fstp_s(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(adr);
emit(0xD9);
emit_operand(3, adr);
@@ -2345,7 +2214,6 @@
void Assembler::fstp_d(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(adr);
emit(0xDD);
emit_operand(3, adr);
@@ -2355,14 +2223,12 @@
void Assembler::fstp(int index) {
ASSERT(is_uint3(index));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDD, 0xD8, index);
}
void Assembler::fild_s(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(adr);
emit(0xDB);
emit_operand(0, adr);
@@ -2371,7 +2237,6 @@
void Assembler::fild_d(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(adr);
emit(0xDF);
emit_operand(5, adr);
@@ -2380,7 +2245,6 @@
void Assembler::fistp_s(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(adr);
emit(0xDB);
emit_operand(3, adr);
@@ -2388,9 +2252,8 @@
void Assembler::fisttp_s(const Operand& adr) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE3));
+ ASSERT(CpuFeatures::IsEnabled(SSE3));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(adr);
emit(0xDB);
emit_operand(1, adr);
@@ -2398,9 +2261,8 @@
void Assembler::fisttp_d(const Operand& adr) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE3));
+ ASSERT(CpuFeatures::IsEnabled(SSE3));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(adr);
emit(0xDD);
emit_operand(1, adr);
@@ -2409,7 +2271,6 @@
void Assembler::fist_s(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(adr);
emit(0xDB);
emit_operand(2, adr);
@@ -2418,7 +2279,6 @@
void Assembler::fistp_d(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(adr);
emit(0xDF);
emit_operand(7, adr);
@@ -2427,7 +2287,6 @@
void Assembler::fabs() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xD9);
emit(0xE1);
}
@@ -2435,7 +2294,6 @@
void Assembler::fchs() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xD9);
emit(0xE0);
}
@@ -2443,7 +2301,6 @@
void Assembler::fcos() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xD9);
emit(0xFF);
}
@@ -2451,7 +2308,6 @@
void Assembler::fsin() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xD9);
emit(0xFE);
}
@@ -2459,7 +2315,6 @@
void Assembler::fyl2x() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xD9);
emit(0xF1);
}
@@ -2467,21 +2322,18 @@
void Assembler::fadd(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDC, 0xC0, i);
}
void Assembler::fsub(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDC, 0xE8, i);
}
void Assembler::fisub_s(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_optional_rex_32(adr);
emit(0xDA);
emit_operand(4, adr);
@@ -2490,56 +2342,48 @@
void Assembler::fmul(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDC, 0xC8, i);
}
void Assembler::fdiv(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDC, 0xF8, i);
}
void Assembler::faddp(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDE, 0xC0, i);
}
void Assembler::fsubp(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDE, 0xE8, i);
}
void Assembler::fsubrp(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDE, 0xE0, i);
}
void Assembler::fmulp(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDE, 0xC8, i);
}
void Assembler::fdivp(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDE, 0xF8, i);
}
void Assembler::fprem() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xD9);
emit(0xF8);
}
@@ -2547,7 +2391,6 @@
void Assembler::fprem1() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xD9);
emit(0xF5);
}
@@ -2555,14 +2398,12 @@
void Assembler::fxch(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xD9, 0xC8, i);
}
void Assembler::fincstp() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xD9);
emit(0xF7);
}
@@ -2570,14 +2411,12 @@
void Assembler::ffree(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDD, 0xC0, i);
}
void Assembler::ftst() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xD9);
emit(0xE4);
}
@@ -2585,14 +2424,12 @@
void Assembler::fucomp(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDD, 0xE8, i);
}
void Assembler::fucompp() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xDA);
emit(0xE9);
}
@@ -2600,7 +2437,6 @@
void Assembler::fucomi(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xDB);
emit(0xE8 + i);
}
@@ -2608,7 +2444,6 @@
void Assembler::fucomip() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xDF);
emit(0xE9);
}
@@ -2616,7 +2451,6 @@
void Assembler::fcompp() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xDE);
emit(0xD9);
}
@@ -2624,7 +2458,6 @@
void Assembler::fnstsw_ax() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xDF);
emit(0xE0);
}
@@ -2632,14 +2465,12 @@
void Assembler::fwait() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x9B);
}
void Assembler::frndint() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xD9);
emit(0xFC);
}
@@ -2647,7 +2478,6 @@
void Assembler::fnclex() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xDB);
emit(0xE2);
}
@@ -2657,7 +2487,6 @@
// TODO(X64): Test for presence. Not all 64-bit intel CPU's have sahf
// in 64-bit mode. Test CpuID.
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x9E);
}
@@ -2673,7 +2502,6 @@
void Assembler::movd(XMMRegister dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x66);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2684,7 +2512,6 @@
void Assembler::movd(Register dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x66);
emit_optional_rex_32(src, dst);
emit(0x0F);
@@ -2695,7 +2522,6 @@
void Assembler::movq(XMMRegister dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x66);
emit_rex_64(dst, src);
emit(0x0F);
@@ -2706,7 +2532,6 @@
void Assembler::movq(Register dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x66);
emit_rex_64(src, dst);
emit(0x0F);
@@ -2715,10 +2540,26 @@
}
-void Assembler::movdqa(const Operand& dst, XMMRegister src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
+void Assembler::movq(XMMRegister dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
+ if (dst.low_bits() == 4) {
+ // Avoid unnecessary SIB byte.
+ emit(0xf3);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x7e);
+ emit_sse_operand(dst, src);
+ } else {
+ emit(0x66);
+ emit_optional_rex_32(src, dst);
+ emit(0x0F);
+ emit(0xD6);
+ emit_sse_operand(src, dst);
+ }
+}
+
+void Assembler::movdqa(const Operand& dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
emit(0x66);
emit_rex_64(src, dst);
emit(0x0F);
@@ -2728,9 +2569,7 @@
void Assembler::movdqa(XMMRegister dst, const Operand& src) {
- ASSERT(isolate()->cpu_features()->IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x66);
emit_rex_64(dst, src);
emit(0x0F);
@@ -2742,7 +2581,6 @@
void Assembler::extractps(Register dst, XMMRegister src, byte imm8) {
ASSERT(is_uint2(imm8));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x66);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2755,7 +2593,6 @@
void Assembler::movsd(const Operand& dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF2); // double
emit_optional_rex_32(src, dst);
emit(0x0F);
@@ -2766,7 +2603,6 @@
void Assembler::movsd(XMMRegister dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF2); // double
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2777,7 +2613,6 @@
void Assembler::movsd(XMMRegister dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF2); // double
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2786,9 +2621,44 @@
}
+void Assembler::movaps(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ if (src.low_bits() == 4) {
+ // Try to avoid an unnecessary SIB byte.
+ emit_optional_rex_32(src, dst);
+ emit(0x0F);
+ emit(0x29);
+ emit_sse_operand(src, dst);
+ } else {
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x28);
+ emit_sse_operand(dst, src);
+ }
+}
+
+
+void Assembler::movapd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ if (src.low_bits() == 4) {
+ // Try to avoid an unnecessary SIB byte.
+ emit(0x66);
+ emit_optional_rex_32(src, dst);
+ emit(0x0F);
+ emit(0x29);
+ emit_sse_operand(src, dst);
+ } else {
+ emit(0x66);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x28);
+ emit_sse_operand(dst, src);
+ }
+}
+
+
void Assembler::movss(XMMRegister dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF3); // single
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2799,7 +2669,6 @@
void Assembler::movss(const Operand& src, XMMRegister dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF3); // single
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2810,7 +2679,6 @@
void Assembler::cvttss2si(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF3);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2821,7 +2689,6 @@
void Assembler::cvttss2si(Register dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF3);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2832,7 +2699,6 @@
void Assembler::cvttsd2si(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF2);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2843,7 +2709,6 @@
void Assembler::cvttsd2si(Register dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF2);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2854,7 +2719,6 @@
void Assembler::cvttsd2siq(Register dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF2);
emit_rex_64(dst, src);
emit(0x0F);
@@ -2865,7 +2729,6 @@
void Assembler::cvtlsi2sd(XMMRegister dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF2);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2876,7 +2739,6 @@
void Assembler::cvtlsi2sd(XMMRegister dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF2);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2887,7 +2749,6 @@
void Assembler::cvtlsi2ss(XMMRegister dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF3);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2898,7 +2759,6 @@
void Assembler::cvtqsi2sd(XMMRegister dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF2);
emit_rex_64(dst, src);
emit(0x0F);
@@ -2909,7 +2769,6 @@
void Assembler::cvtss2sd(XMMRegister dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF3);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2920,7 +2779,6 @@
void Assembler::cvtss2sd(XMMRegister dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF3);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2931,7 +2789,6 @@
void Assembler::cvtsd2ss(XMMRegister dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF2);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2942,7 +2799,6 @@
void Assembler::cvtsd2si(Register dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF2);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2953,7 +2809,6 @@
void Assembler::cvtsd2siq(Register dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF2);
emit_rex_64(dst, src);
emit(0x0F);
@@ -2964,7 +2819,6 @@
void Assembler::addsd(XMMRegister dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF2);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2975,7 +2829,6 @@
void Assembler::mulsd(XMMRegister dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF2);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2986,7 +2839,6 @@
void Assembler::subsd(XMMRegister dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF2);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -2997,7 +2849,6 @@
void Assembler::divsd(XMMRegister dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF2);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -3008,7 +2859,6 @@
void Assembler::andpd(XMMRegister dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x66);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -3019,7 +2869,6 @@
void Assembler::orpd(XMMRegister dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x66);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -3030,7 +2879,6 @@
void Assembler::xorpd(XMMRegister dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x66);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -3039,9 +2887,17 @@
}
+void Assembler::xorps(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x57);
+ emit_sse_operand(dst, src);
+}
+
+
void Assembler::sqrtsd(XMMRegister dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0xF2);
emit_optional_rex_32(dst, src);
emit(0x0F);
@@ -3052,7 +2908,6 @@
void Assembler::ucomisd(XMMRegister dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x66);
emit_optional_rex_32(dst, src);
emit(0x0f);
@@ -3063,7 +2918,6 @@
void Assembler::ucomisd(XMMRegister dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x66);
emit_optional_rex_32(dst, src);
emit(0x0f);
@@ -3072,9 +2926,23 @@
}
+void Assembler::roundsd(XMMRegister dst, XMMRegister src,
+ Assembler::RoundingMode mode) {
+ ASSERT(CpuFeatures::IsEnabled(SSE4_1));
+ EnsureSpace ensure_space(this);
+ emit(0x66);
+ emit_optional_rex_32(dst, src);
+ emit(0x0f);
+ emit(0x3a);
+ emit(0x0b);
+ emit_sse_operand(dst, src);
+ // Mask precision exeption.
+ emit(static_cast<byte>(mode) | 0x8);
+}
+
+
void Assembler::movmskpd(Register dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit(0x66);
emit_optional_rex_32(dst, src);
emit(0x0f);
diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h
index 52aca63..8a9938b 100644
--- a/src/x64/assembler-x64.h
+++ b/src/x64/assembler-x64.h
@@ -434,14 +434,15 @@
// } else {
// // Generate standard x87 or SSE2 floating point code.
// }
-class CpuFeatures {
+class CpuFeatures : public AllStatic {
public:
// Detect features of the target CPU. Set safe defaults if the serializer
// is enabled (snapshots must be portable).
- void Probe(bool portable);
+ static void Probe();
// Check whether a feature is supported by the target CPU.
- bool IsSupported(CpuFeature f) const {
+ static bool IsSupported(CpuFeature f) {
+ ASSERT(initialized_);
if (f == SSE2 && !FLAG_enable_sse2) return false;
if (f == SSE3 && !FLAG_enable_sse3) return false;
if (f == CMOV && !FLAG_enable_cmov) return false;
@@ -449,51 +450,65 @@
if (f == SAHF && !FLAG_enable_sahf) return false;
return (supported_ & (V8_UINT64_C(1) << f)) != 0;
}
+
+#ifdef DEBUG
// Check whether a feature is currently enabled.
- bool IsEnabled(CpuFeature f) const {
- return (enabled_ & (V8_UINT64_C(1) << f)) != 0;
+ static bool IsEnabled(CpuFeature f) {
+ ASSERT(initialized_);
+ Isolate* isolate = Isolate::UncheckedCurrent();
+ if (isolate == NULL) {
+ // When no isolate is available, work as if we're running in
+ // release mode.
+ return IsSupported(f);
+ }
+ uint64_t enabled = isolate->enabled_cpu_features();
+ return (enabled & (V8_UINT64_C(1) << f)) != 0;
}
+#endif
+
// Enable a specified feature within a scope.
class Scope BASE_EMBEDDED {
#ifdef DEBUG
public:
- explicit Scope(CpuFeature f)
- : cpu_features_(Isolate::Current()->cpu_features()),
- isolate_(Isolate::Current()) {
- uint64_t mask = (V8_UINT64_C(1) << f);
- ASSERT(cpu_features_->IsSupported(f));
+ explicit Scope(CpuFeature f) {
+ uint64_t mask = V8_UINT64_C(1) << f;
+ ASSERT(CpuFeatures::IsSupported(f));
ASSERT(!Serializer::enabled() ||
- (cpu_features_->found_by_runtime_probing_ & mask) == 0);
- old_enabled_ = cpu_features_->enabled_;
- cpu_features_->enabled_ |= mask;
+ (CpuFeatures::found_by_runtime_probing_ & mask) == 0);
+ isolate_ = Isolate::UncheckedCurrent();
+ old_enabled_ = 0;
+ if (isolate_ != NULL) {
+ old_enabled_ = isolate_->enabled_cpu_features();
+ isolate_->set_enabled_cpu_features(old_enabled_ | mask);
+ }
}
~Scope() {
- ASSERT_EQ(Isolate::Current(), isolate_);
- cpu_features_->enabled_ = old_enabled_;
+ ASSERT_EQ(Isolate::UncheckedCurrent(), isolate_);
+ if (isolate_ != NULL) {
+ isolate_->set_enabled_cpu_features(old_enabled_);
+ }
}
private:
- uint64_t old_enabled_;
- CpuFeatures* cpu_features_;
Isolate* isolate_;
+ uint64_t old_enabled_;
#else
public:
explicit Scope(CpuFeature f) {}
#endif
};
- private:
- CpuFeatures();
+ private:
// Safe defaults include SSE2 and CMOV for X64. It is always available, if
// anyone checks, but they shouldn't need to check.
// The required user mode extensions in X64 are (from AMD64 ABI Table A.1):
// fpu, tsc, cx8, cmov, mmx, sse, sse2, fxsr, syscall
static const uint64_t kDefaultCpuFeatures = (1 << SSE2 | 1 << CMOV);
- uint64_t supported_;
- uint64_t enabled_;
- uint64_t found_by_runtime_probing_;
-
- friend class Isolate;
+#ifdef DEBUG
+ static bool initialized_;
+#endif
+ static uint64_t supported_;
+ static uint64_t found_by_runtime_probing_;
DISALLOW_COPY_AND_ASSIGN(CpuFeatures);
};
@@ -526,7 +541,7 @@
// for code generation and assumes its size to be buffer_size. If the buffer
// is too small, a fatal error occurs. No deallocation of the buffer is done
// upon destruction of the assembler.
- Assembler(void* buffer, int buffer_size);
+ Assembler(Isolate* isolate, void* buffer, int buffer_size);
~Assembler();
// Overrides the default provided by FLAG_debug_code.
@@ -1276,15 +1291,24 @@
void movd(Register dst, XMMRegister src);
void movq(XMMRegister dst, Register src);
void movq(Register dst, XMMRegister src);
+ void movq(XMMRegister dst, XMMRegister src);
void extractps(Register dst, XMMRegister src, byte imm8);
- void movsd(const Operand& dst, XMMRegister src);
+ // Don't use this unless it's important to keep the
+ // top half of the destination register unchanged.
+ // Used movaps when moving double values and movq for integer
+ // values in xmm registers.
void movsd(XMMRegister dst, XMMRegister src);
+
+ void movsd(const Operand& dst, XMMRegister src);
void movsd(XMMRegister dst, const Operand& src);
void movdqa(const Operand& dst, XMMRegister src);
void movdqa(XMMRegister dst, const Operand& src);
+ void movapd(XMMRegister dst, XMMRegister src);
+ void movaps(XMMRegister dst, XMMRegister src);
+
void movss(XMMRegister dst, const Operand& src);
void movss(const Operand& dst, XMMRegister src);
@@ -1316,11 +1340,21 @@
void andpd(XMMRegister dst, XMMRegister src);
void orpd(XMMRegister dst, XMMRegister src);
void xorpd(XMMRegister dst, XMMRegister src);
+ void xorps(XMMRegister dst, XMMRegister src);
void sqrtsd(XMMRegister dst, XMMRegister src);
void ucomisd(XMMRegister dst, XMMRegister src);
void ucomisd(XMMRegister dst, const Operand& src);
+ enum RoundingMode {
+ kRoundToNearest = 0x0,
+ kRoundDown = 0x1,
+ kRoundUp = 0x2,
+ kRoundToZero = 0x3
+ };
+
+ void roundsd(XMMRegister dst, XMMRegister src, RoundingMode mode);
+
void movmskpd(Register dst, XMMRegister src);
// The first argument is the reg field, the second argument is the r/m field.
@@ -1574,8 +1608,6 @@
RelocInfoWriter reloc_info_writer;
List< Handle<Code> > code_targets_;
- // push-pop elimination
- byte* last_pc_;
PositionsRecorder positions_recorder_;
diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc
index 21d3e54..a549633 100644
--- a/src/x64/builtins-x64.cc
+++ b/src/x64/builtins-x64.cc
@@ -29,7 +29,7 @@
#if defined(V8_TARGET_ARCH_X64)
-#include "codegen-inl.h"
+#include "codegen.h"
#include "deoptimizer.h"
#include "full-codegen.h"
@@ -96,7 +96,7 @@
// rax: number of arguments
__ bind(&non_function_call);
// Set expected number of arguments to zero (not changing rax).
- __ movq(rbx, Immediate(0));
+ __ Set(rbx, 0);
__ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
__ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
RelocInfo::CODE_TARGET);
@@ -1372,7 +1372,7 @@
// Copy receiver and all expected arguments.
const int offset = StandardFrameConstants::kCallerSPOffset;
__ lea(rax, Operand(rbp, rax, times_pointer_size, offset));
- __ movq(rcx, Immediate(-1)); // account for receiver
+ __ Set(rcx, -1); // account for receiver
Label copy;
__ bind(©);
@@ -1391,7 +1391,7 @@
// Copy receiver and all actual arguments.
const int offset = StandardFrameConstants::kCallerSPOffset;
__ lea(rdi, Operand(rbp, rax, times_pointer_size, offset));
- __ movq(rcx, Immediate(-1)); // account for receiver
+ __ Set(rcx, -1); // account for receiver
Label copy;
__ bind(©);
diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc
index 0fb827b..76fcc88 100644
--- a/src/x64/code-stubs-x64.cc
+++ b/src/x64/code-stubs-x64.cc
@@ -266,14 +266,14 @@
__ j(not_equal, &true_result);
// HeapNumber => false iff +0, -0, or NaN.
// These three cases set the zero flag when compared to zero using ucomisd.
- __ xorpd(xmm0, xmm0);
+ __ xorps(xmm0, xmm0);
__ ucomisd(xmm0, FieldOperand(rax, HeapNumber::kValueOffset));
__ j(zero, &false_result);
// Fall through to |true_result|.
// Return 1/0 for true/false in rax.
__ bind(&true_result);
- __ movq(rax, Immediate(1));
+ __ Set(rax, 1);
__ ret(1 * kPointerSize);
__ bind(&false_result);
__ Set(rax, 0);
@@ -281,166 +281,6 @@
}
-const char* GenericBinaryOpStub::GetName() {
- if (name_ != NULL) return name_;
- const int kMaxNameLength = 100;
- name_ = Isolate::Current()->bootstrapper()->AllocateAutoDeletedArray(
- kMaxNameLength);
- if (name_ == NULL) return "OOM";
- const char* op_name = Token::Name(op_);
- const char* overwrite_name;
- switch (mode_) {
- case NO_OVERWRITE: overwrite_name = "Alloc"; break;
- case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break;
- case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break;
- default: overwrite_name = "UnknownOverwrite"; break;
- }
-
- OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
- "GenericBinaryOpStub_%s_%s%s_%s%s_%s_%s",
- op_name,
- overwrite_name,
- (flags_ & NO_SMI_CODE_IN_STUB) ? "_NoSmiInStub" : "",
- args_in_registers_ ? "RegArgs" : "StackArgs",
- args_reversed_ ? "_R" : "",
- static_operands_type_.ToString(),
- BinaryOpIC::GetName(runtime_operands_type_));
- return name_;
-}
-
-
-void GenericBinaryOpStub::GenerateCall(
- MacroAssembler* masm,
- Register left,
- Register right) {
- if (!ArgsInRegistersSupported()) {
- // Pass arguments on the stack.
- __ push(left);
- __ push(right);
- } else {
- // The calling convention with registers is left in rdx and right in rax.
- Register left_arg = rdx;
- Register right_arg = rax;
- if (!(left.is(left_arg) && right.is(right_arg))) {
- if (left.is(right_arg) && right.is(left_arg)) {
- if (IsOperationCommutative()) {
- SetArgsReversed();
- } else {
- __ xchg(left, right);
- }
- } else if (left.is(left_arg)) {
- __ movq(right_arg, right);
- } else if (right.is(right_arg)) {
- __ movq(left_arg, left);
- } else if (left.is(right_arg)) {
- if (IsOperationCommutative()) {
- __ movq(left_arg, right);
- SetArgsReversed();
- } else {
- // Order of moves important to avoid destroying left argument.
- __ movq(left_arg, left);
- __ movq(right_arg, right);
- }
- } else if (right.is(left_arg)) {
- if (IsOperationCommutative()) {
- __ movq(right_arg, left);
- SetArgsReversed();
- } else {
- // Order of moves important to avoid destroying right argument.
- __ movq(right_arg, right);
- __ movq(left_arg, left);
- }
- } else {
- // Order of moves is not important.
- __ movq(left_arg, left);
- __ movq(right_arg, right);
- }
- }
-
- // Update flags to indicate that arguments are in registers.
- SetArgsInRegisters();
- Counters* counters = masm->isolate()->counters();
- __ IncrementCounter(counters->generic_binary_stub_calls_regs(), 1);
- }
-
- // Call the stub.
- __ CallStub(this);
-}
-
-
-void GenericBinaryOpStub::GenerateCall(
- MacroAssembler* masm,
- Register left,
- Smi* right) {
- if (!ArgsInRegistersSupported()) {
- // Pass arguments on the stack.
- __ push(left);
- __ Push(right);
- } else {
- // The calling convention with registers is left in rdx and right in rax.
- Register left_arg = rdx;
- Register right_arg = rax;
- if (left.is(left_arg)) {
- __ Move(right_arg, right);
- } else if (left.is(right_arg) && IsOperationCommutative()) {
- __ Move(left_arg, right);
- SetArgsReversed();
- } else {
- // For non-commutative operations, left and right_arg might be
- // the same register. Therefore, the order of the moves is
- // important here in order to not overwrite left before moving
- // it to left_arg.
- __ movq(left_arg, left);
- __ Move(right_arg, right);
- }
-
- // Update flags to indicate that arguments are in registers.
- SetArgsInRegisters();
- Counters* counters = masm->isolate()->counters();
- __ IncrementCounter(counters->generic_binary_stub_calls_regs(), 1);
- }
-
- // Call the stub.
- __ CallStub(this);
-}
-
-
-void GenericBinaryOpStub::GenerateCall(
- MacroAssembler* masm,
- Smi* left,
- Register right) {
- if (!ArgsInRegistersSupported()) {
- // Pass arguments on the stack.
- __ Push(left);
- __ push(right);
- } else {
- // The calling convention with registers is left in rdx and right in rax.
- Register left_arg = rdx;
- Register right_arg = rax;
- if (right.is(right_arg)) {
- __ Move(left_arg, left);
- } else if (right.is(left_arg) && IsOperationCommutative()) {
- __ Move(right_arg, left);
- SetArgsReversed();
- } else {
- // For non-commutative operations, right and left_arg might be
- // the same register. Therefore, the order of the moves is
- // important here in order to not overwrite right before moving
- // it to right_arg.
- __ movq(right_arg, right);
- __ Move(left_arg, left);
- }
- // Update flags to indicate that arguments are in registers.
- SetArgsInRegisters();
- Counters* counters = masm->isolate()->counters();
- __ IncrementCounter(counters->generic_binary_stub_calls_regs(), 1);
- }
-
- // Call the stub.
- __ CallStub(this);
-}
-
-
class FloatingPointHelper : public AllStatic {
public:
// Load the operands from rdx and rax into xmm0 and xmm1, as doubles.
@@ -460,561 +300,28 @@
// As above, but we know the operands to be numbers. In that case,
// conversion can't fail.
static void LoadNumbersAsIntegers(MacroAssembler* masm);
+
+ // Tries to convert two values to smis losslessly.
+ // This fails if either argument is not a Smi nor a HeapNumber,
+ // or if it's a HeapNumber with a value that can't be converted
+ // losslessly to a Smi. In that case, control transitions to the
+ // on_not_smis label.
+ // On success, either control goes to the on_success label (if one is
+ // provided), or it falls through at the end of the code (if on_success
+ // is NULL).
+ // On success, both first and second holds Smi tagged values.
+ // One of first or second must be non-Smi when entering.
+ static void NumbersToSmis(MacroAssembler* masm,
+ Register first,
+ Register second,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* on_success,
+ Label* on_not_smis);
};
-void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) {
- // 1. Move arguments into rdx, rax except for DIV and MOD, which need the
- // dividend in rax and rdx free for the division. Use rax, rbx for those.
- Comment load_comment(masm, "-- Load arguments");
- Register left = rdx;
- Register right = rax;
- if (op_ == Token::DIV || op_ == Token::MOD) {
- left = rax;
- right = rbx;
- if (HasArgsInRegisters()) {
- __ movq(rbx, rax);
- __ movq(rax, rdx);
- }
- }
- if (!HasArgsInRegisters()) {
- __ movq(right, Operand(rsp, 1 * kPointerSize));
- __ movq(left, Operand(rsp, 2 * kPointerSize));
- }
-
- Label not_smis;
- // 2. Smi check both operands.
- if (static_operands_type_.IsSmi()) {
- // Skip smi check if we know that both arguments are smis.
- if (FLAG_debug_code) {
- __ AbortIfNotSmi(left);
- __ AbortIfNotSmi(right);
- }
- if (op_ == Token::BIT_OR) {
- // Handle OR here, since we do extra smi-checking in the or code below.
- __ SmiOr(right, right, left);
- GenerateReturn(masm);
- return;
- }
- } else {
- if (op_ != Token::BIT_OR) {
- // Skip the check for OR as it is better combined with the
- // actual operation.
- Comment smi_check_comment(masm, "-- Smi check arguments");
- __ JumpIfNotBothSmi(left, right, ¬_smis);
- }
- }
-
- // 3. Operands are both smis (except for OR), perform the operation leaving
- // the result in rax and check the result if necessary.
- Comment perform_smi(masm, "-- Perform smi operation");
- Label use_fp_on_smis;
- switch (op_) {
- case Token::ADD: {
- ASSERT(right.is(rax));
- __ SmiAdd(right, right, left, &use_fp_on_smis); // ADD is commutative.
- break;
- }
-
- case Token::SUB: {
- __ SmiSub(left, left, right, &use_fp_on_smis);
- __ movq(rax, left);
- break;
- }
-
- case Token::MUL:
- ASSERT(right.is(rax));
- __ SmiMul(right, right, left, &use_fp_on_smis); // MUL is commutative.
- break;
-
- case Token::DIV:
- ASSERT(left.is(rax));
- __ SmiDiv(left, left, right, &use_fp_on_smis);
- break;
-
- case Token::MOD:
- ASSERT(left.is(rax));
- __ SmiMod(left, left, right, slow);
- break;
-
- case Token::BIT_OR:
- ASSERT(right.is(rax));
- __ movq(rcx, right); // Save the right operand.
- __ SmiOr(right, right, left); // BIT_OR is commutative.
- __ testb(right, Immediate(kSmiTagMask));
- __ j(not_zero, ¬_smis);
- break;
-
- case Token::BIT_AND:
- ASSERT(right.is(rax));
- __ SmiAnd(right, right, left); // BIT_AND is commutative.
- break;
-
- case Token::BIT_XOR:
- ASSERT(right.is(rax));
- __ SmiXor(right, right, left); // BIT_XOR is commutative.
- break;
-
- case Token::SHL:
- case Token::SHR:
- case Token::SAR:
- switch (op_) {
- case Token::SAR:
- __ SmiShiftArithmeticRight(left, left, right);
- break;
- case Token::SHR:
- __ SmiShiftLogicalRight(left, left, right, slow);
- break;
- case Token::SHL:
- __ SmiShiftLeft(left, left, right);
- break;
- default:
- UNREACHABLE();
- }
- __ movq(rax, left);
- break;
-
- default:
- UNREACHABLE();
- break;
- }
-
- // 4. Emit return of result in rax.
- GenerateReturn(masm);
-
- // 5. For some operations emit inline code to perform floating point
- // operations on known smis (e.g., if the result of the operation
- // overflowed the smi range).
- switch (op_) {
- case Token::ADD:
- case Token::SUB:
- case Token::MUL:
- case Token::DIV: {
- ASSERT(use_fp_on_smis.is_linked());
- __ bind(&use_fp_on_smis);
- if (op_ == Token::DIV) {
- __ movq(rdx, rax);
- __ movq(rax, rbx);
- }
- // left is rdx, right is rax.
- __ AllocateHeapNumber(rbx, rcx, slow);
- FloatingPointHelper::LoadSSE2SmiOperands(masm);
- switch (op_) {
- case Token::ADD: __ addsd(xmm0, xmm1); break;
- case Token::SUB: __ subsd(xmm0, xmm1); break;
- case Token::MUL: __ mulsd(xmm0, xmm1); break;
- case Token::DIV: __ divsd(xmm0, xmm1); break;
- default: UNREACHABLE();
- }
- __ movsd(FieldOperand(rbx, HeapNumber::kValueOffset), xmm0);
- __ movq(rax, rbx);
- GenerateReturn(masm);
- }
- default:
- break;
- }
-
- // 6. Non-smi operands, fall out to the non-smi code with the operands in
- // rdx and rax.
- Comment done_comment(masm, "-- Enter non-smi code");
- __ bind(¬_smis);
-
- switch (op_) {
- case Token::DIV:
- case Token::MOD:
- // Operands are in rax, rbx at this point.
- __ movq(rdx, rax);
- __ movq(rax, rbx);
- break;
-
- case Token::BIT_OR:
- // Right operand is saved in rcx and rax was destroyed by the smi
- // operation.
- __ movq(rax, rcx);
- break;
-
- default:
- break;
- }
-}
-
-
-void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
- Label call_runtime;
-
- if (ShouldGenerateSmiCode()) {
- GenerateSmiCode(masm, &call_runtime);
- } else if (op_ != Token::MOD) {
- if (!HasArgsInRegisters()) {
- GenerateLoadArguments(masm);
- }
- }
- // Floating point case.
- if (ShouldGenerateFPCode()) {
- switch (op_) {
- case Token::ADD:
- case Token::SUB:
- case Token::MUL:
- case Token::DIV: {
- if (runtime_operands_type_ == BinaryOpIC::DEFAULT &&
- HasSmiCodeInStub()) {
- // Execution reaches this point when the first non-smi argument occurs
- // (and only if smi code is generated). This is the right moment to
- // patch to HEAP_NUMBERS state. The transition is attempted only for
- // the four basic operations. The stub stays in the DEFAULT state
- // forever for all other operations (also if smi code is skipped).
- GenerateTypeTransition(masm);
- break;
- }
-
- Label not_floats;
- // rax: y
- // rdx: x
- if (static_operands_type_.IsNumber()) {
- if (FLAG_debug_code) {
- // Assert at runtime that inputs are only numbers.
- __ AbortIfNotNumber(rdx);
- __ AbortIfNotNumber(rax);
- }
- FloatingPointHelper::LoadSSE2NumberOperands(masm);
- } else {
- FloatingPointHelper::LoadSSE2UnknownOperands(masm, &call_runtime);
- }
-
- switch (op_) {
- case Token::ADD: __ addsd(xmm0, xmm1); break;
- case Token::SUB: __ subsd(xmm0, xmm1); break;
- case Token::MUL: __ mulsd(xmm0, xmm1); break;
- case Token::DIV: __ divsd(xmm0, xmm1); break;
- default: UNREACHABLE();
- }
- // Allocate a heap number, if needed.
- Label skip_allocation;
- OverwriteMode mode = mode_;
- if (HasArgsReversed()) {
- if (mode == OVERWRITE_RIGHT) {
- mode = OVERWRITE_LEFT;
- } else if (mode == OVERWRITE_LEFT) {
- mode = OVERWRITE_RIGHT;
- }
- }
- switch (mode) {
- case OVERWRITE_LEFT:
- __ JumpIfNotSmi(rdx, &skip_allocation);
- __ AllocateHeapNumber(rbx, rcx, &call_runtime);
- __ movq(rdx, rbx);
- __ bind(&skip_allocation);
- __ movq(rax, rdx);
- break;
- case OVERWRITE_RIGHT:
- // If the argument in rax is already an object, we skip the
- // allocation of a heap number.
- __ JumpIfNotSmi(rax, &skip_allocation);
- // Fall through!
- case NO_OVERWRITE:
- // Allocate a heap number for the result. Keep rax and rdx intact
- // for the possible runtime call.
- __ AllocateHeapNumber(rbx, rcx, &call_runtime);
- __ movq(rax, rbx);
- __ bind(&skip_allocation);
- break;
- default: UNREACHABLE();
- }
- __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm0);
- GenerateReturn(masm);
- __ bind(¬_floats);
- if (runtime_operands_type_ == BinaryOpIC::DEFAULT &&
- !HasSmiCodeInStub()) {
- // Execution reaches this point when the first non-number argument
- // occurs (and only if smi code is skipped from the stub, otherwise
- // the patching has already been done earlier in this case branch).
- // A perfect moment to try patching to STRINGS for ADD operation.
- if (op_ == Token::ADD) {
- GenerateTypeTransition(masm);
- }
- }
- break;
- }
- case Token::MOD: {
- // For MOD we go directly to runtime in the non-smi case.
- break;
- }
- case Token::BIT_OR:
- case Token::BIT_AND:
- case Token::BIT_XOR:
- case Token::SAR:
- case Token::SHL:
- case Token::SHR: {
- Label skip_allocation, non_smi_shr_result;
- Register heap_number_map = r9;
- __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
- if (static_operands_type_.IsNumber()) {
- if (FLAG_debug_code) {
- // Assert at runtime that inputs are only numbers.
- __ AbortIfNotNumber(rdx);
- __ AbortIfNotNumber(rax);
- }
- FloatingPointHelper::LoadNumbersAsIntegers(masm);
- } else {
- FloatingPointHelper::LoadAsIntegers(masm,
- &call_runtime,
- heap_number_map);
- }
- switch (op_) {
- case Token::BIT_OR: __ orl(rax, rcx); break;
- case Token::BIT_AND: __ andl(rax, rcx); break;
- case Token::BIT_XOR: __ xorl(rax, rcx); break;
- case Token::SAR: __ sarl_cl(rax); break;
- case Token::SHL: __ shll_cl(rax); break;
- case Token::SHR: {
- __ shrl_cl(rax);
- // Check if result is negative. This can only happen for a shift
- // by zero.
- __ testl(rax, rax);
- __ j(negative, &non_smi_shr_result);
- break;
- }
- default: UNREACHABLE();
- }
-
- STATIC_ASSERT(kSmiValueSize == 32);
- // Tag smi result and return.
- __ Integer32ToSmi(rax, rax);
- GenerateReturn(masm);
-
- // All bit-ops except SHR return a signed int32 that can be
- // returned immediately as a smi.
- // We might need to allocate a HeapNumber if we shift a negative
- // number right by zero (i.e., convert to UInt32).
- if (op_ == Token::SHR) {
- ASSERT(non_smi_shr_result.is_linked());
- __ bind(&non_smi_shr_result);
- // Allocate a heap number if needed.
- __ movl(rbx, rax); // rbx holds result value (uint32 value as int64).
- switch (mode_) {
- case OVERWRITE_LEFT:
- case OVERWRITE_RIGHT:
- // If the operand was an object, we skip the
- // allocation of a heap number.
- __ movq(rax, Operand(rsp, mode_ == OVERWRITE_RIGHT ?
- 1 * kPointerSize : 2 * kPointerSize));
- __ JumpIfNotSmi(rax, &skip_allocation);
- // Fall through!
- case NO_OVERWRITE:
- // Allocate heap number in new space.
- // Not using AllocateHeapNumber macro in order to reuse
- // already loaded heap_number_map.
- __ AllocateInNewSpace(HeapNumber::kSize,
- rax,
- rcx,
- no_reg,
- &call_runtime,
- TAG_OBJECT);
- // Set the map.
- if (FLAG_debug_code) {
- __ AbortIfNotRootValue(heap_number_map,
- Heap::kHeapNumberMapRootIndex,
- "HeapNumberMap register clobbered.");
- }
- __ movq(FieldOperand(rax, HeapObject::kMapOffset),
- heap_number_map);
- __ bind(&skip_allocation);
- break;
- default: UNREACHABLE();
- }
- // Store the result in the HeapNumber and return.
- __ cvtqsi2sd(xmm0, rbx);
- __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm0);
- GenerateReturn(masm);
- }
-
- break;
- }
- default: UNREACHABLE(); break;
- }
- }
-
- // If all else fails, use the runtime system to get the correct
- // result. If arguments was passed in registers now place them on the
- // stack in the correct order below the return address.
- __ bind(&call_runtime);
-
- if (HasArgsInRegisters()) {
- GenerateRegisterArgsPush(masm);
- }
-
- switch (op_) {
- case Token::ADD: {
- // Registers containing left and right operands respectively.
- Register lhs, rhs;
-
- if (HasArgsReversed()) {
- lhs = rax;
- rhs = rdx;
- } else {
- lhs = rdx;
- rhs = rax;
- }
-
- // Test for string arguments before calling runtime.
- Label not_strings, both_strings, not_string1, string1, string1_smi2;
-
- // If this stub has already generated FP-specific code then the arguments
- // are already in rdx and rax.
- if (!ShouldGenerateFPCode() && !HasArgsInRegisters()) {
- GenerateLoadArguments(masm);
- }
-
- Condition is_smi;
- is_smi = masm->CheckSmi(lhs);
- __ j(is_smi, ¬_string1);
- __ CmpObjectType(lhs, FIRST_NONSTRING_TYPE, r8);
- __ j(above_equal, ¬_string1);
-
- // First argument is a a string, test second.
- is_smi = masm->CheckSmi(rhs);
- __ j(is_smi, &string1_smi2);
- __ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, r9);
- __ j(above_equal, &string1);
-
- // First and second argument are strings.
- StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
- __ TailCallStub(&string_add_stub);
-
- __ bind(&string1_smi2);
- // First argument is a string, second is a smi. Try to lookup the number
- // string for the smi in the number string cache.
- NumberToStringStub::GenerateLookupNumberStringCache(
- masm, rhs, rbx, rcx, r8, true, &string1);
-
- // Replace second argument on stack and tailcall string add stub to make
- // the result.
- __ movq(Operand(rsp, 1 * kPointerSize), rbx);
- __ TailCallStub(&string_add_stub);
-
- // Only first argument is a string.
- __ bind(&string1);
- __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_FUNCTION);
-
- // First argument was not a string, test second.
- __ bind(¬_string1);
- is_smi = masm->CheckSmi(rhs);
- __ j(is_smi, ¬_strings);
- __ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, rhs);
- __ j(above_equal, ¬_strings);
-
- // Only second argument is a string.
- __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_FUNCTION);
-
- __ bind(¬_strings);
- // Neither argument is a string.
- __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION);
- break;
- }
- case Token::SUB:
- __ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION);
- break;
- case Token::MUL:
- __ InvokeBuiltin(Builtins::MUL, JUMP_FUNCTION);
- break;
- case Token::DIV:
- __ InvokeBuiltin(Builtins::DIV, JUMP_FUNCTION);
- break;
- case Token::MOD:
- __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
- break;
- case Token::BIT_OR:
- __ InvokeBuiltin(Builtins::BIT_OR, JUMP_FUNCTION);
- break;
- case Token::BIT_AND:
- __ InvokeBuiltin(Builtins::BIT_AND, JUMP_FUNCTION);
- break;
- case Token::BIT_XOR:
- __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_FUNCTION);
- break;
- case Token::SAR:
- __ InvokeBuiltin(Builtins::SAR, JUMP_FUNCTION);
- break;
- case Token::SHL:
- __ InvokeBuiltin(Builtins::SHL, JUMP_FUNCTION);
- break;
- case Token::SHR:
- __ InvokeBuiltin(Builtins::SHR, JUMP_FUNCTION);
- break;
- default:
- UNREACHABLE();
- }
-}
-
-
-void GenericBinaryOpStub::GenerateLoadArguments(MacroAssembler* masm) {
- ASSERT(!HasArgsInRegisters());
- __ movq(rax, Operand(rsp, 1 * kPointerSize));
- __ movq(rdx, Operand(rsp, 2 * kPointerSize));
-}
-
-
-void GenericBinaryOpStub::GenerateReturn(MacroAssembler* masm) {
- // If arguments are not passed in registers remove them from the stack before
- // returning.
- if (!HasArgsInRegisters()) {
- __ ret(2 * kPointerSize); // Remove both operands
- } else {
- __ ret(0);
- }
-}
-
-
-void GenericBinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
- ASSERT(HasArgsInRegisters());
- __ pop(rcx);
- if (HasArgsReversed()) {
- __ push(rax);
- __ push(rdx);
- } else {
- __ push(rdx);
- __ push(rax);
- }
- __ push(rcx);
-}
-
-
-void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
- Label get_result;
-
- // Ensure the operands are on the stack.
- if (HasArgsInRegisters()) {
- GenerateRegisterArgsPush(masm);
- }
-
- // Left and right arguments are already on stack.
- __ pop(rcx); // Save the return address.
-
- // Push this stub's key.
- __ Push(Smi::FromInt(MinorKey()));
-
- // Although the operation and the type info are encoded into the key,
- // the encoding is opaque, so push them too.
- __ Push(Smi::FromInt(op_));
-
- __ Push(Smi::FromInt(runtime_operands_type_));
-
- __ push(rcx); // The return address.
-
- // Perform patching to an appropriate fast case and return the result.
- __ TailCallExternalReference(
- ExternalReference(IC_Utility(IC::kBinaryOp_Patch), masm->isolate()),
- 5,
- 1);
-}
-
-
-Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
- GenericBinaryOpStub stub(key, type_info);
- return stub.GetCode();
-}
-
-
Handle<Code> GetTypeRecordingBinaryOpStub(int key,
TRBinaryOpIC::TypeInfo type_info,
TRBinaryOpIC::TypeInfo result_type_info) {
@@ -1065,6 +372,9 @@
case TRBinaryOpIC::ODDBALL:
GenerateOddballStub(masm);
break;
+ case TRBinaryOpIC::BOTH_STRING:
+ GenerateBothStringStub(masm);
+ break;
case TRBinaryOpIC::STRING:
GenerateStringStub(masm);
break;
@@ -1105,29 +415,30 @@
Label* slow,
SmiCodeGenerateHeapNumberResults allow_heapnumber_results) {
- // We only generate heapnumber answers for overflowing calculations
- // for the four basic arithmetic operations.
- bool generate_inline_heapnumber_results =
- (allow_heapnumber_results == ALLOW_HEAPNUMBER_RESULTS) &&
- (op_ == Token::ADD || op_ == Token::SUB ||
- op_ == Token::MUL || op_ == Token::DIV);
-
// Arguments to TypeRecordingBinaryOpStub are in rdx and rax.
Register left = rdx;
Register right = rax;
+ // We only generate heapnumber answers for overflowing calculations
+ // for the four basic arithmetic operations and logical right shift by 0.
+ bool generate_inline_heapnumber_results =
+ (allow_heapnumber_results == ALLOW_HEAPNUMBER_RESULTS) &&
+ (op_ == Token::ADD || op_ == Token::SUB ||
+ op_ == Token::MUL || op_ == Token::DIV || op_ == Token::SHR);
// Smi check of both operands. If op is BIT_OR, the check is delayed
// until after the OR operation.
Label not_smis;
Label use_fp_on_smis;
- Label restore_MOD_registers; // Only used if op_ == Token::MOD.
+ Label fail;
if (op_ != Token::BIT_OR) {
Comment smi_check_comment(masm, "-- Smi check arguments");
__ JumpIfNotBothSmi(left, right, ¬_smis);
}
+ Label smi_values;
+ __ bind(&smi_values);
// Perform the operation.
Comment perform_smi(masm, "-- Perform smi operation");
switch (op_) {
@@ -1166,9 +477,7 @@
case Token::BIT_OR: {
ASSERT(right.is(rax));
- __ movq(rcx, right); // Save the right operand.
- __ SmiOr(right, right, left); // BIT_OR is commutative.
- __ JumpIfNotSmi(right, ¬_smis); // Test delayed until after BIT_OR.
+ __ SmiOrIfSmis(right, right, left, ¬_smis); // BIT_OR is commutative.
break;
}
case Token::BIT_XOR:
@@ -1192,7 +501,7 @@
break;
case Token::SHR:
- __ SmiShiftLogicalRight(left, left, right, ¬_smis);
+ __ SmiShiftLogicalRight(left, left, right, &use_fp_on_smis);
__ movq(rax, left);
break;
@@ -1203,41 +512,52 @@
// 5. Emit return of result in rax. Some operations have registers pushed.
__ ret(0);
- // 6. For some operations emit inline code to perform floating point
- // operations on known smis (e.g., if the result of the operation
- // overflowed the smi range).
- __ bind(&use_fp_on_smis);
- if (op_ == Token::DIV || op_ == Token::MOD) {
- // Restore left and right to rdx and rax.
- __ movq(rdx, rcx);
- __ movq(rax, rbx);
- }
-
-
- if (generate_inline_heapnumber_results) {
- __ AllocateHeapNumber(rcx, rbx, slow);
- Comment perform_float(masm, "-- Perform float operation on smis");
- FloatingPointHelper::LoadSSE2SmiOperands(masm);
- switch (op_) {
- case Token::ADD: __ addsd(xmm0, xmm1); break;
- case Token::SUB: __ subsd(xmm0, xmm1); break;
- case Token::MUL: __ mulsd(xmm0, xmm1); break;
- case Token::DIV: __ divsd(xmm0, xmm1); break;
- default: UNREACHABLE();
+ if (use_fp_on_smis.is_linked()) {
+ // 6. For some operations emit inline code to perform floating point
+ // operations on known smis (e.g., if the result of the operation
+ // overflowed the smi range).
+ __ bind(&use_fp_on_smis);
+ if (op_ == Token::DIV || op_ == Token::MOD) {
+ // Restore left and right to rdx and rax.
+ __ movq(rdx, rcx);
+ __ movq(rax, rbx);
}
- __ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm0);
- __ movq(rax, rcx);
- __ ret(0);
+
+ if (generate_inline_heapnumber_results) {
+ __ AllocateHeapNumber(rcx, rbx, slow);
+ Comment perform_float(masm, "-- Perform float operation on smis");
+ if (op_ == Token::SHR) {
+ __ SmiToInteger32(left, left);
+ __ cvtqsi2sd(xmm0, left);
+ } else {
+ FloatingPointHelper::LoadSSE2SmiOperands(masm);
+ switch (op_) {
+ case Token::ADD: __ addsd(xmm0, xmm1); break;
+ case Token::SUB: __ subsd(xmm0, xmm1); break;
+ case Token::MUL: __ mulsd(xmm0, xmm1); break;
+ case Token::DIV: __ divsd(xmm0, xmm1); break;
+ default: UNREACHABLE();
+ }
+ }
+ __ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm0);
+ __ movq(rax, rcx);
+ __ ret(0);
+ } else {
+ __ jmp(&fail);
+ }
}
// 7. Non-smi operands reach the end of the code generated by
// GenerateSmiCode, and fall through to subsequent code,
// with the operands in rdx and rax.
- Comment done_comment(masm, "-- Enter non-smi code");
+ // But first we check if non-smi values are HeapNumbers holding
+ // values that could be smi.
__ bind(¬_smis);
- if (op_ == Token::BIT_OR) {
- __ movq(right, rcx);
- }
+ Comment done_comment(masm, "-- Enter non-smi code");
+ FloatingPointHelper::NumbersToSmis(masm, left, right, rbx, rdi, rcx,
+ &smi_values, &fail);
+ __ jmp(&smi_values);
+ __ bind(&fail);
}
@@ -1422,12 +742,25 @@
void TypeRecordingBinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
- Label not_smi;
+ Label call_runtime;
+ if (result_type_ == TRBinaryOpIC::UNINITIALIZED ||
+ result_type_ == TRBinaryOpIC::SMI) {
+ // Only allow smi results.
+ GenerateSmiCode(masm, NULL, NO_HEAPNUMBER_RESULTS);
+ } else {
+ // Allow heap number result and don't make a transition if a heap number
+ // cannot be allocated.
+ GenerateSmiCode(masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS);
+ }
- GenerateSmiCode(masm, ¬_smi, NO_HEAPNUMBER_RESULTS);
-
- __ bind(¬_smi);
+ // Code falls through if the result is not returned as either a smi or heap
+ // number.
GenerateTypeTransition(masm);
+
+ if (call_runtime.is_linked()) {
+ __ bind(&call_runtime);
+ GenerateCallRuntimeCode(masm);
+ }
}
@@ -1441,6 +774,36 @@
}
+void TypeRecordingBinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
+ Label call_runtime;
+ ASSERT(operands_type_ == TRBinaryOpIC::BOTH_STRING);
+ ASSERT(op_ == Token::ADD);
+ // If both arguments are strings, call the string add stub.
+ // Otherwise, do a transition.
+
+ // Registers containing left and right operands respectively.
+ Register left = rdx;
+ Register right = rax;
+
+ // Test if left operand is a string.
+ __ JumpIfSmi(left, &call_runtime);
+ __ CmpObjectType(left, FIRST_NONSTRING_TYPE, rcx);
+ __ j(above_equal, &call_runtime);
+
+ // Test if right operand is a string.
+ __ JumpIfSmi(right, &call_runtime);
+ __ CmpObjectType(right, FIRST_NONSTRING_TYPE, rcx);
+ __ j(above_equal, &call_runtime);
+
+ StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_stub);
+
+ __ bind(&call_runtime);
+ GenerateTypeTransition(masm);
+}
+
+
void TypeRecordingBinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
Label call_runtime;
@@ -1951,7 +1314,7 @@
__ bind(&check_undefined_arg1);
__ CompareRoot(rdx, Heap::kUndefinedValueRootIndex);
__ j(not_equal, conversion_failure);
- __ movl(r8, Immediate(0));
+ __ Set(r8, 0);
__ jmp(&load_arg2);
__ bind(&arg1_is_object);
@@ -1971,7 +1334,7 @@
__ bind(&check_undefined_arg2);
__ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
__ j(not_equal, conversion_failure);
- __ movl(rcx, Immediate(0));
+ __ Set(rcx, 0);
__ jmp(&done);
__ bind(&arg2_is_object);
@@ -2046,6 +1409,62 @@
}
+void FloatingPointHelper::NumbersToSmis(MacroAssembler* masm,
+ Register first,
+ Register second,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* on_success,
+ Label* on_not_smis) {
+ Register heap_number_map = scratch3;
+ Register smi_result = scratch1;
+ Label done;
+
+ __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
+
+ NearLabel first_smi, check_second;
+ __ JumpIfSmi(first, &first_smi);
+ __ cmpq(FieldOperand(first, HeapObject::kMapOffset), heap_number_map);
+ __ j(not_equal, on_not_smis);
+ // Convert HeapNumber to smi if possible.
+ __ movsd(xmm0, FieldOperand(first, HeapNumber::kValueOffset));
+ __ movq(scratch2, xmm0);
+ __ cvttsd2siq(smi_result, xmm0);
+ // Check if conversion was successful by converting back and
+ // comparing to the original double's bits.
+ __ cvtlsi2sd(xmm1, smi_result);
+ __ movq(kScratchRegister, xmm1);
+ __ cmpq(scratch2, kScratchRegister);
+ __ j(not_equal, on_not_smis);
+ __ Integer32ToSmi(first, smi_result);
+
+ __ bind(&check_second);
+ __ JumpIfSmi(second, (on_success != NULL) ? on_success : &done);
+ __ bind(&first_smi);
+ if (FLAG_debug_code) {
+ // Second should be non-smi if we get here.
+ __ AbortIfSmi(second);
+ }
+ __ cmpq(FieldOperand(second, HeapObject::kMapOffset), heap_number_map);
+ __ j(not_equal, on_not_smis);
+ // Convert second to smi, if possible.
+ __ movsd(xmm0, FieldOperand(second, HeapNumber::kValueOffset));
+ __ movq(scratch2, xmm0);
+ __ cvttsd2siq(smi_result, xmm0);
+ __ cvtlsi2sd(xmm1, smi_result);
+ __ movq(kScratchRegister, xmm1);
+ __ cmpq(scratch2, kScratchRegister);
+ __ j(not_equal, on_not_smis);
+ __ Integer32ToSmi(second, smi_result);
+ if (on_success != NULL) {
+ __ jmp(on_success);
+ } else {
+ __ bind(&done);
+ }
+}
+
+
void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
Label slow, done;
@@ -2072,7 +1491,7 @@
__ j(not_equal, &slow);
// Operand is a float, negate its value by flipping sign bit.
__ movq(rdx, FieldOperand(rax, HeapNumber::kValueOffset));
- __ movq(kScratchRegister, Immediate(0x01));
+ __ Set(kScratchRegister, 0x01);
__ shl(kScratchRegister, Immediate(63));
__ xor_(rdx, kScratchRegister); // Flip sign.
// rdx is value to store.
@@ -2144,7 +1563,7 @@
__ movq(rax, Operand(rsp, 1 * kPointerSize));
// Save 1 in xmm3 - we need this several times later on.
- __ movl(rcx, Immediate(1));
+ __ Set(rcx, 1);
__ cvtlsi2sd(xmm3, rcx);
Label exponent_nonsmi;
@@ -2183,7 +1602,7 @@
__ bind(&no_neg);
// Load xmm1 with 1.
- __ movsd(xmm1, xmm3);
+ __ movaps(xmm1, xmm3);
NearLabel while_true;
NearLabel no_multiply;
@@ -2201,8 +1620,8 @@
__ j(positive, &allocate_return);
// Special case if xmm1 has reached infinity.
__ divsd(xmm3, xmm1);
- __ movsd(xmm1, xmm3);
- __ xorpd(xmm0, xmm0);
+ __ movaps(xmm1, xmm3);
+ __ xorps(xmm0, xmm0);
__ ucomisd(xmm0, xmm1);
__ j(equal, &call_runtime);
@@ -2250,11 +1669,11 @@
// Calculates reciprocal of square root.
// sqrtsd returns -0 when input is -0. ECMA spec requires +0.
- __ xorpd(xmm1, xmm1);
+ __ xorps(xmm1, xmm1);
__ addsd(xmm1, xmm0);
__ sqrtsd(xmm1, xmm1);
__ divsd(xmm3, xmm1);
- __ movsd(xmm1, xmm3);
+ __ movaps(xmm1, xmm3);
__ jmp(&allocate_return);
// Test for 0.5.
@@ -2267,8 +1686,8 @@
__ j(not_equal, &call_runtime);
// Calculates square root.
// sqrtsd returns -0 when input is -0. ECMA spec requires +0.
- __ xorpd(xmm1, xmm1);
- __ addsd(xmm1, xmm0);
+ __ xorps(xmm1, xmm1);
+ __ addsd(xmm1, xmm0); // Convert -0 to 0.
__ sqrtsd(xmm1, xmm1);
__ bind(&allocate_return);
@@ -2944,9 +2363,10 @@
// Heap::GetNumberStringCache.
Label is_smi;
Label load_result_from_cache;
+ Factory* factory = masm->isolate()->factory();
if (!object_is_smi) {
__ JumpIfSmi(object, &is_smi);
- __ CheckMap(object, FACTORY->heap_number_map(), not_found, true);
+ __ CheckMap(object, factory->heap_number_map(), not_found, true);
STATIC_ASSERT(8 == kDoubleSize);
__ movl(scratch, FieldOperand(object, HeapNumber::kValueOffset + 4));
@@ -2961,8 +2381,6 @@
times_1,
FixedArray::kHeaderSize));
__ JumpIfSmi(probe, not_found);
- ASSERT(Isolate::Current()->cpu_features()->IsSupported(SSE2));
- CpuFeatures::Scope fscope(SSE2);
__ movsd(xmm0, FieldOperand(object, HeapNumber::kValueOffset));
__ movsd(xmm1, FieldOperand(probe, HeapNumber::kValueOffset));
__ ucomisd(xmm0, xmm1);
@@ -3035,6 +2453,7 @@
ASSERT(lhs_.is(no_reg) && rhs_.is(no_reg));
Label check_unequal_objects, done;
+ Factory* factory = masm->isolate()->factory();
// Compare two smis if required.
if (include_smi_compare_) {
@@ -3082,7 +2501,6 @@
// Note: if cc_ != equal, never_nan_nan_ is not used.
// We cannot set rax to EQUAL until just before return because
// rax must be unchanged on jump to not_identical.
-
if (never_nan_nan_ && (cc_ == equal)) {
__ Set(rax, EQUAL);
__ ret(0);
@@ -3090,7 +2508,7 @@
NearLabel heap_number;
// If it's not a heap number, then return equal for (in)equality operator.
__ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
- FACTORY->heap_number_map());
+ factory->heap_number_map());
__ j(equal, &heap_number);
if (cc_ != equal) {
// Call runtime on identical JSObjects. Otherwise return equal.
@@ -3135,7 +2553,7 @@
// Check if the non-smi operand is a heap number.
__ Cmp(FieldOperand(rbx, HeapObject::kMapOffset),
- FACTORY->heap_number_map());
+ factory->heap_number_map());
// If heap number, handle it in the slow case.
__ j(equal, &slow);
// Return non-equal. ebx (the lower half of rbx) is not zero.
@@ -3761,10 +3179,10 @@
// is and instance of the function and anything else to
// indicate that the value is not an instance.
- static const int kOffsetToMapCheckValue = 5;
- static const int kOffsetToResultValue = 21;
+ static const int kOffsetToMapCheckValue = 2;
+ static const int kOffsetToResultValue = 18;
// The last 4 bytes of the instruction sequence
- // movq(rax, FieldOperand(rdi, HeapObject::kMapOffset)
+ // movq(rdi, FieldOperand(rax, HeapObject::kMapOffset))
// Move(kScratchRegister, FACTORY->the_hole_value())
// in front of the hole value address.
static const unsigned int kWordBeforeMapCheckValue = 0xBA49FF78;
@@ -3830,7 +3248,7 @@
if (FLAG_debug_code) {
__ movl(rdi, Immediate(kWordBeforeMapCheckValue));
__ cmpl(Operand(kScratchRegister, kOffsetToMapCheckValue - 4), rdi);
- __ Assert(equal, "InstanceofStub unexpected call site cache.");
+ __ Assert(equal, "InstanceofStub unexpected call site cache (check).");
}
}
@@ -3867,9 +3285,9 @@
if (FLAG_debug_code) {
__ movl(rax, Immediate(kWordBeforeResultValue));
__ cmpl(Operand(kScratchRegister, kOffsetToResultValue - 4), rax);
- __ Assert(equal, "InstanceofStub unexpected call site cache.");
+ __ Assert(equal, "InstanceofStub unexpected call site cache (mov).");
}
- __ xorl(rax, rax);
+ __ Set(rax, 0);
}
__ ret(2 * kPointerSize + extra_stack_space);
@@ -4066,10 +3484,11 @@
MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
__ Abort("Unexpected fallthrough to CharCodeAt slow case");
+ Factory* factory = masm->isolate()->factory();
// Index is not a smi.
__ bind(&index_not_smi_);
// If index is a heap number, try converting it to an integer.
- __ CheckMap(index_, FACTORY->heap_number_map(), index_not_number_, true);
+ __ CheckMap(index_, factory->heap_number_map(), index_not_number_, true);
call_helper.BeforeCall(masm);
__ push(object_);
__ push(index_);
@@ -4728,7 +4147,7 @@
// if (hash == 0) hash = 27;
Label hash_not_zero;
__ j(not_zero, &hash_not_zero);
- __ movl(hash, Immediate(27));
+ __ Set(hash, 27);
__ bind(&hash_not_zero);
}
@@ -4924,7 +4343,7 @@
// Use scratch3 as loop index, min_length as limit and scratch2
// for computation.
const Register index = scratch3;
- __ movl(index, Immediate(0)); // Index into strings.
+ __ Set(index, 0); // Index into strings.
__ bind(&loop);
// Compare characters.
// TODO(lrn): Could we load more than one character at a time?
diff --git a/src/x64/code-stubs-x64.h b/src/x64/code-stubs-x64.h
index 246650a..3b40280 100644
--- a/src/x64/code-stubs-x64.h
+++ b/src/x64/code-stubs-x64.h
@@ -71,145 +71,6 @@
};
-// Flag that indicates how to generate code for the stub GenericBinaryOpStub.
-enum GenericBinaryFlags {
- NO_GENERIC_BINARY_FLAGS = 0,
- NO_SMI_CODE_IN_STUB = 1 << 0 // Omit smi code in stub.
-};
-
-
-class GenericBinaryOpStub: public CodeStub {
- public:
- GenericBinaryOpStub(Token::Value op,
- OverwriteMode mode,
- GenericBinaryFlags flags,
- TypeInfo operands_type = TypeInfo::Unknown())
- : op_(op),
- mode_(mode),
- flags_(flags),
- args_in_registers_(false),
- args_reversed_(false),
- static_operands_type_(operands_type),
- runtime_operands_type_(BinaryOpIC::DEFAULT),
- name_(NULL) {
- ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
- }
-
- GenericBinaryOpStub(int key, BinaryOpIC::TypeInfo runtime_operands_type)
- : op_(OpBits::decode(key)),
- mode_(ModeBits::decode(key)),
- flags_(FlagBits::decode(key)),
- args_in_registers_(ArgsInRegistersBits::decode(key)),
- args_reversed_(ArgsReversedBits::decode(key)),
- static_operands_type_(TypeInfo::ExpandedRepresentation(
- StaticTypeInfoBits::decode(key))),
- runtime_operands_type_(runtime_operands_type),
- name_(NULL) {
- }
-
- // Generate code to call the stub with the supplied arguments. This will add
- // code at the call site to prepare arguments either in registers or on the
- // stack together with the actual call.
- void GenerateCall(MacroAssembler* masm, Register left, Register right);
- void GenerateCall(MacroAssembler* masm, Register left, Smi* right);
- void GenerateCall(MacroAssembler* masm, Smi* left, Register right);
-
- bool ArgsInRegistersSupported() {
- return (op_ == Token::ADD) || (op_ == Token::SUB)
- || (op_ == Token::MUL) || (op_ == Token::DIV);
- }
-
- private:
- Token::Value op_;
- OverwriteMode mode_;
- GenericBinaryFlags flags_;
- bool args_in_registers_; // Arguments passed in registers not on the stack.
- bool args_reversed_; // Left and right argument are swapped.
-
- // Number type information of operands, determined by code generator.
- TypeInfo static_operands_type_;
-
- // Operand type information determined at runtime.
- BinaryOpIC::TypeInfo runtime_operands_type_;
-
- char* name_;
-
- const char* GetName();
-
-#ifdef DEBUG
- void Print() {
- PrintF("GenericBinaryOpStub %d (op %s), "
- "(mode %d, flags %d, registers %d, reversed %d, type_info %s)\n",
- MinorKey(),
- Token::String(op_),
- static_cast<int>(mode_),
- static_cast<int>(flags_),
- static_cast<int>(args_in_registers_),
- static_cast<int>(args_reversed_),
- static_operands_type_.ToString());
- }
-#endif
-
- // Minor key encoding in 17 bits TTNNNFRAOOOOOOOMM.
- class ModeBits: public BitField<OverwriteMode, 0, 2> {};
- class OpBits: public BitField<Token::Value, 2, 7> {};
- class ArgsInRegistersBits: public BitField<bool, 9, 1> {};
- class ArgsReversedBits: public BitField<bool, 10, 1> {};
- class FlagBits: public BitField<GenericBinaryFlags, 11, 1> {};
- class StaticTypeInfoBits: public BitField<int, 12, 3> {};
- class RuntimeTypeInfoBits: public BitField<BinaryOpIC::TypeInfo, 15, 3> {};
-
- Major MajorKey() { return GenericBinaryOp; }
- int MinorKey() {
- // Encode the parameters in a unique 18 bit value.
- return OpBits::encode(op_)
- | ModeBits::encode(mode_)
- | FlagBits::encode(flags_)
- | ArgsInRegistersBits::encode(args_in_registers_)
- | ArgsReversedBits::encode(args_reversed_)
- | StaticTypeInfoBits::encode(
- static_operands_type_.ThreeBitRepresentation())
- | RuntimeTypeInfoBits::encode(runtime_operands_type_);
- }
-
- void Generate(MacroAssembler* masm);
- void GenerateSmiCode(MacroAssembler* masm, Label* slow);
- void GenerateLoadArguments(MacroAssembler* masm);
- void GenerateReturn(MacroAssembler* masm);
- void GenerateRegisterArgsPush(MacroAssembler* masm);
- void GenerateTypeTransition(MacroAssembler* masm);
-
- bool IsOperationCommutative() {
- return (op_ == Token::ADD) || (op_ == Token::MUL);
- }
-
- void SetArgsInRegisters() { args_in_registers_ = true; }
- void SetArgsReversed() { args_reversed_ = true; }
- bool HasSmiCodeInStub() { return (flags_ & NO_SMI_CODE_IN_STUB) == 0; }
- bool HasArgsInRegisters() { return args_in_registers_; }
- bool HasArgsReversed() { return args_reversed_; }
-
- bool ShouldGenerateSmiCode() {
- return HasSmiCodeInStub() &&
- runtime_operands_type_ != BinaryOpIC::HEAP_NUMBERS &&
- runtime_operands_type_ != BinaryOpIC::STRINGS;
- }
-
- bool ShouldGenerateFPCode() {
- return runtime_operands_type_ != BinaryOpIC::STRINGS;
- }
-
- virtual int GetCodeKind() { return Code::BINARY_OP_IC; }
-
- virtual InlineCacheState GetICState() {
- return BinaryOpIC::ToState(runtime_operands_type_);
- }
-
- friend class CodeGenerator;
- friend class LCodeGen;
-};
-
-
class TypeRecordingBinaryOpStub: public CodeStub {
public:
TypeRecordingBinaryOpStub(Token::Value op, OverwriteMode mode)
@@ -291,6 +152,7 @@
void GenerateHeapNumberStub(MacroAssembler* masm);
void GenerateOddballStub(MacroAssembler* masm);
void GenerateStringStub(MacroAssembler* masm);
+ void GenerateBothStringStub(MacroAssembler* masm);
void GenerateGenericStub(MacroAssembler* masm);
void GenerateHeapResultAllocation(MacroAssembler* masm, Label* alloc_failure);
diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc
index 8c338fe..f8f2d6e 100644
--- a/src/x64/codegen-x64.cc
+++ b/src/x64/codegen-x64.cc
@@ -29,81 +29,14 @@
#if defined(V8_TARGET_ARCH_X64)
-#include "bootstrapper.h"
-#include "code-stubs.h"
-#include "codegen-inl.h"
-#include "compiler.h"
-#include "debug.h"
-#include "ic-inl.h"
-#include "parser.h"
-#include "regexp-macro-assembler.h"
-#include "register-allocator-inl.h"
-#include "scopes.h"
-#include "virtual-frame-inl.h"
+#include "codegen.h"
namespace v8 {
namespace internal {
-#define __ ACCESS_MASM(masm)
-
-// -------------------------------------------------------------------------
-// Platform-specific FrameRegisterState functions.
-
-void FrameRegisterState::Save(MacroAssembler* masm) const {
- for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
- int action = registers_[i];
- if (action == kPush) {
- __ push(RegisterAllocator::ToRegister(i));
- } else if (action != kIgnore && (action & kSyncedFlag) == 0) {
- __ movq(Operand(rbp, action), RegisterAllocator::ToRegister(i));
- }
- }
-}
-
-
-void FrameRegisterState::Restore(MacroAssembler* masm) const {
- // Restore registers in reverse order due to the stack.
- for (int i = RegisterAllocator::kNumRegisters - 1; i >= 0; i--) {
- int action = registers_[i];
- if (action == kPush) {
- __ pop(RegisterAllocator::ToRegister(i));
- } else if (action != kIgnore) {
- action &= ~kSyncedFlag;
- __ movq(RegisterAllocator::ToRegister(i), Operand(rbp, action));
- }
- }
-}
-
-
-#undef __
-#define __ ACCESS_MASM(masm_)
-
-// -------------------------------------------------------------------------
-// Platform-specific DeferredCode functions.
-
-void DeferredCode::SaveRegisters() {
- frame_state_.Save(masm_);
-}
-
-
-void DeferredCode::RestoreRegisters() {
- frame_state_.Restore(masm_);
-}
-
-
// -------------------------------------------------------------------------
// Platform-specific RuntimeCallHelper functions.
-void VirtualFrameRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
- frame_state_->Save(masm);
-}
-
-
-void VirtualFrameRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
- frame_state_->Restore(masm);
-}
-
-
void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
masm->EnterInternalFrame();
}
@@ -114,8639 +47,6 @@
}
-// -------------------------------------------------------------------------
-// CodeGenState implementation.
-
-CodeGenState::CodeGenState(CodeGenerator* owner)
- : owner_(owner),
- destination_(NULL),
- previous_(NULL) {
- owner_->set_state(this);
-}
-
-
-CodeGenState::CodeGenState(CodeGenerator* owner,
- ControlDestination* destination)
- : owner_(owner),
- destination_(destination),
- previous_(owner->state()) {
- owner_->set_state(this);
-}
-
-
-CodeGenState::~CodeGenState() {
- ASSERT(owner_->state() == this);
- owner_->set_state(previous_);
-}
-
-
-// -------------------------------------------------------------------------
-// CodeGenerator implementation.
-
-CodeGenerator::CodeGenerator(MacroAssembler* masm)
- : deferred_(8),
- masm_(masm),
- info_(NULL),
- frame_(NULL),
- allocator_(NULL),
- state_(NULL),
- loop_nesting_(0),
- function_return_is_shadowed_(false),
- in_spilled_code_(false) {
-}
-
-
-// Calling conventions:
-// rbp: caller's frame pointer
-// rsp: stack pointer
-// rdi: called JS function
-// rsi: callee's context
-
-void CodeGenerator::Generate(CompilationInfo* info) {
- // Record the position for debugging purposes.
- CodeForFunctionPosition(info->function());
- Comment cmnt(masm_, "[ function compiled by virtual frame code generator");
-
- // Initialize state.
- info_ = info;
- ASSERT(allocator_ == NULL);
- RegisterAllocator register_allocator(this);
- allocator_ = ®ister_allocator;
- ASSERT(frame_ == NULL);
- frame_ = new VirtualFrame();
- set_in_spilled_code(false);
-
- // Adjust for function-level loop nesting.
- ASSERT_EQ(0, loop_nesting_);
- loop_nesting_ = info->is_in_loop() ? 1 : 0;
-
- Isolate::Current()->set_jump_target_compiling_deferred_code(false);
-
- {
- CodeGenState state(this);
- // Entry:
- // Stack: receiver, arguments, return address.
- // rbp: caller's frame pointer
- // rsp: stack pointer
- // rdi: called JS function
- // rsi: callee's context
- allocator_->Initialize();
-
-#ifdef DEBUG
- if (strlen(FLAG_stop_at) > 0 &&
- info->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
- frame_->SpillAll();
- __ int3();
- }
-#endif
-
- frame_->Enter();
-
- // Allocate space for locals and initialize them.
- frame_->AllocateStackSlots();
-
- // Allocate the local context if needed.
- int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
- if (heap_slots > 0) {
- Comment cmnt(masm_, "[ allocate local context");
- // Allocate local context.
- // Get outer context and create a new context based on it.
- frame_->PushFunction();
- Result context;
- if (heap_slots <= FastNewContextStub::kMaximumSlots) {
- FastNewContextStub stub(heap_slots);
- context = frame_->CallStub(&stub, 1);
- } else {
- context = frame_->CallRuntime(Runtime::kNewContext, 1);
- }
-
- // Update context local.
- frame_->SaveContextRegister();
-
- // Verify that the runtime call result and rsi agree.
- if (FLAG_debug_code) {
- __ cmpq(context.reg(), rsi);
- __ Assert(equal, "Runtime::NewContext should end up in rsi");
- }
- }
-
- // TODO(1241774): Improve this code:
- // 1) only needed if we have a context
- // 2) no need to recompute context ptr every single time
- // 3) don't copy parameter operand code from SlotOperand!
- {
- Comment cmnt2(masm_, "[ copy context parameters into .context");
- // Note that iteration order is relevant here! If we have the same
- // parameter twice (e.g., function (x, y, x)), and that parameter
- // needs to be copied into the context, it must be the last argument
- // passed to the parameter that needs to be copied. This is a rare
- // case so we don't check for it, instead we rely on the copying
- // order: such a parameter is copied repeatedly into the same
- // context location and thus the last value is what is seen inside
- // the function.
- for (int i = 0; i < scope()->num_parameters(); i++) {
- Variable* par = scope()->parameter(i);
- Slot* slot = par->AsSlot();
- if (slot != NULL && slot->type() == Slot::CONTEXT) {
- // The use of SlotOperand below is safe in unspilled code
- // because the slot is guaranteed to be a context slot.
- //
- // There are no parameters in the global scope.
- ASSERT(!scope()->is_global_scope());
- frame_->PushParameterAt(i);
- Result value = frame_->Pop();
- value.ToRegister();
-
- // SlotOperand loads context.reg() with the context object
- // stored to, used below in RecordWrite.
- Result context = allocator_->Allocate();
- ASSERT(context.is_valid());
- __ movq(SlotOperand(slot, context.reg()), value.reg());
- int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
- Result scratch = allocator_->Allocate();
- ASSERT(scratch.is_valid());
- frame_->Spill(context.reg());
- frame_->Spill(value.reg());
- __ RecordWrite(context.reg(), offset, value.reg(), scratch.reg());
- }
- }
- }
-
- // Store the arguments object. This must happen after context
- // initialization because the arguments object may be stored in
- // the context.
- if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) {
- StoreArgumentsObject(true);
- }
-
- // Initialize ThisFunction reference if present.
- if (scope()->is_function_scope() && scope()->function() != NULL) {
- frame_->Push(FACTORY->the_hole_value());
- StoreToSlot(scope()->function()->AsSlot(), NOT_CONST_INIT);
- }
-
- // Initialize the function return target after the locals are set
- // up, because it needs the expected frame height from the frame.
- function_return_.set_direction(JumpTarget::BIDIRECTIONAL);
- function_return_is_shadowed_ = false;
-
- // Generate code to 'execute' declarations and initialize functions
- // (source elements). In case of an illegal redeclaration we need to
- // handle that instead of processing the declarations.
- if (scope()->HasIllegalRedeclaration()) {
- Comment cmnt(masm_, "[ illegal redeclarations");
- scope()->VisitIllegalRedeclaration(this);
- } else {
- Comment cmnt(masm_, "[ declarations");
- ProcessDeclarations(scope()->declarations());
- // Bail out if a stack-overflow exception occurred when processing
- // declarations.
- if (HasStackOverflow()) return;
- }
-
- if (FLAG_trace) {
- frame_->CallRuntime(Runtime::kTraceEnter, 0);
- // Ignore the return value.
- }
- CheckStack();
-
- // Compile the body of the function in a vanilla state. Don't
- // bother compiling all the code if the scope has an illegal
- // redeclaration.
- if (!scope()->HasIllegalRedeclaration()) {
- Comment cmnt(masm_, "[ function body");
-#ifdef DEBUG
- bool is_builtin = Isolate::Current()->bootstrapper()->IsActive();
- bool should_trace =
- is_builtin ? FLAG_trace_builtin_calls : FLAG_trace_calls;
- if (should_trace) {
- frame_->CallRuntime(Runtime::kDebugTrace, 0);
- // Ignore the return value.
- }
-#endif
- VisitStatements(info->function()->body());
-
- // Handle the return from the function.
- if (has_valid_frame()) {
- // If there is a valid frame, control flow can fall off the end of
- // the body. In that case there is an implicit return statement.
- ASSERT(!function_return_is_shadowed_);
- CodeForReturnPosition(info->function());
- frame_->PrepareForReturn();
- Result undefined(FACTORY->undefined_value());
- if (function_return_.is_bound()) {
- function_return_.Jump(&undefined);
- } else {
- function_return_.Bind(&undefined);
- GenerateReturnSequence(&undefined);
- }
- } else if (function_return_.is_linked()) {
- // If the return target has dangling jumps to it, then we have not
- // yet generated the return sequence. This can happen when (a)
- // control does not flow off the end of the body so we did not
- // compile an artificial return statement just above, and (b) there
- // are return statements in the body but (c) they are all shadowed.
- Result return_value;
- function_return_.Bind(&return_value);
- GenerateReturnSequence(&return_value);
- }
- }
- }
-
- // Adjust for function-level loop nesting.
- ASSERT_EQ(loop_nesting_, info->is_in_loop() ? 1 : 0);
- loop_nesting_ = 0;
-
- // Code generation state must be reset.
- ASSERT(state_ == NULL);
- ASSERT(!function_return_is_shadowed_);
- function_return_.Unuse();
- DeleteFrame();
-
- // Process any deferred code using the register allocator.
- if (!HasStackOverflow()) {
- info->isolate()->set_jump_target_compiling_deferred_code(true);
- ProcessDeferred();
- info->isolate()->set_jump_target_compiling_deferred_code(false);
- }
-
- // There is no need to delete the register allocator, it is a
- // stack-allocated local.
- allocator_ = NULL;
-}
-
-
-Operand CodeGenerator::SlotOperand(Slot* slot, Register tmp) {
- // Currently, this assertion will fail if we try to assign to
- // a constant variable that is constant because it is read-only
- // (such as the variable referring to a named function expression).
- // We need to implement assignments to read-only variables.
- // Ideally, we should do this during AST generation (by converting
- // such assignments into expression statements); however, in general
- // we may not be able to make the decision until past AST generation,
- // that is when the entire program is known.
- ASSERT(slot != NULL);
- int index = slot->index();
- switch (slot->type()) {
- case Slot::PARAMETER:
- return frame_->ParameterAt(index);
-
- case Slot::LOCAL:
- return frame_->LocalAt(index);
-
- case Slot::CONTEXT: {
- // Follow the context chain if necessary.
- ASSERT(!tmp.is(rsi)); // do not overwrite context register
- Register context = rsi;
- int chain_length = scope()->ContextChainLength(slot->var()->scope());
- for (int i = 0; i < chain_length; i++) {
- // Load the closure.
- // (All contexts, even 'with' contexts, have a closure,
- // and it is the same for all contexts inside a function.
- // There is no need to go to the function context first.)
- __ movq(tmp, ContextOperand(context, Context::CLOSURE_INDEX));
- // Load the function context (which is the incoming, outer context).
- __ movq(tmp, FieldOperand(tmp, JSFunction::kContextOffset));
- context = tmp;
- }
- // We may have a 'with' context now. Get the function context.
- // (In fact this mov may never be the needed, since the scope analysis
- // may not permit a direct context access in this case and thus we are
- // always at a function context. However it is safe to dereference be-
- // cause the function context of a function context is itself. Before
- // deleting this mov we should try to create a counter-example first,
- // though...)
- __ movq(tmp, ContextOperand(context, Context::FCONTEXT_INDEX));
- return ContextOperand(tmp, index);
- }
-
- default:
- UNREACHABLE();
- return Operand(rsp, 0);
- }
-}
-
-
-Operand CodeGenerator::ContextSlotOperandCheckExtensions(Slot* slot,
- Result tmp,
- JumpTarget* slow) {
- ASSERT(slot->type() == Slot::CONTEXT);
- ASSERT(tmp.is_register());
- Register context = rsi;
-
- for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) {
- if (s->num_heap_slots() > 0) {
- if (s->calls_eval()) {
- // Check that extension is NULL.
- __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX),
- Immediate(0));
- slow->Branch(not_equal, not_taken);
- }
- __ movq(tmp.reg(), ContextOperand(context, Context::CLOSURE_INDEX));
- __ movq(tmp.reg(), FieldOperand(tmp.reg(), JSFunction::kContextOffset));
- context = tmp.reg();
- }
- }
- // Check that last extension is NULL.
- __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0));
- slow->Branch(not_equal, not_taken);
- __ movq(tmp.reg(), ContextOperand(context, Context::FCONTEXT_INDEX));
- return ContextOperand(tmp.reg(), slot->index());
-}
-
-
-// Emit code to load the value of an expression to the top of the
-// frame. If the expression is boolean-valued it may be compiled (or
-// partially compiled) into control flow to the control destination.
-// If force_control is true, control flow is forced.
-void CodeGenerator::LoadCondition(Expression* expr,
- ControlDestination* dest,
- bool force_control) {
- ASSERT(!in_spilled_code());
- int original_height = frame_->height();
-
- { CodeGenState new_state(this, dest);
- Visit(expr);
-
- // If we hit a stack overflow, we may not have actually visited
- // the expression. In that case, we ensure that we have a
- // valid-looking frame state because we will continue to generate
- // code as we unwind the C++ stack.
- //
- // It's possible to have both a stack overflow and a valid frame
- // state (eg, a subexpression overflowed, visiting it returned
- // with a dummied frame state, and visiting this expression
- // returned with a normal-looking state).
- if (HasStackOverflow() &&
- !dest->is_used() &&
- frame_->height() == original_height) {
- dest->Goto(true);
- }
- }
-
- if (force_control && !dest->is_used()) {
- // Convert the TOS value into flow to the control destination.
- ToBoolean(dest);
- }
-
- ASSERT(!(force_control && !dest->is_used()));
- ASSERT(dest->is_used() || frame_->height() == original_height + 1);
-}
-
-
-void CodeGenerator::LoadAndSpill(Expression* expression) {
- ASSERT(in_spilled_code());
- set_in_spilled_code(false);
- Load(expression);
- frame_->SpillAll();
- set_in_spilled_code(true);
-}
-
-
-void CodeGenerator::Load(Expression* expr) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- ASSERT(!in_spilled_code());
- JumpTarget true_target;
- JumpTarget false_target;
- ControlDestination dest(&true_target, &false_target, true);
- LoadCondition(expr, &dest, false);
-
- if (dest.false_was_fall_through()) {
- // The false target was just bound.
- JumpTarget loaded;
- frame_->Push(FACTORY->false_value());
- // There may be dangling jumps to the true target.
- if (true_target.is_linked()) {
- loaded.Jump();
- true_target.Bind();
- frame_->Push(FACTORY->true_value());
- loaded.Bind();
- }
-
- } else if (dest.is_used()) {
- // There is true, and possibly false, control flow (with true as
- // the fall through).
- JumpTarget loaded;
- frame_->Push(FACTORY->true_value());
- if (false_target.is_linked()) {
- loaded.Jump();
- false_target.Bind();
- frame_->Push(FACTORY->false_value());
- loaded.Bind();
- }
-
- } else {
- // We have a valid value on top of the frame, but we still may
- // have dangling jumps to the true and false targets from nested
- // subexpressions (eg, the left subexpressions of the
- // short-circuited boolean operators).
- ASSERT(has_valid_frame());
- if (true_target.is_linked() || false_target.is_linked()) {
- JumpTarget loaded;
- loaded.Jump(); // Don't lose the current TOS.
- if (true_target.is_linked()) {
- true_target.Bind();
- frame_->Push(FACTORY->true_value());
- if (false_target.is_linked()) {
- loaded.Jump();
- }
- }
- if (false_target.is_linked()) {
- false_target.Bind();
- frame_->Push(FACTORY->false_value());
- }
- loaded.Bind();
- }
- }
-
- ASSERT(has_valid_frame());
- ASSERT(frame_->height() == original_height + 1);
-}
-
-
-void CodeGenerator::LoadGlobal() {
- if (in_spilled_code()) {
- frame_->EmitPush(GlobalObjectOperand());
- } else {
- Result temp = allocator_->Allocate();
- __ movq(temp.reg(), GlobalObjectOperand());
- frame_->Push(&temp);
- }
-}
-
-
-void CodeGenerator::LoadGlobalReceiver() {
- Result temp = allocator_->Allocate();
- Register reg = temp.reg();
- __ movq(reg, GlobalObjectOperand());
- __ movq(reg, FieldOperand(reg, GlobalObject::kGlobalReceiverOffset));
- frame_->Push(&temp);
-}
-
-
-void CodeGenerator::LoadTypeofExpression(Expression* expr) {
- // Special handling of identifiers as subexpressions of typeof.
- Variable* variable = expr->AsVariableProxy()->AsVariable();
- if (variable != NULL && !variable->is_this() && variable->is_global()) {
- // For a global variable we build the property reference
- // <global>.<variable> and perform a (regular non-contextual) property
- // load to make sure we do not get reference errors.
- Slot global(variable, Slot::CONTEXT, Context::GLOBAL_INDEX);
- Literal key(variable->name());
- Property property(&global, &key, RelocInfo::kNoPosition);
- Reference ref(this, &property);
- ref.GetValue();
- } else if (variable != NULL && variable->AsSlot() != NULL) {
- // For a variable that rewrites to a slot, we signal it is the immediate
- // subexpression of a typeof.
- LoadFromSlotCheckForArguments(variable->AsSlot(), INSIDE_TYPEOF);
- } else {
- // Anything else can be handled normally.
- Load(expr);
- }
-}
-
-
-ArgumentsAllocationMode CodeGenerator::ArgumentsMode() {
- if (scope()->arguments() == NULL) return NO_ARGUMENTS_ALLOCATION;
-
- // In strict mode there is no need for shadow arguments.
- ASSERT(scope()->arguments_shadow() != NULL || scope()->is_strict_mode());
- // We don't want to do lazy arguments allocation for functions that
- // have heap-allocated contexts, because it interfers with the
- // uninitialized const tracking in the context objects.
- return (scope()->num_heap_slots() > 0 || scope()->is_strict_mode())
- ? EAGER_ARGUMENTS_ALLOCATION
- : LAZY_ARGUMENTS_ALLOCATION;
-}
-
-
-Result CodeGenerator::StoreArgumentsObject(bool initial) {
- ArgumentsAllocationMode mode = ArgumentsMode();
- ASSERT(mode != NO_ARGUMENTS_ALLOCATION);
-
- Comment cmnt(masm_, "[ store arguments object");
- if (mode == LAZY_ARGUMENTS_ALLOCATION && initial) {
- // When using lazy arguments allocation, we store the arguments marker value
- // as a sentinel indicating that the arguments object hasn't been
- // allocated yet.
- frame_->Push(FACTORY->arguments_marker());
- } else {
- ArgumentsAccessStub stub(is_strict_mode()
- ? ArgumentsAccessStub::NEW_STRICT
- : ArgumentsAccessStub::NEW_NON_STRICT);
- frame_->PushFunction();
- frame_->PushReceiverSlotAddress();
- frame_->Push(Smi::FromInt(scope()->num_parameters()));
- Result result = frame_->CallStub(&stub, 3);
- frame_->Push(&result);
- }
-
- Variable* arguments = scope()->arguments();
- Variable* shadow = scope()->arguments_shadow();
- ASSERT(arguments != NULL && arguments->AsSlot() != NULL);
- ASSERT((shadow != NULL && shadow->AsSlot() != NULL) ||
- scope()->is_strict_mode());
-
- JumpTarget done;
- bool skip_arguments = false;
- if (mode == LAZY_ARGUMENTS_ALLOCATION && !initial) {
- // We have to skip storing into the arguments slot if it has
- // already been written to. This can happen if the a function
- // has a local variable named 'arguments'.
- LoadFromSlot(arguments->AsSlot(), NOT_INSIDE_TYPEOF);
- Result probe = frame_->Pop();
- if (probe.is_constant()) {
- // We have to skip updating the arguments object if it has
- // been assigned a proper value.
- skip_arguments = !probe.handle()->IsArgumentsMarker();
- } else {
- __ CompareRoot(probe.reg(), Heap::kArgumentsMarkerRootIndex);
- probe.Unuse();
- done.Branch(not_equal);
- }
- }
- if (!skip_arguments) {
- StoreToSlot(arguments->AsSlot(), NOT_CONST_INIT);
- if (mode == LAZY_ARGUMENTS_ALLOCATION) done.Bind();
- }
- if (shadow != NULL) {
- StoreToSlot(shadow->AsSlot(), NOT_CONST_INIT);
- }
- return frame_->Pop();
-}
-
-//------------------------------------------------------------------------------
-// CodeGenerator implementation of variables, lookups, and stores.
-
-Reference::Reference(CodeGenerator* cgen,
- Expression* expression,
- bool persist_after_get)
- : cgen_(cgen),
- expression_(expression),
- type_(ILLEGAL),
- persist_after_get_(persist_after_get) {
- cgen->LoadReference(this);
-}
-
-
-Reference::~Reference() {
- ASSERT(is_unloaded() || is_illegal());
-}
-
-
-void CodeGenerator::LoadReference(Reference* ref) {
- // References are loaded from both spilled and unspilled code. Set the
- // state to unspilled to allow that (and explicitly spill after
- // construction at the construction sites).
- bool was_in_spilled_code = in_spilled_code_;
- in_spilled_code_ = false;
-
- Comment cmnt(masm_, "[ LoadReference");
- Expression* e = ref->expression();
- Property* property = e->AsProperty();
- Variable* var = e->AsVariableProxy()->AsVariable();
-
- if (property != NULL) {
- // The expression is either a property or a variable proxy that rewrites
- // to a property.
- Load(property->obj());
- if (property->key()->IsPropertyName()) {
- ref->set_type(Reference::NAMED);
- } else {
- Load(property->key());
- ref->set_type(Reference::KEYED);
- }
- } else if (var != NULL) {
- // The expression is a variable proxy that does not rewrite to a
- // property. Global variables are treated as named property references.
- if (var->is_global()) {
- // If rax is free, the register allocator prefers it. Thus the code
- // generator will load the global object into rax, which is where
- // LoadIC wants it. Most uses of Reference call LoadIC directly
- // after the reference is created.
- frame_->Spill(rax);
- LoadGlobal();
- ref->set_type(Reference::NAMED);
- } else {
- ASSERT(var->AsSlot() != NULL);
- ref->set_type(Reference::SLOT);
- }
- } else {
- // Anything else is a runtime error.
- Load(e);
- frame_->CallRuntime(Runtime::kThrowReferenceError, 1);
- }
-
- in_spilled_code_ = was_in_spilled_code;
-}
-
-
-void CodeGenerator::UnloadReference(Reference* ref) {
- // Pop a reference from the stack while preserving TOS.
- Comment cmnt(masm_, "[ UnloadReference");
- frame_->Nip(ref->size());
- ref->set_unloaded();
-}
-
-
-// ECMA-262, section 9.2, page 30: ToBoolean(). Pop the top of stack and
-// convert it to a boolean in the condition code register or jump to
-// 'false_target'/'true_target' as appropriate.
-void CodeGenerator::ToBoolean(ControlDestination* dest) {
- Comment cmnt(masm_, "[ ToBoolean");
-
- // The value to convert should be popped from the frame.
- Result value = frame_->Pop();
- value.ToRegister();
-
- if (value.is_number()) {
- // Fast case if TypeInfo indicates only numbers.
- if (FLAG_debug_code) {
- __ AbortIfNotNumber(value.reg());
- }
- // Smi => false iff zero.
- __ Cmp(value.reg(), Smi::FromInt(0));
- if (value.is_smi()) {
- value.Unuse();
- dest->Split(not_zero);
- } else {
- dest->false_target()->Branch(equal);
- Condition is_smi = masm_->CheckSmi(value.reg());
- dest->true_target()->Branch(is_smi);
- __ xorpd(xmm0, xmm0);
- __ ucomisd(xmm0, FieldOperand(value.reg(), HeapNumber::kValueOffset));
- value.Unuse();
- dest->Split(not_zero);
- }
- } else {
- // Fast case checks.
- // 'false' => false.
- __ CompareRoot(value.reg(), Heap::kFalseValueRootIndex);
- dest->false_target()->Branch(equal);
-
- // 'true' => true.
- __ CompareRoot(value.reg(), Heap::kTrueValueRootIndex);
- dest->true_target()->Branch(equal);
-
- // 'undefined' => false.
- __ CompareRoot(value.reg(), Heap::kUndefinedValueRootIndex);
- dest->false_target()->Branch(equal);
-
- // Smi => false iff zero.
- __ Cmp(value.reg(), Smi::FromInt(0));
- dest->false_target()->Branch(equal);
- Condition is_smi = masm_->CheckSmi(value.reg());
- dest->true_target()->Branch(is_smi);
-
- // Call the stub for all other cases.
- frame_->Push(&value); // Undo the Pop() from above.
- ToBooleanStub stub;
- Result temp = frame_->CallStub(&stub, 1);
- // Convert the result to a condition code.
- __ testq(temp.reg(), temp.reg());
- temp.Unuse();
- dest->Split(not_equal);
- }
-}
-
-
-// Call the specialized stub for a binary operation.
-class DeferredInlineBinaryOperation: public DeferredCode {
- public:
- DeferredInlineBinaryOperation(Token::Value op,
- Register dst,
- Register left,
- Register right,
- OverwriteMode mode)
- : op_(op), dst_(dst), left_(left), right_(right), mode_(mode) {
- set_comment("[ DeferredInlineBinaryOperation");
- }
-
- virtual void Generate();
-
- private:
- Token::Value op_;
- Register dst_;
- Register left_;
- Register right_;
- OverwriteMode mode_;
-};
-
-
-void DeferredInlineBinaryOperation::Generate() {
- Label done;
- if ((op_ == Token::ADD)
- || (op_ == Token::SUB)
- || (op_ == Token::MUL)
- || (op_ == Token::DIV)) {
- Label call_runtime;
- Label left_smi, right_smi, load_right, do_op;
- __ JumpIfSmi(left_, &left_smi);
- __ CompareRoot(FieldOperand(left_, HeapObject::kMapOffset),
- Heap::kHeapNumberMapRootIndex);
- __ j(not_equal, &call_runtime);
- __ movsd(xmm0, FieldOperand(left_, HeapNumber::kValueOffset));
- if (mode_ == OVERWRITE_LEFT) {
- __ movq(dst_, left_);
- }
- __ jmp(&load_right);
-
- __ bind(&left_smi);
- __ SmiToInteger32(left_, left_);
- __ cvtlsi2sd(xmm0, left_);
- __ Integer32ToSmi(left_, left_);
- if (mode_ == OVERWRITE_LEFT) {
- Label alloc_failure;
- __ AllocateHeapNumber(dst_, no_reg, &call_runtime);
- }
-
- __ bind(&load_right);
- __ JumpIfSmi(right_, &right_smi);
- __ CompareRoot(FieldOperand(right_, HeapObject::kMapOffset),
- Heap::kHeapNumberMapRootIndex);
- __ j(not_equal, &call_runtime);
- __ movsd(xmm1, FieldOperand(right_, HeapNumber::kValueOffset));
- if (mode_ == OVERWRITE_RIGHT) {
- __ movq(dst_, right_);
- } else if (mode_ == NO_OVERWRITE) {
- Label alloc_failure;
- __ AllocateHeapNumber(dst_, no_reg, &call_runtime);
- }
- __ jmp(&do_op);
-
- __ bind(&right_smi);
- __ SmiToInteger32(right_, right_);
- __ cvtlsi2sd(xmm1, right_);
- __ Integer32ToSmi(right_, right_);
- if (mode_ == OVERWRITE_RIGHT || mode_ == NO_OVERWRITE) {
- Label alloc_failure;
- __ AllocateHeapNumber(dst_, no_reg, &call_runtime);
- }
-
- __ bind(&do_op);
- switch (op_) {
- case Token::ADD: __ addsd(xmm0, xmm1); break;
- case Token::SUB: __ subsd(xmm0, xmm1); break;
- case Token::MUL: __ mulsd(xmm0, xmm1); break;
- case Token::DIV: __ divsd(xmm0, xmm1); break;
- default: UNREACHABLE();
- }
- __ movsd(FieldOperand(dst_, HeapNumber::kValueOffset), xmm0);
- __ jmp(&done);
-
- __ bind(&call_runtime);
- }
- GenericBinaryOpStub stub(op_, mode_, NO_SMI_CODE_IN_STUB);
- stub.GenerateCall(masm_, left_, right_);
- if (!dst_.is(rax)) __ movq(dst_, rax);
- __ bind(&done);
-}
-
-
-static TypeInfo CalculateTypeInfo(TypeInfo operands_type,
- Token::Value op,
- const Result& right,
- const Result& left) {
- // Set TypeInfo of result according to the operation performed.
- // We rely on the fact that smis have a 32 bit payload on x64.
- STATIC_ASSERT(kSmiValueSize == 32);
- switch (op) {
- case Token::COMMA:
- return right.type_info();
- case Token::OR:
- case Token::AND:
- // Result type can be either of the two input types.
- return operands_type;
- case Token::BIT_OR:
- case Token::BIT_XOR:
- case Token::BIT_AND:
- // Result is always a smi.
- return TypeInfo::Smi();
- case Token::SAR:
- case Token::SHL:
- // Result is always a smi.
- return TypeInfo::Smi();
- case Token::SHR:
- // Result of x >>> y is always a smi if masked y >= 1, otherwise a number.
- return (right.is_constant() && right.handle()->IsSmi()
- && (Smi::cast(*right.handle())->value() & 0x1F) >= 1)
- ? TypeInfo::Smi()
- : TypeInfo::Number();
- case Token::ADD:
- if (operands_type.IsNumber()) {
- return TypeInfo::Number();
- } else if (left.type_info().IsString() || right.type_info().IsString()) {
- return TypeInfo::String();
- } else {
- return TypeInfo::Unknown();
- }
- case Token::SUB:
- case Token::MUL:
- case Token::DIV:
- case Token::MOD:
- // Result is always a number.
- return TypeInfo::Number();
- default:
- UNREACHABLE();
- }
- UNREACHABLE();
- return TypeInfo::Unknown();
-}
-
-
-void CodeGenerator::GenericBinaryOperation(BinaryOperation* expr,
- OverwriteMode overwrite_mode) {
- Comment cmnt(masm_, "[ BinaryOperation");
- Token::Value op = expr->op();
- Comment cmnt_token(masm_, Token::String(op));
-
- if (op == Token::COMMA) {
- // Simply discard left value.
- frame_->Nip(1);
- return;
- }
-
- Result right = frame_->Pop();
- Result left = frame_->Pop();
-
- if (op == Token::ADD) {
- const bool left_is_string = left.type_info().IsString();
- const bool right_is_string = right.type_info().IsString();
- // Make sure constant strings have string type info.
- ASSERT(!(left.is_constant() && left.handle()->IsString()) ||
- left_is_string);
- ASSERT(!(right.is_constant() && right.handle()->IsString()) ||
- right_is_string);
- if (left_is_string || right_is_string) {
- frame_->Push(&left);
- frame_->Push(&right);
- Result answer;
- if (left_is_string) {
- if (right_is_string) {
- StringAddStub stub(NO_STRING_CHECK_IN_STUB);
- answer = frame_->CallStub(&stub, 2);
- } else {
- answer =
- frame_->InvokeBuiltin(Builtins::STRING_ADD_LEFT, CALL_FUNCTION, 2);
- }
- } else if (right_is_string) {
- answer =
- frame_->InvokeBuiltin(Builtins::STRING_ADD_RIGHT, CALL_FUNCTION, 2);
- }
- answer.set_type_info(TypeInfo::String());
- frame_->Push(&answer);
- return;
- }
- // Neither operand is known to be a string.
- }
-
- bool left_is_smi_constant = left.is_constant() && left.handle()->IsSmi();
- bool left_is_non_smi_constant = left.is_constant() && !left.handle()->IsSmi();
- bool right_is_smi_constant = right.is_constant() && right.handle()->IsSmi();
- bool right_is_non_smi_constant =
- right.is_constant() && !right.handle()->IsSmi();
-
- if (left_is_smi_constant && right_is_smi_constant) {
- // Compute the constant result at compile time, and leave it on the frame.
- int left_int = Smi::cast(*left.handle())->value();
- int right_int = Smi::cast(*right.handle())->value();
- if (FoldConstantSmis(op, left_int, right_int)) return;
- }
-
- // Get number type of left and right sub-expressions.
- TypeInfo operands_type =
- TypeInfo::Combine(left.type_info(), right.type_info());
-
- TypeInfo result_type = CalculateTypeInfo(operands_type, op, right, left);
-
- Result answer;
- if (left_is_non_smi_constant || right_is_non_smi_constant) {
- // Go straight to the slow case, with no smi code.
- GenericBinaryOpStub stub(op,
- overwrite_mode,
- NO_SMI_CODE_IN_STUB,
- operands_type);
- answer = GenerateGenericBinaryOpStubCall(&stub, &left, &right);
- } else if (right_is_smi_constant) {
- answer = ConstantSmiBinaryOperation(expr, &left, right.handle(),
- false, overwrite_mode);
- } else if (left_is_smi_constant) {
- answer = ConstantSmiBinaryOperation(expr, &right, left.handle(),
- true, overwrite_mode);
- } else {
- // Set the flags based on the operation, type and loop nesting level.
- // Bit operations always assume they likely operate on smis. Still only
- // generate the inline Smi check code if this operation is part of a loop.
- // For all other operations only inline the Smi check code for likely smis
- // if the operation is part of a loop.
- if (loop_nesting() > 0 &&
- (Token::IsBitOp(op) ||
- operands_type.IsInteger32() ||
- expr->type()->IsLikelySmi())) {
- answer = LikelySmiBinaryOperation(expr, &left, &right, overwrite_mode);
- } else {
- GenericBinaryOpStub stub(op,
- overwrite_mode,
- NO_GENERIC_BINARY_FLAGS,
- operands_type);
- answer = GenerateGenericBinaryOpStubCall(&stub, &left, &right);
- }
- }
-
- answer.set_type_info(result_type);
- frame_->Push(&answer);
-}
-
-
-bool CodeGenerator::FoldConstantSmis(Token::Value op, int left, int right) {
- Object* answer_object = HEAP->undefined_value();
- switch (op) {
- case Token::ADD:
- // Use intptr_t to detect overflow of 32-bit int.
- if (Smi::IsValid(static_cast<intptr_t>(left) + right)) {
- answer_object = Smi::FromInt(left + right);
- }
- break;
- case Token::SUB:
- // Use intptr_t to detect overflow of 32-bit int.
- if (Smi::IsValid(static_cast<intptr_t>(left) - right)) {
- answer_object = Smi::FromInt(left - right);
- }
- break;
- case Token::MUL: {
- double answer = static_cast<double>(left) * right;
- if (answer >= Smi::kMinValue && answer <= Smi::kMaxValue) {
- // If the product is zero and the non-zero factor is negative,
- // the spec requires us to return floating point negative zero.
- if (answer != 0 || (left >= 0 && right >= 0)) {
- answer_object = Smi::FromInt(static_cast<int>(answer));
- }
- }
- }
- break;
- case Token::DIV:
- case Token::MOD:
- break;
- case Token::BIT_OR:
- answer_object = Smi::FromInt(left | right);
- break;
- case Token::BIT_AND:
- answer_object = Smi::FromInt(left & right);
- break;
- case Token::BIT_XOR:
- answer_object = Smi::FromInt(left ^ right);
- break;
-
- case Token::SHL: {
- int shift_amount = right & 0x1F;
- if (Smi::IsValid(left << shift_amount)) {
- answer_object = Smi::FromInt(left << shift_amount);
- }
- break;
- }
- case Token::SHR: {
- int shift_amount = right & 0x1F;
- unsigned int unsigned_left = left;
- unsigned_left >>= shift_amount;
- if (unsigned_left <= static_cast<unsigned int>(Smi::kMaxValue)) {
- answer_object = Smi::FromInt(unsigned_left);
- }
- break;
- }
- case Token::SAR: {
- int shift_amount = right & 0x1F;
- unsigned int unsigned_left = left;
- if (left < 0) {
- // Perform arithmetic shift of a negative number by
- // complementing number, logical shifting, complementing again.
- unsigned_left = ~unsigned_left;
- unsigned_left >>= shift_amount;
- unsigned_left = ~unsigned_left;
- } else {
- unsigned_left >>= shift_amount;
- }
- ASSERT(Smi::IsValid(static_cast<int32_t>(unsigned_left)));
- answer_object = Smi::FromInt(static_cast<int32_t>(unsigned_left));
- break;
- }
- default:
- UNREACHABLE();
- break;
- }
- if (answer_object->IsUndefined()) {
- return false;
- }
- frame_->Push(Handle<Object>(answer_object));
- return true;
-}
-
-
-void CodeGenerator::JumpIfBothSmiUsingTypeInfo(Result* left,
- Result* right,
- JumpTarget* both_smi) {
- TypeInfo left_info = left->type_info();
- TypeInfo right_info = right->type_info();
- if (left_info.IsDouble() || left_info.IsString() ||
- right_info.IsDouble() || right_info.IsString()) {
- // We know that left and right are not both smi. Don't do any tests.
- return;
- }
-
- if (left->reg().is(right->reg())) {
- if (!left_info.IsSmi()) {
- Condition is_smi = masm()->CheckSmi(left->reg());
- both_smi->Branch(is_smi);
- } else {
- if (FLAG_debug_code) __ AbortIfNotSmi(left->reg());
- left->Unuse();
- right->Unuse();
- both_smi->Jump();
- }
- } else if (!left_info.IsSmi()) {
- if (!right_info.IsSmi()) {
- Condition is_smi = masm()->CheckBothSmi(left->reg(), right->reg());
- both_smi->Branch(is_smi);
- } else {
- Condition is_smi = masm()->CheckSmi(left->reg());
- both_smi->Branch(is_smi);
- }
- } else {
- if (FLAG_debug_code) __ AbortIfNotSmi(left->reg());
- if (!right_info.IsSmi()) {
- Condition is_smi = masm()->CheckSmi(right->reg());
- both_smi->Branch(is_smi);
- } else {
- if (FLAG_debug_code) __ AbortIfNotSmi(right->reg());
- left->Unuse();
- right->Unuse();
- both_smi->Jump();
- }
- }
-}
-
-
-void CodeGenerator::JumpIfNotSmiUsingTypeInfo(Register reg,
- TypeInfo type,
- DeferredCode* deferred) {
- if (!type.IsSmi()) {
- __ JumpIfNotSmi(reg, deferred->entry_label());
- }
- if (FLAG_debug_code) {
- __ AbortIfNotSmi(reg);
- }
-}
-
-
-void CodeGenerator::JumpIfNotBothSmiUsingTypeInfo(Register left,
- Register right,
- TypeInfo left_info,
- TypeInfo right_info,
- DeferredCode* deferred) {
- if (!left_info.IsSmi() && !right_info.IsSmi()) {
- __ JumpIfNotBothSmi(left, right, deferred->entry_label());
- } else if (!left_info.IsSmi()) {
- __ JumpIfNotSmi(left, deferred->entry_label());
- } else if (!right_info.IsSmi()) {
- __ JumpIfNotSmi(right, deferred->entry_label());
- }
- if (FLAG_debug_code) {
- __ AbortIfNotSmi(left);
- __ AbortIfNotSmi(right);
- }
-}
-
-
-// Implements a binary operation using a deferred code object and some
-// inline code to operate on smis quickly.
-Result CodeGenerator::LikelySmiBinaryOperation(BinaryOperation* expr,
- Result* left,
- Result* right,
- OverwriteMode overwrite_mode) {
- // Copy the type info because left and right may be overwritten.
- TypeInfo left_type_info = left->type_info();
- TypeInfo right_type_info = right->type_info();
- Token::Value op = expr->op();
- Result answer;
- // Special handling of div and mod because they use fixed registers.
- if (op == Token::DIV || op == Token::MOD) {
- // We need rax as the quotient register, rdx as the remainder
- // register, neither left nor right in rax or rdx, and left copied
- // to rax.
- Result quotient;
- Result remainder;
- bool left_is_in_rax = false;
- // Step 1: get rax for quotient.
- if ((left->is_register() && left->reg().is(rax)) ||
- (right->is_register() && right->reg().is(rax))) {
- // One or both is in rax. Use a fresh non-rdx register for
- // them.
- Result fresh = allocator_->Allocate();
- ASSERT(fresh.is_valid());
- if (fresh.reg().is(rdx)) {
- remainder = fresh;
- fresh = allocator_->Allocate();
- ASSERT(fresh.is_valid());
- }
- if (left->is_register() && left->reg().is(rax)) {
- quotient = *left;
- *left = fresh;
- left_is_in_rax = true;
- }
- if (right->is_register() && right->reg().is(rax)) {
- quotient = *right;
- *right = fresh;
- }
- __ movq(fresh.reg(), rax);
- } else {
- // Neither left nor right is in rax.
- quotient = allocator_->Allocate(rax);
- }
- ASSERT(quotient.is_register() && quotient.reg().is(rax));
- ASSERT(!(left->is_register() && left->reg().is(rax)));
- ASSERT(!(right->is_register() && right->reg().is(rax)));
-
- // Step 2: get rdx for remainder if necessary.
- if (!remainder.is_valid()) {
- if ((left->is_register() && left->reg().is(rdx)) ||
- (right->is_register() && right->reg().is(rdx))) {
- Result fresh = allocator_->Allocate();
- ASSERT(fresh.is_valid());
- if (left->is_register() && left->reg().is(rdx)) {
- remainder = *left;
- *left = fresh;
- }
- if (right->is_register() && right->reg().is(rdx)) {
- remainder = *right;
- *right = fresh;
- }
- __ movq(fresh.reg(), rdx);
- } else {
- // Neither left nor right is in rdx.
- remainder = allocator_->Allocate(rdx);
- }
- }
- ASSERT(remainder.is_register() && remainder.reg().is(rdx));
- ASSERT(!(left->is_register() && left->reg().is(rdx)));
- ASSERT(!(right->is_register() && right->reg().is(rdx)));
-
- left->ToRegister();
- right->ToRegister();
- frame_->Spill(rax);
- frame_->Spill(rdx);
-
- // Check that left and right are smi tagged.
- DeferredInlineBinaryOperation* deferred =
- new DeferredInlineBinaryOperation(op,
- (op == Token::DIV) ? rax : rdx,
- left->reg(),
- right->reg(),
- overwrite_mode);
- JumpIfNotBothSmiUsingTypeInfo(left->reg(), right->reg(),
- left_type_info, right_type_info, deferred);
-
- if (op == Token::DIV) {
- __ SmiDiv(rax, left->reg(), right->reg(), deferred->entry_label());
- deferred->BindExit();
- left->Unuse();
- right->Unuse();
- answer = quotient;
- } else {
- ASSERT(op == Token::MOD);
- __ SmiMod(rdx, left->reg(), right->reg(), deferred->entry_label());
- deferred->BindExit();
- left->Unuse();
- right->Unuse();
- answer = remainder;
- }
- ASSERT(answer.is_valid());
- return answer;
- }
-
- // Special handling of shift operations because they use fixed
- // registers.
- if (op == Token::SHL || op == Token::SHR || op == Token::SAR) {
- // Move left out of rcx if necessary.
- if (left->is_register() && left->reg().is(rcx)) {
- *left = allocator_->Allocate();
- ASSERT(left->is_valid());
- __ movq(left->reg(), rcx);
- }
- right->ToRegister(rcx);
- left->ToRegister();
- ASSERT(left->is_register() && !left->reg().is(rcx));
- ASSERT(right->is_register() && right->reg().is(rcx));
-
- // We will modify right, it must be spilled.
- frame_->Spill(rcx);
-
- // Use a fresh answer register to avoid spilling the left operand.
- answer = allocator_->Allocate();
- ASSERT(answer.is_valid());
- // Check that both operands are smis using the answer register as a
- // temporary.
- DeferredInlineBinaryOperation* deferred =
- new DeferredInlineBinaryOperation(op,
- answer.reg(),
- left->reg(),
- rcx,
- overwrite_mode);
-
- Label do_op;
- // Left operand must be unchanged in left->reg() for deferred code.
- // Left operand is in answer.reg(), possibly converted to int32, for
- // inline code.
- __ movq(answer.reg(), left->reg());
- if (right_type_info.IsSmi()) {
- if (FLAG_debug_code) {
- __ AbortIfNotSmi(right->reg());
- }
- // If left is not known to be a smi, check if it is.
- // If left is not known to be a number, and it isn't a smi, check if
- // it is a HeapNumber.
- if (!left_type_info.IsSmi()) {
- __ JumpIfSmi(answer.reg(), &do_op);
- if (!left_type_info.IsNumber()) {
- // Branch if not a heapnumber.
- __ Cmp(FieldOperand(answer.reg(), HeapObject::kMapOffset),
- FACTORY->heap_number_map());
- deferred->Branch(not_equal);
- }
- // Load integer value into answer register using truncation.
- __ cvttsd2si(answer.reg(),
- FieldOperand(answer.reg(), HeapNumber::kValueOffset));
- // Branch if we might have overflowed.
- // (False negative for Smi::kMinValue)
- __ cmpl(answer.reg(), Immediate(0x80000000));
- deferred->Branch(equal);
- // TODO(lrn): Inline shifts on int32 here instead of first smi-tagging.
- __ Integer32ToSmi(answer.reg(), answer.reg());
- } else {
- // Fast case - both are actually smis.
- if (FLAG_debug_code) {
- __ AbortIfNotSmi(left->reg());
- }
- }
- } else {
- JumpIfNotBothSmiUsingTypeInfo(left->reg(), rcx,
- left_type_info, right_type_info, deferred);
- }
- __ bind(&do_op);
-
- // Perform the operation.
- switch (op) {
- case Token::SAR:
- __ SmiShiftArithmeticRight(answer.reg(), answer.reg(), rcx);
- break;
- case Token::SHR: {
- __ SmiShiftLogicalRight(answer.reg(),
- answer.reg(),
- rcx,
- deferred->entry_label());
- break;
- }
- case Token::SHL: {
- __ SmiShiftLeft(answer.reg(),
- answer.reg(),
- rcx);
- break;
- }
- default:
- UNREACHABLE();
- }
- deferred->BindExit();
- left->Unuse();
- right->Unuse();
- ASSERT(answer.is_valid());
- return answer;
- }
-
- // Handle the other binary operations.
- left->ToRegister();
- right->ToRegister();
- // A newly allocated register answer is used to hold the answer. The
- // registers containing left and right are not modified so they don't
- // need to be spilled in the fast case.
- answer = allocator_->Allocate();
- ASSERT(answer.is_valid());
-
- // Perform the smi tag check.
- DeferredInlineBinaryOperation* deferred =
- new DeferredInlineBinaryOperation(op,
- answer.reg(),
- left->reg(),
- right->reg(),
- overwrite_mode);
- JumpIfNotBothSmiUsingTypeInfo(left->reg(), right->reg(),
- left_type_info, right_type_info, deferred);
-
- switch (op) {
- case Token::ADD:
- __ SmiAdd(answer.reg(),
- left->reg(),
- right->reg(),
- deferred->entry_label());
- break;
-
- case Token::SUB:
- __ SmiSub(answer.reg(),
- left->reg(),
- right->reg(),
- deferred->entry_label());
- break;
-
- case Token::MUL: {
- __ SmiMul(answer.reg(),
- left->reg(),
- right->reg(),
- deferred->entry_label());
- break;
- }
-
- case Token::BIT_OR:
- __ SmiOr(answer.reg(), left->reg(), right->reg());
- break;
-
- case Token::BIT_AND:
- __ SmiAnd(answer.reg(), left->reg(), right->reg());
- break;
-
- case Token::BIT_XOR:
- __ SmiXor(answer.reg(), left->reg(), right->reg());
- break;
-
- default:
- UNREACHABLE();
- break;
- }
- deferred->BindExit();
- left->Unuse();
- right->Unuse();
- ASSERT(answer.is_valid());
- return answer;
-}
-
-
-// Call the appropriate binary operation stub to compute src op value
-// and leave the result in dst.
-class DeferredInlineSmiOperation: public DeferredCode {
- public:
- DeferredInlineSmiOperation(Token::Value op,
- Register dst,
- Register src,
- Smi* value,
- OverwriteMode overwrite_mode)
- : op_(op),
- dst_(dst),
- src_(src),
- value_(value),
- overwrite_mode_(overwrite_mode) {
- set_comment("[ DeferredInlineSmiOperation");
- }
-
- virtual void Generate();
-
- private:
- Token::Value op_;
- Register dst_;
- Register src_;
- Smi* value_;
- OverwriteMode overwrite_mode_;
-};
-
-
-void DeferredInlineSmiOperation::Generate() {
- // For mod we don't generate all the Smi code inline.
- GenericBinaryOpStub stub(
- op_,
- overwrite_mode_,
- (op_ == Token::MOD) ? NO_GENERIC_BINARY_FLAGS : NO_SMI_CODE_IN_STUB);
- stub.GenerateCall(masm_, src_, value_);
- if (!dst_.is(rax)) __ movq(dst_, rax);
-}
-
-
-// Call the appropriate binary operation stub to compute value op src
-// and leave the result in dst.
-class DeferredInlineSmiOperationReversed: public DeferredCode {
- public:
- DeferredInlineSmiOperationReversed(Token::Value op,
- Register dst,
- Smi* value,
- Register src,
- OverwriteMode overwrite_mode)
- : op_(op),
- dst_(dst),
- value_(value),
- src_(src),
- overwrite_mode_(overwrite_mode) {
- set_comment("[ DeferredInlineSmiOperationReversed");
- }
-
- virtual void Generate();
-
- private:
- Token::Value op_;
- Register dst_;
- Smi* value_;
- Register src_;
- OverwriteMode overwrite_mode_;
-};
-
-
-void DeferredInlineSmiOperationReversed::Generate() {
- GenericBinaryOpStub stub(
- op_,
- overwrite_mode_,
- NO_SMI_CODE_IN_STUB);
- stub.GenerateCall(masm_, value_, src_);
- if (!dst_.is(rax)) __ movq(dst_, rax);
-}
-class DeferredInlineSmiAdd: public DeferredCode {
- public:
- DeferredInlineSmiAdd(Register dst,
- Smi* value,
- OverwriteMode overwrite_mode)
- : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
- set_comment("[ DeferredInlineSmiAdd");
- }
-
- virtual void Generate();
-
- private:
- Register dst_;
- Smi* value_;
- OverwriteMode overwrite_mode_;
-};
-
-
-void DeferredInlineSmiAdd::Generate() {
- GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, NO_SMI_CODE_IN_STUB);
- igostub.GenerateCall(masm_, dst_, value_);
- if (!dst_.is(rax)) __ movq(dst_, rax);
-}
-
-
-// The result of value + src is in dst. It either overflowed or was not
-// smi tagged. Undo the speculative addition and call the appropriate
-// specialized stub for add. The result is left in dst.
-class DeferredInlineSmiAddReversed: public DeferredCode {
- public:
- DeferredInlineSmiAddReversed(Register dst,
- Smi* value,
- OverwriteMode overwrite_mode)
- : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
- set_comment("[ DeferredInlineSmiAddReversed");
- }
-
- virtual void Generate();
-
- private:
- Register dst_;
- Smi* value_;
- OverwriteMode overwrite_mode_;
-};
-
-
-void DeferredInlineSmiAddReversed::Generate() {
- GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, NO_SMI_CODE_IN_STUB);
- igostub.GenerateCall(masm_, value_, dst_);
- if (!dst_.is(rax)) __ movq(dst_, rax);
-}
-
-
-class DeferredInlineSmiSub: public DeferredCode {
- public:
- DeferredInlineSmiSub(Register dst,
- Smi* value,
- OverwriteMode overwrite_mode)
- : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
- set_comment("[ DeferredInlineSmiSub");
- }
-
- virtual void Generate();
-
- private:
- Register dst_;
- Smi* value_;
- OverwriteMode overwrite_mode_;
-};
-
-
-void DeferredInlineSmiSub::Generate() {
- GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, NO_SMI_CODE_IN_STUB);
- igostub.GenerateCall(masm_, dst_, value_);
- if (!dst_.is(rax)) __ movq(dst_, rax);
-}
-
-
-Result CodeGenerator::ConstantSmiBinaryOperation(BinaryOperation* expr,
- Result* operand,
- Handle<Object> value,
- bool reversed,
- OverwriteMode overwrite_mode) {
- // Generate inline code for a binary operation when one of the
- // operands is a constant smi. Consumes the argument "operand".
- if (IsUnsafeSmi(value)) {
- Result unsafe_operand(value);
- if (reversed) {
- return LikelySmiBinaryOperation(expr, &unsafe_operand, operand,
- overwrite_mode);
- } else {
- return LikelySmiBinaryOperation(expr, operand, &unsafe_operand,
- overwrite_mode);
- }
- }
-
- // Get the literal value.
- Smi* smi_value = Smi::cast(*value);
- int int_value = smi_value->value();
-
- Token::Value op = expr->op();
- Result answer;
- switch (op) {
- case Token::ADD: {
- operand->ToRegister();
- frame_->Spill(operand->reg());
- DeferredCode* deferred = NULL;
- if (reversed) {
- deferred = new DeferredInlineSmiAddReversed(operand->reg(),
- smi_value,
- overwrite_mode);
- } else {
- deferred = new DeferredInlineSmiAdd(operand->reg(),
- smi_value,
- overwrite_mode);
- }
- JumpIfNotSmiUsingTypeInfo(operand->reg(), operand->type_info(),
- deferred);
- __ SmiAddConstant(operand->reg(),
- operand->reg(),
- smi_value,
- deferred->entry_label());
- deferred->BindExit();
- answer = *operand;
- break;
- }
-
- case Token::SUB: {
- if (reversed) {
- Result constant_operand(value);
- answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
- overwrite_mode);
- } else {
- operand->ToRegister();
- frame_->Spill(operand->reg());
- answer = *operand;
- DeferredCode* deferred = new DeferredInlineSmiSub(operand->reg(),
- smi_value,
- overwrite_mode);
- JumpIfNotSmiUsingTypeInfo(operand->reg(), operand->type_info(),
- deferred);
- // A smi currently fits in a 32-bit Immediate.
- __ SmiSubConstant(operand->reg(),
- operand->reg(),
- smi_value,
- deferred->entry_label());
- deferred->BindExit();
- operand->Unuse();
- }
- break;
- }
-
- case Token::SAR:
- if (reversed) {
- Result constant_operand(value);
- answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
- overwrite_mode);
- } else {
- // Only the least significant 5 bits of the shift value are used.
- // In the slow case, this masking is done inside the runtime call.
- int shift_value = int_value & 0x1f;
- operand->ToRegister();
- frame_->Spill(operand->reg());
- DeferredInlineSmiOperation* deferred =
- new DeferredInlineSmiOperation(op,
- operand->reg(),
- operand->reg(),
- smi_value,
- overwrite_mode);
- JumpIfNotSmiUsingTypeInfo(operand->reg(), operand->type_info(),
- deferred);
- __ SmiShiftArithmeticRightConstant(operand->reg(),
- operand->reg(),
- shift_value);
- deferred->BindExit();
- answer = *operand;
- }
- break;
-
- case Token::SHR:
- if (reversed) {
- Result constant_operand(value);
- answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
- overwrite_mode);
- } else {
- // Only the least significant 5 bits of the shift value are used.
- // In the slow case, this masking is done inside the runtime call.
- int shift_value = int_value & 0x1f;
- operand->ToRegister();
- answer = allocator()->Allocate();
- ASSERT(answer.is_valid());
- DeferredInlineSmiOperation* deferred =
- new DeferredInlineSmiOperation(op,
- answer.reg(),
- operand->reg(),
- smi_value,
- overwrite_mode);
- JumpIfNotSmiUsingTypeInfo(operand->reg(), operand->type_info(),
- deferred);
- __ SmiShiftLogicalRightConstant(answer.reg(),
- operand->reg(),
- shift_value,
- deferred->entry_label());
- deferred->BindExit();
- operand->Unuse();
- }
- break;
-
- case Token::SHL:
- if (reversed) {
- operand->ToRegister();
-
- // We need rcx to be available to hold operand, and to be spilled.
- // SmiShiftLeft implicitly modifies rcx.
- if (operand->reg().is(rcx)) {
- frame_->Spill(operand->reg());
- answer = allocator()->Allocate();
- } else {
- Result rcx_reg = allocator()->Allocate(rcx);
- // answer must not be rcx.
- answer = allocator()->Allocate();
- // rcx_reg goes out of scope.
- }
-
- DeferredInlineSmiOperationReversed* deferred =
- new DeferredInlineSmiOperationReversed(op,
- answer.reg(),
- smi_value,
- operand->reg(),
- overwrite_mode);
- JumpIfNotSmiUsingTypeInfo(operand->reg(), operand->type_info(),
- deferred);
-
- __ Move(answer.reg(), smi_value);
- __ SmiShiftLeft(answer.reg(), answer.reg(), operand->reg());
- operand->Unuse();
-
- deferred->BindExit();
- } else {
- // Only the least significant 5 bits of the shift value are used.
- // In the slow case, this masking is done inside the runtime call.
- int shift_value = int_value & 0x1f;
- operand->ToRegister();
- if (shift_value == 0) {
- // Spill operand so it can be overwritten in the slow case.
- frame_->Spill(operand->reg());
- DeferredInlineSmiOperation* deferred =
- new DeferredInlineSmiOperation(op,
- operand->reg(),
- operand->reg(),
- smi_value,
- overwrite_mode);
- JumpIfNotSmiUsingTypeInfo(operand->reg(), operand->type_info(),
- deferred);
- deferred->BindExit();
- answer = *operand;
- } else {
- // Use a fresh temporary for nonzero shift values.
- answer = allocator()->Allocate();
- ASSERT(answer.is_valid());
- DeferredInlineSmiOperation* deferred =
- new DeferredInlineSmiOperation(op,
- answer.reg(),
- operand->reg(),
- smi_value,
- overwrite_mode);
- JumpIfNotSmiUsingTypeInfo(operand->reg(), operand->type_info(),
- deferred);
- __ SmiShiftLeftConstant(answer.reg(),
- operand->reg(),
- shift_value);
- deferred->BindExit();
- operand->Unuse();
- }
- }
- break;
-
- case Token::BIT_OR:
- case Token::BIT_XOR:
- case Token::BIT_AND: {
- operand->ToRegister();
- frame_->Spill(operand->reg());
- if (reversed) {
- // Bit operations with a constant smi are commutative.
- // We can swap left and right operands with no problem.
- // Swap left and right overwrite modes. 0->0, 1->2, 2->1.
- overwrite_mode = static_cast<OverwriteMode>((2 * overwrite_mode) % 3);
- }
- DeferredCode* deferred = new DeferredInlineSmiOperation(op,
- operand->reg(),
- operand->reg(),
- smi_value,
- overwrite_mode);
- JumpIfNotSmiUsingTypeInfo(operand->reg(), operand->type_info(),
- deferred);
- if (op == Token::BIT_AND) {
- __ SmiAndConstant(operand->reg(), operand->reg(), smi_value);
- } else if (op == Token::BIT_XOR) {
- if (int_value != 0) {
- __ SmiXorConstant(operand->reg(), operand->reg(), smi_value);
- }
- } else {
- ASSERT(op == Token::BIT_OR);
- if (int_value != 0) {
- __ SmiOrConstant(operand->reg(), operand->reg(), smi_value);
- }
- }
- deferred->BindExit();
- answer = *operand;
- break;
- }
-
- // Generate inline code for mod of powers of 2 and negative powers of 2.
- case Token::MOD:
- if (!reversed &&
- int_value != 0 &&
- (IsPowerOf2(int_value) || IsPowerOf2(-int_value))) {
- operand->ToRegister();
- frame_->Spill(operand->reg());
- DeferredCode* deferred =
- new DeferredInlineSmiOperation(op,
- operand->reg(),
- operand->reg(),
- smi_value,
- overwrite_mode);
- __ JumpUnlessNonNegativeSmi(operand->reg(), deferred->entry_label());
- if (int_value < 0) int_value = -int_value;
- if (int_value == 1) {
- __ Move(operand->reg(), Smi::FromInt(0));
- } else {
- __ SmiAndConstant(operand->reg(),
- operand->reg(),
- Smi::FromInt(int_value - 1));
- }
- deferred->BindExit();
- answer = *operand;
- break; // This break only applies if we generated code for MOD.
- }
- // Fall through if we did not find a power of 2 on the right hand side!
- // The next case must be the default.
-
- default: {
- Result constant_operand(value);
- if (reversed) {
- answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
- overwrite_mode);
- } else {
- answer = LikelySmiBinaryOperation(expr, operand, &constant_operand,
- overwrite_mode);
- }
- break;
- }
- }
- ASSERT(answer.is_valid());
- return answer;
-}
-
-
-static bool CouldBeNaN(const Result& result) {
- if (result.type_info().IsSmi()) return false;
- if (result.type_info().IsInteger32()) return false;
- if (!result.is_constant()) return true;
- if (!result.handle()->IsHeapNumber()) return false;
- return isnan(HeapNumber::cast(*result.handle())->value());
-}
-
-
-// Convert from signed to unsigned comparison to match the way EFLAGS are set
-// by FPU and XMM compare instructions.
-static Condition DoubleCondition(Condition cc) {
- switch (cc) {
- case less: return below;
- case equal: return equal;
- case less_equal: return below_equal;
- case greater: return above;
- case greater_equal: return above_equal;
- default: UNREACHABLE();
- }
- UNREACHABLE();
- return equal;
-}
-
-
-static CompareFlags ComputeCompareFlags(NaNInformation nan_info,
- bool inline_number_compare) {
- CompareFlags flags = NO_SMI_COMPARE_IN_STUB;
- if (nan_info == kCantBothBeNaN) {
- flags = static_cast<CompareFlags>(flags | CANT_BOTH_BE_NAN);
- }
- if (inline_number_compare) {
- flags = static_cast<CompareFlags>(flags | NO_NUMBER_COMPARE_IN_STUB);
- }
- return flags;
-}
-
-
-void CodeGenerator::Comparison(AstNode* node,
- Condition cc,
- bool strict,
- ControlDestination* dest) {
- // Strict only makes sense for equality comparisons.
- ASSERT(!strict || cc == equal);
-
- Result left_side;
- Result right_side;
- // Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order.
- if (cc == greater || cc == less_equal) {
- cc = ReverseCondition(cc);
- left_side = frame_->Pop();
- right_side = frame_->Pop();
- } else {
- right_side = frame_->Pop();
- left_side = frame_->Pop();
- }
- ASSERT(cc == less || cc == equal || cc == greater_equal);
-
- // If either side is a constant smi, optimize the comparison.
- bool left_side_constant_smi = false;
- bool left_side_constant_null = false;
- bool left_side_constant_1_char_string = false;
- if (left_side.is_constant()) {
- left_side_constant_smi = left_side.handle()->IsSmi();
- left_side_constant_null = left_side.handle()->IsNull();
- left_side_constant_1_char_string =
- (left_side.handle()->IsString() &&
- String::cast(*left_side.handle())->length() == 1 &&
- String::cast(*left_side.handle())->IsAsciiRepresentation());
- }
- bool right_side_constant_smi = false;
- bool right_side_constant_null = false;
- bool right_side_constant_1_char_string = false;
- if (right_side.is_constant()) {
- right_side_constant_smi = right_side.handle()->IsSmi();
- right_side_constant_null = right_side.handle()->IsNull();
- right_side_constant_1_char_string =
- (right_side.handle()->IsString() &&
- String::cast(*right_side.handle())->length() == 1 &&
- String::cast(*right_side.handle())->IsAsciiRepresentation());
- }
-
- if (left_side_constant_smi || right_side_constant_smi) {
- bool is_loop_condition = (node->AsExpression() != NULL) &&
- node->AsExpression()->is_loop_condition();
- ConstantSmiComparison(cc, strict, dest, &left_side, &right_side,
- left_side_constant_smi, right_side_constant_smi,
- is_loop_condition);
- } else if (left_side_constant_1_char_string ||
- right_side_constant_1_char_string) {
- if (left_side_constant_1_char_string && right_side_constant_1_char_string) {
- // Trivial case, comparing two constants.
- int left_value = String::cast(*left_side.handle())->Get(0);
- int right_value = String::cast(*right_side.handle())->Get(0);
- switch (cc) {
- case less:
- dest->Goto(left_value < right_value);
- break;
- case equal:
- dest->Goto(left_value == right_value);
- break;
- case greater_equal:
- dest->Goto(left_value >= right_value);
- break;
- default:
- UNREACHABLE();
- }
- } else {
- // Only one side is a constant 1 character string.
- // If left side is a constant 1-character string, reverse the operands.
- // Since one side is a constant string, conversion order does not matter.
- if (left_side_constant_1_char_string) {
- Result temp = left_side;
- left_side = right_side;
- right_side = temp;
- cc = ReverseCondition(cc);
- // This may reintroduce greater or less_equal as the value of cc.
- // CompareStub and the inline code both support all values of cc.
- }
- // Implement comparison against a constant string, inlining the case
- // where both sides are strings.
- left_side.ToRegister();
-
- // Here we split control flow to the stub call and inlined cases
- // before finally splitting it to the control destination. We use
- // a jump target and branching to duplicate the virtual frame at
- // the first split. We manually handle the off-frame references
- // by reconstituting them on the non-fall-through path.
- JumpTarget is_not_string, is_string;
- Register left_reg = left_side.reg();
- Handle<Object> right_val = right_side.handle();
- ASSERT(StringShape(String::cast(*right_val)).IsSymbol());
- Condition is_smi = masm()->CheckSmi(left_reg);
- is_not_string.Branch(is_smi, &left_side);
- Result temp = allocator_->Allocate();
- ASSERT(temp.is_valid());
- __ movq(temp.reg(),
- FieldOperand(left_reg, HeapObject::kMapOffset));
- __ movzxbl(temp.reg(),
- FieldOperand(temp.reg(), Map::kInstanceTypeOffset));
- // If we are testing for equality then make use of the symbol shortcut.
- // Check if the left hand side has the same type as the right hand
- // side (which is always a symbol).
- if (cc == equal) {
- Label not_a_symbol;
- STATIC_ASSERT(kSymbolTag != 0);
- // Ensure that no non-strings have the symbol bit set.
- STATIC_ASSERT(LAST_TYPE < kNotStringTag + kIsSymbolMask);
- __ testb(temp.reg(), Immediate(kIsSymbolMask)); // Test the symbol bit.
- __ j(zero, ¬_a_symbol);
- // They are symbols, so do identity compare.
- __ Cmp(left_reg, right_side.handle());
- dest->true_target()->Branch(equal);
- dest->false_target()->Branch(not_equal);
- __ bind(¬_a_symbol);
- }
- // Call the compare stub if the left side is not a flat ascii string.
- __ andb(temp.reg(),
- Immediate(kIsNotStringMask |
- kStringRepresentationMask |
- kStringEncodingMask));
- __ cmpb(temp.reg(),
- Immediate(kStringTag | kSeqStringTag | kAsciiStringTag));
- temp.Unuse();
- is_string.Branch(equal, &left_side);
-
- // Setup and call the compare stub.
- is_not_string.Bind(&left_side);
- CompareFlags flags =
- static_cast<CompareFlags>(CANT_BOTH_BE_NAN | NO_SMI_CODE_IN_STUB);
- CompareStub stub(cc, strict, flags);
- Result result = frame_->CallStub(&stub, &left_side, &right_side);
- result.ToRegister();
- __ testq(result.reg(), result.reg());
- result.Unuse();
- dest->true_target()->Branch(cc);
- dest->false_target()->Jump();
-
- is_string.Bind(&left_side);
- // left_side is a sequential ASCII string.
- ASSERT(left_side.reg().is(left_reg));
- right_side = Result(right_val);
- Result temp2 = allocator_->Allocate();
- ASSERT(temp2.is_valid());
- // Test string equality and comparison.
- if (cc == equal) {
- Label comparison_done;
- __ SmiCompare(FieldOperand(left_side.reg(), String::kLengthOffset),
- Smi::FromInt(1));
- __ j(not_equal, &comparison_done);
- uint8_t char_value =
- static_cast<uint8_t>(String::cast(*right_val)->Get(0));
- __ cmpb(FieldOperand(left_side.reg(), SeqAsciiString::kHeaderSize),
- Immediate(char_value));
- __ bind(&comparison_done);
- } else {
- __ movq(temp2.reg(),
- FieldOperand(left_side.reg(), String::kLengthOffset));
- __ SmiSubConstant(temp2.reg(), temp2.reg(), Smi::FromInt(1));
- Label comparison;
- // If the length is 0 then the subtraction gave -1 which compares less
- // than any character.
- __ j(negative, &comparison);
- // Otherwise load the first character.
- __ movzxbl(temp2.reg(),
- FieldOperand(left_side.reg(), SeqAsciiString::kHeaderSize));
- __ bind(&comparison);
- // Compare the first character of the string with the
- // constant 1-character string.
- uint8_t char_value =
- static_cast<uint8_t>(String::cast(*right_side.handle())->Get(0));
- __ cmpb(temp2.reg(), Immediate(char_value));
- Label characters_were_different;
- __ j(not_equal, &characters_were_different);
- // If the first character is the same then the long string sorts after
- // the short one.
- __ SmiCompare(FieldOperand(left_side.reg(), String::kLengthOffset),
- Smi::FromInt(1));
- __ bind(&characters_were_different);
- }
- temp2.Unuse();
- left_side.Unuse();
- right_side.Unuse();
- dest->Split(cc);
- }
- } else {
- // Neither side is a constant Smi, constant 1-char string, or constant null.
- // If either side is a non-smi constant, or known to be a heap number,
- // skip the smi check.
- bool known_non_smi =
- (left_side.is_constant() && !left_side.handle()->IsSmi()) ||
- (right_side.is_constant() && !right_side.handle()->IsSmi()) ||
- left_side.type_info().IsDouble() ||
- right_side.type_info().IsDouble();
-
- NaNInformation nan_info =
- (CouldBeNaN(left_side) && CouldBeNaN(right_side)) ?
- kBothCouldBeNaN :
- kCantBothBeNaN;
-
- // Inline number comparison handling any combination of smi's and heap
- // numbers if:
- // code is in a loop
- // the compare operation is different from equal
- // compare is not a for-loop comparison
- // The reason for excluding equal is that it will most likely be done
- // with smi's (not heap numbers) and the code to comparing smi's is inlined
- // separately. The same reason applies for for-loop comparison which will
- // also most likely be smi comparisons.
- bool is_loop_condition = (node->AsExpression() != NULL)
- && node->AsExpression()->is_loop_condition();
- bool inline_number_compare =
- loop_nesting() > 0 && cc != equal && !is_loop_condition;
-
- // Left and right needed in registers for the following code.
- left_side.ToRegister();
- right_side.ToRegister();
-
- if (known_non_smi) {
- // Inlined equality check:
- // If at least one of the objects is not NaN, then if the objects
- // are identical, they are equal.
- if (nan_info == kCantBothBeNaN && cc == equal) {
- __ cmpq(left_side.reg(), right_side.reg());
- dest->true_target()->Branch(equal);
- }
-
- // Inlined number comparison:
- if (inline_number_compare) {
- GenerateInlineNumberComparison(&left_side, &right_side, cc, dest);
- }
-
- // End of in-line compare, call out to the compare stub. Don't include
- // number comparison in the stub if it was inlined.
- CompareFlags flags = ComputeCompareFlags(nan_info, inline_number_compare);
- CompareStub stub(cc, strict, flags);
- Result answer = frame_->CallStub(&stub, &left_side, &right_side);
- __ testq(answer.reg(), answer.reg()); // Sets both zero and sign flag.
- answer.Unuse();
- dest->Split(cc);
- } else {
- // Here we split control flow to the stub call and inlined cases
- // before finally splitting it to the control destination. We use
- // a jump target and branching to duplicate the virtual frame at
- // the first split. We manually handle the off-frame references
- // by reconstituting them on the non-fall-through path.
- JumpTarget is_smi;
- Register left_reg = left_side.reg();
- Register right_reg = right_side.reg();
-
- // In-line check for comparing two smis.
- JumpIfBothSmiUsingTypeInfo(&left_side, &right_side, &is_smi);
-
- if (has_valid_frame()) {
- // Inline the equality check if both operands can't be a NaN. If both
- // objects are the same they are equal.
- if (nan_info == kCantBothBeNaN && cc == equal) {
- __ cmpq(left_side.reg(), right_side.reg());
- dest->true_target()->Branch(equal);
- }
-
- // Inlined number comparison:
- if (inline_number_compare) {
- GenerateInlineNumberComparison(&left_side, &right_side, cc, dest);
- }
-
- // End of in-line compare, call out to the compare stub. Don't include
- // number comparison in the stub if it was inlined.
- CompareFlags flags =
- ComputeCompareFlags(nan_info, inline_number_compare);
- CompareStub stub(cc, strict, flags);
- Result answer = frame_->CallStub(&stub, &left_side, &right_side);
- __ testq(answer.reg(), answer.reg()); // Sets both zero and sign flags.
- answer.Unuse();
- if (is_smi.is_linked()) {
- dest->true_target()->Branch(cc);
- dest->false_target()->Jump();
- } else {
- dest->Split(cc);
- }
- }
-
- if (is_smi.is_linked()) {
- is_smi.Bind();
- left_side = Result(left_reg);
- right_side = Result(right_reg);
- __ SmiCompare(left_side.reg(), right_side.reg());
- right_side.Unuse();
- left_side.Unuse();
- dest->Split(cc);
- }
- }
- }
-}
-
-
-void CodeGenerator::ConstantSmiComparison(Condition cc,
- bool strict,
- ControlDestination* dest,
- Result* left_side,
- Result* right_side,
- bool left_side_constant_smi,
- bool right_side_constant_smi,
- bool is_loop_condition) {
- if (left_side_constant_smi && right_side_constant_smi) {
- // Trivial case, comparing two constants.
- int left_value = Smi::cast(*left_side->handle())->value();
- int right_value = Smi::cast(*right_side->handle())->value();
- switch (cc) {
- case less:
- dest->Goto(left_value < right_value);
- break;
- case equal:
- dest->Goto(left_value == right_value);
- break;
- case greater_equal:
- dest->Goto(left_value >= right_value);
- break;
- default:
- UNREACHABLE();
- }
- } else {
- // Only one side is a constant Smi.
- // If left side is a constant Smi, reverse the operands.
- // Since one side is a constant Smi, conversion order does not matter.
- if (left_side_constant_smi) {
- Result* temp = left_side;
- left_side = right_side;
- right_side = temp;
- cc = ReverseCondition(cc);
- // This may re-introduce greater or less_equal as the value of cc.
- // CompareStub and the inline code both support all values of cc.
- }
- // Implement comparison against a constant Smi, inlining the case
- // where both sides are smis.
- left_side->ToRegister();
- Register left_reg = left_side->reg();
- Smi* constant_smi = Smi::cast(*right_side->handle());
-
- if (left_side->is_smi()) {
- if (FLAG_debug_code) {
- __ AbortIfNotSmi(left_reg);
- }
- // Test smi equality and comparison by signed int comparison.
- __ SmiCompare(left_reg, constant_smi);
- left_side->Unuse();
- right_side->Unuse();
- dest->Split(cc);
- } else {
- // Only the case where the left side could possibly be a non-smi is left.
- JumpTarget is_smi;
- if (cc == equal) {
- // We can do the equality comparison before the smi check.
- __ Cmp(left_reg, constant_smi);
- dest->true_target()->Branch(equal);
- Condition left_is_smi = masm_->CheckSmi(left_reg);
- dest->false_target()->Branch(left_is_smi);
- } else {
- // Do the smi check, then the comparison.
- Condition left_is_smi = masm_->CheckSmi(left_reg);
- is_smi.Branch(left_is_smi, left_side, right_side);
- }
-
- // Jump or fall through to here if we are comparing a non-smi to a
- // constant smi. If the non-smi is a heap number and this is not
- // a loop condition, inline the floating point code.
- if (!is_loop_condition) {
- // Right side is a constant smi and left side has been checked
- // not to be a smi.
- JumpTarget not_number;
- __ Cmp(FieldOperand(left_reg, HeapObject::kMapOffset),
- FACTORY->heap_number_map());
- not_number.Branch(not_equal, left_side);
- __ movsd(xmm1,
- FieldOperand(left_reg, HeapNumber::kValueOffset));
- int value = constant_smi->value();
- if (value == 0) {
- __ xorpd(xmm0, xmm0);
- } else {
- Result temp = allocator()->Allocate();
- __ movl(temp.reg(), Immediate(value));
- __ cvtlsi2sd(xmm0, temp.reg());
- temp.Unuse();
- }
- __ ucomisd(xmm1, xmm0);
- // Jump to builtin for NaN.
- not_number.Branch(parity_even, left_side);
- left_side->Unuse();
- dest->true_target()->Branch(DoubleCondition(cc));
- dest->false_target()->Jump();
- not_number.Bind(left_side);
- }
-
- // Setup and call the compare stub.
- CompareFlags flags =
- static_cast<CompareFlags>(CANT_BOTH_BE_NAN | NO_SMI_CODE_IN_STUB);
- CompareStub stub(cc, strict, flags);
- Result result = frame_->CallStub(&stub, left_side, right_side);
- result.ToRegister();
- __ testq(result.reg(), result.reg());
- result.Unuse();
- if (cc == equal) {
- dest->Split(cc);
- } else {
- dest->true_target()->Branch(cc);
- dest->false_target()->Jump();
-
- // It is important for performance for this case to be at the end.
- is_smi.Bind(left_side, right_side);
- __ SmiCompare(left_reg, constant_smi);
- left_side->Unuse();
- right_side->Unuse();
- dest->Split(cc);
- }
- }
- }
-}
-
-
-// Load a comparison operand into into a XMM register. Jump to not_numbers jump
-// target passing the left and right result if the operand is not a number.
-static void LoadComparisonOperand(MacroAssembler* masm_,
- Result* operand,
- XMMRegister xmm_reg,
- Result* left_side,
- Result* right_side,
- JumpTarget* not_numbers) {
- Label done;
- if (operand->type_info().IsDouble()) {
- // Operand is known to be a heap number, just load it.
- __ movsd(xmm_reg, FieldOperand(operand->reg(), HeapNumber::kValueOffset));
- } else if (operand->type_info().IsSmi()) {
- // Operand is known to be a smi. Convert it to double and keep the original
- // smi.
- __ SmiToInteger32(kScratchRegister, operand->reg());
- __ cvtlsi2sd(xmm_reg, kScratchRegister);
- } else {
- // Operand type not known, check for smi or heap number.
- Label smi;
- __ JumpIfSmi(operand->reg(), &smi);
- if (!operand->type_info().IsNumber()) {
- __ LoadRoot(kScratchRegister, Heap::kHeapNumberMapRootIndex);
- __ cmpq(FieldOperand(operand->reg(), HeapObject::kMapOffset),
- kScratchRegister);
- not_numbers->Branch(not_equal, left_side, right_side, taken);
- }
- __ movsd(xmm_reg, FieldOperand(operand->reg(), HeapNumber::kValueOffset));
- __ jmp(&done);
-
- __ bind(&smi);
- // Comvert smi to float and keep the original smi.
- __ SmiToInteger32(kScratchRegister, operand->reg());
- __ cvtlsi2sd(xmm_reg, kScratchRegister);
- __ jmp(&done);
- }
- __ bind(&done);
-}
-
-
-void CodeGenerator::GenerateInlineNumberComparison(Result* left_side,
- Result* right_side,
- Condition cc,
- ControlDestination* dest) {
- ASSERT(left_side->is_register());
- ASSERT(right_side->is_register());
-
- JumpTarget not_numbers;
- // Load left and right operand into registers xmm0 and xmm1 and compare.
- LoadComparisonOperand(masm_, left_side, xmm0, left_side, right_side,
- ¬_numbers);
- LoadComparisonOperand(masm_, right_side, xmm1, left_side, right_side,
- ¬_numbers);
- __ ucomisd(xmm0, xmm1);
- // Bail out if a NaN is involved.
- not_numbers.Branch(parity_even, left_side, right_side);
-
- // Split to destination targets based on comparison.
- left_side->Unuse();
- right_side->Unuse();
- dest->true_target()->Branch(DoubleCondition(cc));
- dest->false_target()->Jump();
-
- not_numbers.Bind(left_side, right_side);
-}
-
-
-// Call the function just below TOS on the stack with the given
-// arguments. The receiver is the TOS.
-void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
- CallFunctionFlags flags,
- int position) {
- // Push the arguments ("left-to-right") on the stack.
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- frame_->SpillTop();
- }
-
- // Record the position for debugging purposes.
- CodeForSourcePosition(position);
-
- // Use the shared code stub to call the function.
- InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub call_function(arg_count, in_loop, flags);
- Result answer = frame_->CallStub(&call_function, arg_count + 1);
- // Restore context and replace function on the stack with the
- // result of the stub invocation.
- frame_->RestoreContextRegister();
- frame_->SetElementAt(0, &answer);
-}
-
-
-void CodeGenerator::CallApplyLazy(Expression* applicand,
- Expression* receiver,
- VariableProxy* arguments,
- int position) {
- // An optimized implementation of expressions of the form
- // x.apply(y, arguments).
- // If the arguments object of the scope has not been allocated,
- // and x.apply is Function.prototype.apply, this optimization
- // just copies y and the arguments of the current function on the
- // stack, as receiver and arguments, and calls x.
- // In the implementation comments, we call x the applicand
- // and y the receiver.
- ASSERT(ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION);
- ASSERT(arguments->IsArguments());
-
- // Load applicand.apply onto the stack. This will usually
- // give us a megamorphic load site. Not super, but it works.
- Load(applicand);
- frame()->Dup();
- Handle<String> name = FACTORY->LookupAsciiSymbol("apply");
- frame()->Push(name);
- Result answer = frame()->CallLoadIC(RelocInfo::CODE_TARGET);
- __ nop();
- frame()->Push(&answer);
-
- // Load the receiver and the existing arguments object onto the
- // expression stack. Avoid allocating the arguments object here.
- Load(receiver);
- LoadFromSlot(scope()->arguments()->AsSlot(), NOT_INSIDE_TYPEOF);
-
- // Emit the source position information after having loaded the
- // receiver and the arguments.
- CodeForSourcePosition(position);
- // Contents of frame at this point:
- // Frame[0]: arguments object of the current function or the hole.
- // Frame[1]: receiver
- // Frame[2]: applicand.apply
- // Frame[3]: applicand.
-
- // Check if the arguments object has been lazily allocated
- // already. If so, just use that instead of copying the arguments
- // from the stack. This also deals with cases where a local variable
- // named 'arguments' has been introduced.
- frame_->Dup();
- Result probe = frame_->Pop();
- { VirtualFrame::SpilledScope spilled_scope;
- Label slow, done;
- bool try_lazy = true;
- if (probe.is_constant()) {
- try_lazy = probe.handle()->IsArgumentsMarker();
- } else {
- __ CompareRoot(probe.reg(), Heap::kArgumentsMarkerRootIndex);
- probe.Unuse();
- __ j(not_equal, &slow);
- }
-
- if (try_lazy) {
- Label build_args;
- // Get rid of the arguments object probe.
- frame_->Drop(); // Can be called on a spilled frame.
- // Stack now has 3 elements on it.
- // Contents of stack at this point:
- // rsp[0]: receiver
- // rsp[1]: applicand.apply
- // rsp[2]: applicand.
-
- // Check that the receiver really is a JavaScript object.
- __ movq(rax, Operand(rsp, 0));
- Condition is_smi = masm_->CheckSmi(rax);
- __ j(is_smi, &build_args);
- // We allow all JSObjects including JSFunctions. As long as
- // JS_FUNCTION_TYPE is the last instance type and it is right
- // after LAST_JS_OBJECT_TYPE, we do not have to check the upper
- // bound.
- STATIC_ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
- STATIC_ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
- __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx);
- __ j(below, &build_args);
-
- // Check that applicand.apply is Function.prototype.apply.
- __ movq(rax, Operand(rsp, kPointerSize));
- is_smi = masm_->CheckSmi(rax);
- __ j(is_smi, &build_args);
- __ CmpObjectType(rax, JS_FUNCTION_TYPE, rcx);
- __ j(not_equal, &build_args);
- __ movq(rcx, FieldOperand(rax, JSFunction::kCodeEntryOffset));
- __ subq(rcx, Immediate(Code::kHeaderSize - kHeapObjectTag));
- Handle<Code> apply_code = Isolate::Current()->builtins()->FunctionApply();
- __ Cmp(rcx, apply_code);
- __ j(not_equal, &build_args);
-
- // Check that applicand is a function.
- __ movq(rdi, Operand(rsp, 2 * kPointerSize));
- is_smi = masm_->CheckSmi(rdi);
- __ j(is_smi, &build_args);
- __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
- __ j(not_equal, &build_args);
-
- // Copy the arguments to this function possibly from the
- // adaptor frame below it.
- Label invoke, adapted;
- __ movq(rdx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
- __ Cmp(Operand(rdx, StandardFrameConstants::kContextOffset),
- Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
- __ j(equal, &adapted);
-
- // No arguments adaptor frame. Copy fixed number of arguments.
- __ Set(rax, scope()->num_parameters());
- for (int i = 0; i < scope()->num_parameters(); i++) {
- __ push(frame_->ParameterAt(i));
- }
- __ jmp(&invoke);
-
- // Arguments adaptor frame present. Copy arguments from there, but
- // avoid copying too many arguments to avoid stack overflows.
- __ bind(&adapted);
- static const uint32_t kArgumentsLimit = 1 * KB;
- __ SmiToInteger32(rax,
- Operand(rdx,
- ArgumentsAdaptorFrameConstants::kLengthOffset));
- __ movl(rcx, rax);
- __ cmpl(rax, Immediate(kArgumentsLimit));
- __ j(above, &build_args);
-
- // Loop through the arguments pushing them onto the execution
- // stack. We don't inform the virtual frame of the push, so we don't
- // have to worry about getting rid of the elements from the virtual
- // frame.
- Label loop;
- // rcx is a small non-negative integer, due to the test above.
- __ testl(rcx, rcx);
- __ j(zero, &invoke);
- __ bind(&loop);
- __ push(Operand(rdx, rcx, times_pointer_size, 1 * kPointerSize));
- __ decl(rcx);
- __ j(not_zero, &loop);
-
- // Invoke the function.
- __ bind(&invoke);
- ParameterCount actual(rax);
- __ InvokeFunction(rdi, actual, CALL_FUNCTION);
- // Drop applicand.apply and applicand from the stack, and push
- // the result of the function call, but leave the spilled frame
- // unchanged, with 3 elements, so it is correct when we compile the
- // slow-case code.
- __ addq(rsp, Immediate(2 * kPointerSize));
- __ push(rax);
- // Stack now has 1 element:
- // rsp[0]: result
- __ jmp(&done);
-
- // Slow-case: Allocate the arguments object since we know it isn't
- // there, and fall-through to the slow-case where we call
- // applicand.apply.
- __ bind(&build_args);
- // Stack now has 3 elements, because we have jumped from where:
- // rsp[0]: receiver
- // rsp[1]: applicand.apply
- // rsp[2]: applicand.
-
- // StoreArgumentsObject requires a correct frame, and may modify it.
- Result arguments_object = StoreArgumentsObject(false);
- frame_->SpillAll();
- arguments_object.ToRegister();
- frame_->EmitPush(arguments_object.reg());
- arguments_object.Unuse();
- // Stack and frame now have 4 elements.
- __ bind(&slow);
- }
-
- // Generic computation of x.apply(y, args) with no special optimization.
- // Flip applicand.apply and applicand on the stack, so
- // applicand looks like the receiver of the applicand.apply call.
- // Then process it as a normal function call.
- __ movq(rax, Operand(rsp, 3 * kPointerSize));
- __ movq(rbx, Operand(rsp, 2 * kPointerSize));
- __ movq(Operand(rsp, 2 * kPointerSize), rax);
- __ movq(Operand(rsp, 3 * kPointerSize), rbx);
-
- CallFunctionStub call_function(2, NOT_IN_LOOP, NO_CALL_FUNCTION_FLAGS);
- Result res = frame_->CallStub(&call_function, 3);
- // The function and its two arguments have been dropped.
- frame_->Drop(1); // Drop the receiver as well.
- res.ToRegister();
- frame_->EmitPush(res.reg());
- // Stack now has 1 element:
- // rsp[0]: result
- if (try_lazy) __ bind(&done);
- } // End of spilled scope.
- // Restore the context register after a call.
- frame_->RestoreContextRegister();
-}
-
-
-class DeferredStackCheck: public DeferredCode {
- public:
- DeferredStackCheck() {
- set_comment("[ DeferredStackCheck");
- }
-
- virtual void Generate();
-};
-
-
-void DeferredStackCheck::Generate() {
- StackCheckStub stub;
- __ CallStub(&stub);
-}
-
-
-void CodeGenerator::CheckStack() {
- DeferredStackCheck* deferred = new DeferredStackCheck;
- __ CompareRoot(rsp, Heap::kStackLimitRootIndex);
- deferred->Branch(below);
- deferred->BindExit();
-}
-
-
-void CodeGenerator::VisitAndSpill(Statement* statement) {
- ASSERT(in_spilled_code());
- set_in_spilled_code(false);
- Visit(statement);
- if (frame_ != NULL) {
- frame_->SpillAll();
- }
- set_in_spilled_code(true);
-}
-
-
-void CodeGenerator::VisitStatementsAndSpill(ZoneList<Statement*>* statements) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- ASSERT(in_spilled_code());
- set_in_spilled_code(false);
- VisitStatements(statements);
- if (frame_ != NULL) {
- frame_->SpillAll();
- }
- set_in_spilled_code(true);
-
- ASSERT(!has_valid_frame() || frame_->height() == original_height);
-}
-
-
-void CodeGenerator::VisitStatements(ZoneList<Statement*>* statements) {
-#ifdef DEBUG
- int original_height = frame_->height();
-#endif
- ASSERT(!in_spilled_code());
- for (int i = 0; has_valid_frame() && i < statements->length(); i++) {
- Visit(statements->at(i));
- }
- ASSERT(!has_valid_frame() || frame_->height() == original_height);
-}
-
-
-void CodeGenerator::VisitBlock(Block* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ Block");
- CodeForStatementPosition(node);
- node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
- VisitStatements(node->statements());
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- node->break_target()->Unuse();
-}
-
-
-void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
- // Call the runtime to declare the globals. The inevitable call
- // will sync frame elements to memory anyway, so we do it eagerly to
- // allow us to push the arguments directly into place.
- frame_->SyncRange(0, frame_->element_count() - 1);
-
- __ movq(kScratchRegister, pairs, RelocInfo::EMBEDDED_OBJECT);
- frame_->EmitPush(rsi); // The context is the first argument.
- frame_->EmitPush(kScratchRegister);
- frame_->EmitPush(Smi::FromInt(is_eval() ? 1 : 0));
- frame_->EmitPush(Smi::FromInt(strict_mode_flag()));
- Result ignored = frame_->CallRuntime(Runtime::kDeclareGlobals, 4);
- // Return value is ignored.
-}
-
-
-void CodeGenerator::VisitDeclaration(Declaration* node) {
- Comment cmnt(masm_, "[ Declaration");
- Variable* var = node->proxy()->var();
- ASSERT(var != NULL); // must have been resolved
- Slot* slot = var->AsSlot();
-
- // If it was not possible to allocate the variable at compile time,
- // we need to "declare" it at runtime to make sure it actually
- // exists in the local context.
- if (slot != NULL && slot->type() == Slot::LOOKUP) {
- // Variables with a "LOOKUP" slot were introduced as non-locals
- // during variable resolution and must have mode DYNAMIC.
- ASSERT(var->is_dynamic());
- // For now, just do a runtime call. Sync the virtual frame eagerly
- // so we can simply push the arguments into place.
- frame_->SyncRange(0, frame_->element_count() - 1);
- frame_->EmitPush(rsi);
- __ movq(kScratchRegister, var->name(), RelocInfo::EMBEDDED_OBJECT);
- frame_->EmitPush(kScratchRegister);
- // Declaration nodes are always introduced in one of two modes.
- ASSERT(node->mode() == Variable::VAR || node->mode() == Variable::CONST);
- PropertyAttributes attr = node->mode() == Variable::VAR ? NONE : READ_ONLY;
- frame_->EmitPush(Smi::FromInt(attr));
- // Push initial value, if any.
- // Note: For variables we must not push an initial value (such as
- // 'undefined') because we may have a (legal) redeclaration and we
- // must not destroy the current value.
- if (node->mode() == Variable::CONST) {
- frame_->EmitPush(Heap::kTheHoleValueRootIndex);
- } else if (node->fun() != NULL) {
- Load(node->fun());
- } else {
- frame_->EmitPush(Smi::FromInt(0)); // no initial value!
- }
- Result ignored = frame_->CallRuntime(Runtime::kDeclareContextSlot, 4);
- // Ignore the return value (declarations are statements).
- return;
- }
-
- ASSERT(!var->is_global());
-
- // If we have a function or a constant, we need to initialize the variable.
- Expression* val = NULL;
- if (node->mode() == Variable::CONST) {
- val = new Literal(FACTORY->the_hole_value());
- } else {
- val = node->fun(); // NULL if we don't have a function
- }
-
- if (val != NULL) {
- {
- // Set the initial value.
- Reference target(this, node->proxy());
- Load(val);
- target.SetValue(NOT_CONST_INIT);
- // The reference is removed from the stack (preserving TOS) when
- // it goes out of scope.
- }
- // Get rid of the assigned value (declarations are statements).
- frame_->Drop();
- }
-}
-
-
-void CodeGenerator::VisitExpressionStatement(ExpressionStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ ExpressionStatement");
- CodeForStatementPosition(node);
- Expression* expression = node->expression();
- expression->MarkAsStatement();
- Load(expression);
- // Remove the lingering expression result from the top of stack.
- frame_->Drop();
-}
-
-
-void CodeGenerator::VisitEmptyStatement(EmptyStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "// EmptyStatement");
- CodeForStatementPosition(node);
- // nothing to do
-}
-
-
-void CodeGenerator::VisitIfStatement(IfStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ IfStatement");
- // Generate different code depending on which parts of the if statement
- // are present or not.
- bool has_then_stm = node->HasThenStatement();
- bool has_else_stm = node->HasElseStatement();
-
- CodeForStatementPosition(node);
- JumpTarget exit;
- if (has_then_stm && has_else_stm) {
- JumpTarget then;
- JumpTarget else_;
- ControlDestination dest(&then, &else_, true);
- LoadCondition(node->condition(), &dest, true);
-
- if (dest.false_was_fall_through()) {
- // The else target was bound, so we compile the else part first.
- Visit(node->else_statement());
-
- // We may have dangling jumps to the then part.
- if (then.is_linked()) {
- if (has_valid_frame()) exit.Jump();
- then.Bind();
- Visit(node->then_statement());
- }
- } else {
- // The then target was bound, so we compile the then part first.
- Visit(node->then_statement());
-
- if (else_.is_linked()) {
- if (has_valid_frame()) exit.Jump();
- else_.Bind();
- Visit(node->else_statement());
- }
- }
-
- } else if (has_then_stm) {
- ASSERT(!has_else_stm);
- JumpTarget then;
- ControlDestination dest(&then, &exit, true);
- LoadCondition(node->condition(), &dest, true);
-
- if (dest.false_was_fall_through()) {
- // The exit label was bound. We may have dangling jumps to the
- // then part.
- if (then.is_linked()) {
- exit.Unuse();
- exit.Jump();
- then.Bind();
- Visit(node->then_statement());
- }
- } else {
- // The then label was bound.
- Visit(node->then_statement());
- }
-
- } else if (has_else_stm) {
- ASSERT(!has_then_stm);
- JumpTarget else_;
- ControlDestination dest(&exit, &else_, false);
- LoadCondition(node->condition(), &dest, true);
-
- if (dest.true_was_fall_through()) {
- // The exit label was bound. We may have dangling jumps to the
- // else part.
- if (else_.is_linked()) {
- exit.Unuse();
- exit.Jump();
- else_.Bind();
- Visit(node->else_statement());
- }
- } else {
- // The else label was bound.
- Visit(node->else_statement());
- }
-
- } else {
- ASSERT(!has_then_stm && !has_else_stm);
- // We only care about the condition's side effects (not its value
- // or control flow effect). LoadCondition is called without
- // forcing control flow.
- ControlDestination dest(&exit, &exit, true);
- LoadCondition(node->condition(), &dest, false);
- if (!dest.is_used()) {
- // We got a value on the frame rather than (or in addition to)
- // control flow.
- frame_->Drop();
- }
- }
-
- if (exit.is_linked()) {
- exit.Bind();
- }
-}
-
-
-void CodeGenerator::VisitContinueStatement(ContinueStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ ContinueStatement");
- CodeForStatementPosition(node);
- node->target()->continue_target()->Jump();
-}
-
-
-void CodeGenerator::VisitBreakStatement(BreakStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ BreakStatement");
- CodeForStatementPosition(node);
- node->target()->break_target()->Jump();
-}
-
-
-void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ ReturnStatement");
-
- CodeForStatementPosition(node);
- Load(node->expression());
- Result return_value = frame_->Pop();
- masm()->positions_recorder()->WriteRecordedPositions();
- if (function_return_is_shadowed_) {
- function_return_.Jump(&return_value);
- } else {
- frame_->PrepareForReturn();
- if (function_return_.is_bound()) {
- // If the function return label is already bound we reuse the
- // code by jumping to the return site.
- function_return_.Jump(&return_value);
- } else {
- function_return_.Bind(&return_value);
- GenerateReturnSequence(&return_value);
- }
- }
-}
-
-
-void CodeGenerator::GenerateReturnSequence(Result* return_value) {
- // The return value is a live (but not currently reference counted)
- // reference to rax. This is safe because the current frame does not
- // contain a reference to rax (it is prepared for the return by spilling
- // all registers).
- if (FLAG_trace) {
- frame_->Push(return_value);
- *return_value = frame_->CallRuntime(Runtime::kTraceExit, 1);
- }
- return_value->ToRegister(rax);
-
- // Add a label for checking the size of the code used for returning.
-#ifdef DEBUG
- Label check_exit_codesize;
- masm_->bind(&check_exit_codesize);
-#endif
-
- // Leave the frame and return popping the arguments and the
- // receiver.
- frame_->Exit();
- int arguments_bytes = (scope()->num_parameters() + 1) * kPointerSize;
- __ Ret(arguments_bytes, rcx);
- DeleteFrame();
-
-#ifdef ENABLE_DEBUGGER_SUPPORT
- // Add padding that will be overwritten by a debugger breakpoint.
- // The shortest return sequence generated is "movq rsp, rbp; pop rbp; ret k"
- // with length 7 (3 + 1 + 3).
- const int kPadding = Assembler::kJSReturnSequenceLength - 7;
- for (int i = 0; i < kPadding; ++i) {
- masm_->int3();
- }
- // Check that the size of the code used for returning is large enough
- // for the debugger's requirements.
- ASSERT(Assembler::kJSReturnSequenceLength <=
- masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
-#endif
-}
-
-
-void CodeGenerator::VisitWithEnterStatement(WithEnterStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ WithEnterStatement");
- CodeForStatementPosition(node);
- Load(node->expression());
- Result context;
- if (node->is_catch_block()) {
- context = frame_->CallRuntime(Runtime::kPushCatchContext, 1);
- } else {
- context = frame_->CallRuntime(Runtime::kPushContext, 1);
- }
-
- // Update context local.
- frame_->SaveContextRegister();
-
- // Verify that the runtime call result and rsi agree.
- if (FLAG_debug_code) {
- __ cmpq(context.reg(), rsi);
- __ Assert(equal, "Runtime::NewContext should end up in rsi");
- }
-}
-
-
-void CodeGenerator::VisitWithExitStatement(WithExitStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ WithExitStatement");
- CodeForStatementPosition(node);
- // Pop context.
- __ movq(rsi, ContextOperand(rsi, Context::PREVIOUS_INDEX));
- // Update context local.
- frame_->SaveContextRegister();
-}
-
-
-void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ SwitchStatement");
- CodeForStatementPosition(node);
- node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
-
- // Compile the switch value.
- Load(node->tag());
-
- ZoneList<CaseClause*>* cases = node->cases();
- int length = cases->length();
- CaseClause* default_clause = NULL;
-
- JumpTarget next_test;
- // Compile the case label expressions and comparisons. Exit early
- // if a comparison is unconditionally true. The target next_test is
- // bound before the loop in order to indicate control flow to the
- // first comparison.
- next_test.Bind();
- for (int i = 0; i < length && !next_test.is_unused(); i++) {
- CaseClause* clause = cases->at(i);
- // The default is not a test, but remember it for later.
- if (clause->is_default()) {
- default_clause = clause;
- continue;
- }
-
- Comment cmnt(masm_, "[ Case comparison");
- // We recycle the same target next_test for each test. Bind it if
- // the previous test has not done so and then unuse it for the
- // loop.
- if (next_test.is_linked()) {
- next_test.Bind();
- }
- next_test.Unuse();
-
- // Duplicate the switch value.
- frame_->Dup();
-
- // Compile the label expression.
- Load(clause->label());
-
- // Compare and branch to the body if true or the next test if
- // false. Prefer the next test as a fall through.
- ControlDestination dest(clause->body_target(), &next_test, false);
- Comparison(node, equal, true, &dest);
-
- // If the comparison fell through to the true target, jump to the
- // actual body.
- if (dest.true_was_fall_through()) {
- clause->body_target()->Unuse();
- clause->body_target()->Jump();
- }
- }
-
- // If there was control flow to a next test from the last one
- // compiled, compile a jump to the default or break target.
- if (!next_test.is_unused()) {
- if (next_test.is_linked()) {
- next_test.Bind();
- }
- // Drop the switch value.
- frame_->Drop();
- if (default_clause != NULL) {
- default_clause->body_target()->Jump();
- } else {
- node->break_target()->Jump();
- }
- }
-
- // The last instruction emitted was a jump, either to the default
- // clause or the break target, or else to a case body from the loop
- // that compiles the tests.
- ASSERT(!has_valid_frame());
- // Compile case bodies as needed.
- for (int i = 0; i < length; i++) {
- CaseClause* clause = cases->at(i);
-
- // There are two ways to reach the body: from the corresponding
- // test or as the fall through of the previous body.
- if (clause->body_target()->is_linked() || has_valid_frame()) {
- if (clause->body_target()->is_linked()) {
- if (has_valid_frame()) {
- // If we have both a jump to the test and a fall through, put
- // a jump on the fall through path to avoid the dropping of
- // the switch value on the test path. The exception is the
- // default which has already had the switch value dropped.
- if (clause->is_default()) {
- clause->body_target()->Bind();
- } else {
- JumpTarget body;
- body.Jump();
- clause->body_target()->Bind();
- frame_->Drop();
- body.Bind();
- }
- } else {
- // No fall through to worry about.
- clause->body_target()->Bind();
- if (!clause->is_default()) {
- frame_->Drop();
- }
- }
- } else {
- // Otherwise, we have only fall through.
- ASSERT(has_valid_frame());
- }
-
- // We are now prepared to compile the body.
- Comment cmnt(masm_, "[ Case body");
- VisitStatements(clause->statements());
- }
- clause->body_target()->Unuse();
- }
-
- // We may not have a valid frame here so bind the break target only
- // if needed.
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- node->break_target()->Unuse();
-}
-
-
-void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ DoWhileStatement");
- CodeForStatementPosition(node);
- node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
- JumpTarget body(JumpTarget::BIDIRECTIONAL);
- IncrementLoopNesting();
-
- ConditionAnalysis info = AnalyzeCondition(node->cond());
- // Label the top of the loop for the backward jump if necessary.
- switch (info) {
- case ALWAYS_TRUE:
- // Use the continue target.
- node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
- node->continue_target()->Bind();
- break;
- case ALWAYS_FALSE:
- // No need to label it.
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
- break;
- case DONT_KNOW:
- // Continue is the test, so use the backward body target.
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
- body.Bind();
- break;
- }
-
- CheckStack(); // TODO(1222600): ignore if body contains calls.
- Visit(node->body());
-
- // Compile the test.
- switch (info) {
- case ALWAYS_TRUE:
- // If control flow can fall off the end of the body, jump back
- // to the top and bind the break target at the exit.
- if (has_valid_frame()) {
- node->continue_target()->Jump();
- }
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- break;
- case ALWAYS_FALSE:
- // We may have had continues or breaks in the body.
- if (node->continue_target()->is_linked()) {
- node->continue_target()->Bind();
- }
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- break;
- case DONT_KNOW:
- // We have to compile the test expression if it can be reached by
- // control flow falling out of the body or via continue.
- if (node->continue_target()->is_linked()) {
- node->continue_target()->Bind();
- }
- if (has_valid_frame()) {
- Comment cmnt(masm_, "[ DoWhileCondition");
- CodeForDoWhileConditionPosition(node);
- ControlDestination dest(&body, node->break_target(), false);
- LoadCondition(node->cond(), &dest, true);
- }
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- break;
- }
-
- DecrementLoopNesting();
- node->continue_target()->Unuse();
- node->break_target()->Unuse();
-}
-
-
-void CodeGenerator::VisitWhileStatement(WhileStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ WhileStatement");
- CodeForStatementPosition(node);
-
- // If the condition is always false and has no side effects, we do not
- // need to compile anything.
- ConditionAnalysis info = AnalyzeCondition(node->cond());
- if (info == ALWAYS_FALSE) return;
-
- // Do not duplicate conditions that may have function literal
- // subexpressions. This can cause us to compile the function literal
- // twice.
- bool test_at_bottom = !node->may_have_function_literal();
- node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
- IncrementLoopNesting();
- JumpTarget body;
- if (test_at_bottom) {
- body.set_direction(JumpTarget::BIDIRECTIONAL);
- }
-
- // Based on the condition analysis, compile the test as necessary.
- switch (info) {
- case ALWAYS_TRUE:
- // We will not compile the test expression. Label the top of the
- // loop with the continue target.
- node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
- node->continue_target()->Bind();
- break;
- case DONT_KNOW: {
- if (test_at_bottom) {
- // Continue is the test at the bottom, no need to label the test
- // at the top. The body is a backward target.
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
- } else {
- // Label the test at the top as the continue target. The body
- // is a forward-only target.
- node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
- node->continue_target()->Bind();
- }
- // Compile the test with the body as the true target and preferred
- // fall-through and with the break target as the false target.
- ControlDestination dest(&body, node->break_target(), true);
- LoadCondition(node->cond(), &dest, true);
-
- if (dest.false_was_fall_through()) {
- // If we got the break target as fall-through, the test may have
- // been unconditionally false (if there are no jumps to the
- // body).
- if (!body.is_linked()) {
- DecrementLoopNesting();
- return;
- }
-
- // Otherwise, jump around the body on the fall through and then
- // bind the body target.
- node->break_target()->Unuse();
- node->break_target()->Jump();
- body.Bind();
- }
- break;
- }
- case ALWAYS_FALSE:
- UNREACHABLE();
- break;
- }
-
- CheckStack(); // TODO(1222600): ignore if body contains calls.
- Visit(node->body());
-
- // Based on the condition analysis, compile the backward jump as
- // necessary.
- switch (info) {
- case ALWAYS_TRUE:
- // The loop body has been labeled with the continue target.
- if (has_valid_frame()) {
- node->continue_target()->Jump();
- }
- break;
- case DONT_KNOW:
- if (test_at_bottom) {
- // If we have chosen to recompile the test at the bottom,
- // then it is the continue target.
- if (node->continue_target()->is_linked()) {
- node->continue_target()->Bind();
- }
- if (has_valid_frame()) {
- // The break target is the fall-through (body is a backward
- // jump from here and thus an invalid fall-through).
- ControlDestination dest(&body, node->break_target(), false);
- LoadCondition(node->cond(), &dest, true);
- }
- } else {
- // If we have chosen not to recompile the test at the bottom,
- // jump back to the one at the top.
- if (has_valid_frame()) {
- node->continue_target()->Jump();
- }
- }
- break;
- case ALWAYS_FALSE:
- UNREACHABLE();
- break;
- }
-
- // The break target may be already bound (by the condition), or there
- // may not be a valid frame. Bind it only if needed.
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- DecrementLoopNesting();
-}
-
-
-void CodeGenerator::SetTypeForStackSlot(Slot* slot, TypeInfo info) {
- ASSERT(slot->type() == Slot::LOCAL || slot->type() == Slot::PARAMETER);
- if (slot->type() == Slot::LOCAL) {
- frame_->SetTypeForLocalAt(slot->index(), info);
- } else {
- frame_->SetTypeForParamAt(slot->index(), info);
- }
- if (FLAG_debug_code && info.IsSmi()) {
- if (slot->type() == Slot::LOCAL) {
- frame_->PushLocalAt(slot->index());
- } else {
- frame_->PushParameterAt(slot->index());
- }
- Result var = frame_->Pop();
- var.ToRegister();
- __ AbortIfNotSmi(var.reg());
- }
-}
-
-
-void CodeGenerator::GenerateFastSmiLoop(ForStatement* node) {
- // A fast smi loop is a for loop with an initializer
- // that is a simple assignment of a smi to a stack variable,
- // a test that is a simple test of that variable against a smi constant,
- // and a step that is a increment/decrement of the variable, and
- // where the variable isn't modified in the loop body.
- // This guarantees that the variable is always a smi.
-
- Variable* loop_var = node->loop_variable();
- Smi* initial_value = *Handle<Smi>::cast(node->init()
- ->StatementAsSimpleAssignment()->value()->AsLiteral()->handle());
- Smi* limit_value = *Handle<Smi>::cast(
- node->cond()->AsCompareOperation()->right()->AsLiteral()->handle());
- Token::Value compare_op =
- node->cond()->AsCompareOperation()->op();
- bool increments =
- node->next()->StatementAsCountOperation()->op() == Token::INC;
-
- // Check that the condition isn't initially false.
- bool initially_false = false;
- int initial_int_value = initial_value->value();
- int limit_int_value = limit_value->value();
- switch (compare_op) {
- case Token::LT:
- initially_false = initial_int_value >= limit_int_value;
- break;
- case Token::LTE:
- initially_false = initial_int_value > limit_int_value;
- break;
- case Token::GT:
- initially_false = initial_int_value <= limit_int_value;
- break;
- case Token::GTE:
- initially_false = initial_int_value < limit_int_value;
- break;
- default:
- UNREACHABLE();
- }
- if (initially_false) return;
-
- // Only check loop condition at the end.
-
- Visit(node->init());
-
- JumpTarget loop(JumpTarget::BIDIRECTIONAL);
- // Set type and stack height of BreakTargets.
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
- node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
-
- IncrementLoopNesting();
- loop.Bind();
-
- // Set number type of the loop variable to smi.
- CheckStack(); // TODO(1222600): ignore if body contains calls.
-
- SetTypeForStackSlot(loop_var->AsSlot(), TypeInfo::Smi());
- Visit(node->body());
-
- if (node->continue_target()->is_linked()) {
- node->continue_target()->Bind();
- }
-
- if (has_valid_frame()) {
- CodeForStatementPosition(node);
- Slot* loop_var_slot = loop_var->AsSlot();
- if (loop_var_slot->type() == Slot::LOCAL) {
- frame_->TakeLocalAt(loop_var_slot->index());
- } else {
- ASSERT(loop_var_slot->type() == Slot::PARAMETER);
- frame_->TakeParameterAt(loop_var_slot->index());
- }
- Result loop_var_result = frame_->Pop();
- if (!loop_var_result.is_register()) {
- loop_var_result.ToRegister();
- }
- Register loop_var_reg = loop_var_result.reg();
- frame_->Spill(loop_var_reg);
- if (increments) {
- __ SmiAddConstant(loop_var_reg,
- loop_var_reg,
- Smi::FromInt(1));
- } else {
- __ SmiSubConstant(loop_var_reg,
- loop_var_reg,
- Smi::FromInt(1));
- }
-
- frame_->Push(&loop_var_result);
- if (loop_var_slot->type() == Slot::LOCAL) {
- frame_->StoreToLocalAt(loop_var_slot->index());
- } else {
- ASSERT(loop_var_slot->type() == Slot::PARAMETER);
- frame_->StoreToParameterAt(loop_var_slot->index());
- }
- frame_->Drop();
-
- __ SmiCompare(loop_var_reg, limit_value);
- Condition condition;
- switch (compare_op) {
- case Token::LT:
- condition = less;
- break;
- case Token::LTE:
- condition = less_equal;
- break;
- case Token::GT:
- condition = greater;
- break;
- case Token::GTE:
- condition = greater_equal;
- break;
- default:
- condition = never;
- UNREACHABLE();
- }
- loop.Branch(condition);
- }
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- DecrementLoopNesting();
-}
-
-
-void CodeGenerator::VisitForStatement(ForStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ ForStatement");
- CodeForStatementPosition(node);
-
- if (node->is_fast_smi_loop()) {
- GenerateFastSmiLoop(node);
- return;
- }
-
- // Compile the init expression if present.
- if (node->init() != NULL) {
- Visit(node->init());
- }
-
- // If the condition is always false and has no side effects, we do not
- // need to compile anything else.
- ConditionAnalysis info = AnalyzeCondition(node->cond());
- if (info == ALWAYS_FALSE) return;
-
- // Do not duplicate conditions that may have function literal
- // subexpressions. This can cause us to compile the function literal
- // twice.
- bool test_at_bottom = !node->may_have_function_literal();
- node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
- IncrementLoopNesting();
-
- // Target for backward edge if no test at the bottom, otherwise
- // unused.
- JumpTarget loop(JumpTarget::BIDIRECTIONAL);
-
- // Target for backward edge if there is a test at the bottom,
- // otherwise used as target for test at the top.
- JumpTarget body;
- if (test_at_bottom) {
- body.set_direction(JumpTarget::BIDIRECTIONAL);
- }
-
- // Based on the condition analysis, compile the test as necessary.
- switch (info) {
- case ALWAYS_TRUE:
- // We will not compile the test expression. Label the top of the
- // loop.
- if (node->next() == NULL) {
- // Use the continue target if there is no update expression.
- node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
- node->continue_target()->Bind();
- } else {
- // Otherwise use the backward loop target.
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
- loop.Bind();
- }
- break;
- case DONT_KNOW: {
- if (test_at_bottom) {
- // Continue is either the update expression or the test at the
- // bottom, no need to label the test at the top.
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
- } else if (node->next() == NULL) {
- // We are not recompiling the test at the bottom and there is no
- // update expression.
- node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
- node->continue_target()->Bind();
- } else {
- // We are not recompiling the test at the bottom and there is an
- // update expression.
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
- loop.Bind();
- }
-
- // Compile the test with the body as the true target and preferred
- // fall-through and with the break target as the false target.
- ControlDestination dest(&body, node->break_target(), true);
- LoadCondition(node->cond(), &dest, true);
-
- if (dest.false_was_fall_through()) {
- // If we got the break target as fall-through, the test may have
- // been unconditionally false (if there are no jumps to the
- // body).
- if (!body.is_linked()) {
- DecrementLoopNesting();
- return;
- }
-
- // Otherwise, jump around the body on the fall through and then
- // bind the body target.
- node->break_target()->Unuse();
- node->break_target()->Jump();
- body.Bind();
- }
- break;
- }
- case ALWAYS_FALSE:
- UNREACHABLE();
- break;
- }
-
- CheckStack(); // TODO(1222600): ignore if body contains calls.
-
- Visit(node->body());
-
- // If there is an update expression, compile it if necessary.
- if (node->next() != NULL) {
- if (node->continue_target()->is_linked()) {
- node->continue_target()->Bind();
- }
-
- // Control can reach the update by falling out of the body or by a
- // continue.
- if (has_valid_frame()) {
- // Record the source position of the statement as this code which
- // is after the code for the body actually belongs to the loop
- // statement and not the body.
- CodeForStatementPosition(node);
- Visit(node->next());
- }
- }
-
- // Based on the condition analysis, compile the backward jump as
- // necessary.
- switch (info) {
- case ALWAYS_TRUE:
- if (has_valid_frame()) {
- if (node->next() == NULL) {
- node->continue_target()->Jump();
- } else {
- loop.Jump();
- }
- }
- break;
- case DONT_KNOW:
- if (test_at_bottom) {
- if (node->continue_target()->is_linked()) {
- // We can have dangling jumps to the continue target if there
- // was no update expression.
- node->continue_target()->Bind();
- }
- // Control can reach the test at the bottom by falling out of
- // the body, by a continue in the body, or from the update
- // expression.
- if (has_valid_frame()) {
- // The break target is the fall-through (body is a backward
- // jump from here).
- ControlDestination dest(&body, node->break_target(), false);
- LoadCondition(node->cond(), &dest, true);
- }
- } else {
- // Otherwise, jump back to the test at the top.
- if (has_valid_frame()) {
- if (node->next() == NULL) {
- node->continue_target()->Jump();
- } else {
- loop.Jump();
- }
- }
- }
- break;
- case ALWAYS_FALSE:
- UNREACHABLE();
- break;
- }
-
- // The break target may be already bound (by the condition), or there
- // may not be a valid frame. Bind it only if needed.
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
- }
- DecrementLoopNesting();
-}
-
-
-void CodeGenerator::VisitForInStatement(ForInStatement* node) {
- ASSERT(!in_spilled_code());
- VirtualFrame::SpilledScope spilled_scope;
- Comment cmnt(masm_, "[ ForInStatement");
- CodeForStatementPosition(node);
-
- JumpTarget primitive;
- JumpTarget jsobject;
- JumpTarget fixed_array;
- JumpTarget entry(JumpTarget::BIDIRECTIONAL);
- JumpTarget end_del_check;
- JumpTarget exit;
-
- // Get the object to enumerate over (converted to JSObject).
- LoadAndSpill(node->enumerable());
-
- // Both SpiderMonkey and kjs ignore null and undefined in contrast
- // to the specification. 12.6.4 mandates a call to ToObject.
- frame_->EmitPop(rax);
-
- // rax: value to be iterated over
- __ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
- exit.Branch(equal);
- __ CompareRoot(rax, Heap::kNullValueRootIndex);
- exit.Branch(equal);
-
- // Stack layout in body:
- // [iteration counter (smi)] <- slot 0
- // [length of array] <- slot 1
- // [FixedArray] <- slot 2
- // [Map or 0] <- slot 3
- // [Object] <- slot 4
-
- // Check if enumerable is already a JSObject
- // rax: value to be iterated over
- Condition is_smi = masm_->CheckSmi(rax);
- primitive.Branch(is_smi);
- __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx);
- jsobject.Branch(above_equal);
-
- primitive.Bind();
- frame_->EmitPush(rax);
- frame_->InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION, 1);
- // function call returns the value in rax, which is where we want it below
-
- jsobject.Bind();
- // Get the set of properties (as a FixedArray or Map).
- // rax: value to be iterated over
- frame_->EmitPush(rax); // Push the object being iterated over.
-
-
- // Check cache validity in generated code. This is a fast case for
- // the JSObject::IsSimpleEnum cache validity checks. If we cannot
- // guarantee cache validity, call the runtime system to check cache
- // validity or get the property names in a fixed array.
- JumpTarget call_runtime;
- JumpTarget loop(JumpTarget::BIDIRECTIONAL);
- JumpTarget check_prototype;
- JumpTarget use_cache;
- __ movq(rcx, rax);
- loop.Bind();
- // Check that there are no elements.
- __ movq(rdx, FieldOperand(rcx, JSObject::kElementsOffset));
- __ CompareRoot(rdx, Heap::kEmptyFixedArrayRootIndex);
- call_runtime.Branch(not_equal);
- // Check that instance descriptors are not empty so that we can
- // check for an enum cache. Leave the map in ebx for the subsequent
- // prototype load.
- __ movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset));
- __ movq(rdx, FieldOperand(rbx, Map::kInstanceDescriptorsOffset));
- __ CompareRoot(rdx, Heap::kEmptyDescriptorArrayRootIndex);
- call_runtime.Branch(equal);
- // Check that there in an enum cache in the non-empty instance
- // descriptors. This is the case if the next enumeration index
- // field does not contain a smi.
- __ movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumerationIndexOffset));
- is_smi = masm_->CheckSmi(rdx);
- call_runtime.Branch(is_smi);
- // For all objects but the receiver, check that the cache is empty.
- __ cmpq(rcx, rax);
- check_prototype.Branch(equal);
- __ movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumCacheBridgeCacheOffset));
- __ CompareRoot(rdx, Heap::kEmptyFixedArrayRootIndex);
- call_runtime.Branch(not_equal);
- check_prototype.Bind();
- // Load the prototype from the map and loop if non-null.
- __ movq(rcx, FieldOperand(rbx, Map::kPrototypeOffset));
- __ CompareRoot(rcx, Heap::kNullValueRootIndex);
- loop.Branch(not_equal);
- // The enum cache is valid. Load the map of the object being
- // iterated over and use the cache for the iteration.
- __ movq(rax, FieldOperand(rax, HeapObject::kMapOffset));
- use_cache.Jump();
-
- call_runtime.Bind();
- // Call the runtime to get the property names for the object.
- frame_->EmitPush(rax); // push the Object (slot 4) for the runtime call
- frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1);
-
- // If we got a Map, we can do a fast modification check.
- // Otherwise, we got a FixedArray, and we have to do a slow check.
- // rax: map or fixed array (result from call to
- // Runtime::kGetPropertyNamesFast)
- __ movq(rdx, rax);
- __ movq(rcx, FieldOperand(rdx, HeapObject::kMapOffset));
- __ CompareRoot(rcx, Heap::kMetaMapRootIndex);
- fixed_array.Branch(not_equal);
-
- use_cache.Bind();
- // Get enum cache
- // rax: map (either the result from a call to
- // Runtime::kGetPropertyNamesFast or has been fetched directly from
- // the object)
- __ movq(rcx, rax);
- __ movq(rcx, FieldOperand(rcx, Map::kInstanceDescriptorsOffset));
- // Get the bridge array held in the enumeration index field.
- __ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumerationIndexOffset));
- // Get the cache from the bridge array.
- __ movq(rdx, FieldOperand(rcx, DescriptorArray::kEnumCacheBridgeCacheOffset));
-
- frame_->EmitPush(rax); // <- slot 3
- frame_->EmitPush(rdx); // <- slot 2
- __ movq(rax, FieldOperand(rdx, FixedArray::kLengthOffset));
- frame_->EmitPush(rax); // <- slot 1
- frame_->EmitPush(Smi::FromInt(0)); // <- slot 0
- entry.Jump();
-
- fixed_array.Bind();
- // rax: fixed array (result from call to Runtime::kGetPropertyNamesFast)
- frame_->EmitPush(Smi::FromInt(0)); // <- slot 3
- frame_->EmitPush(rax); // <- slot 2
-
- // Push the length of the array and the initial index onto the stack.
- __ movq(rax, FieldOperand(rax, FixedArray::kLengthOffset));
- frame_->EmitPush(rax); // <- slot 1
- frame_->EmitPush(Smi::FromInt(0)); // <- slot 0
-
- // Condition.
- entry.Bind();
- // Grab the current frame's height for the break and continue
- // targets only after all the state is pushed on the frame.
- node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
-
- __ movq(rax, frame_->ElementAt(0)); // load the current count
- __ SmiCompare(frame_->ElementAt(1), rax); // compare to the array length
- node->break_target()->Branch(below_equal);
-
- // Get the i'th entry of the array.
- __ movq(rdx, frame_->ElementAt(2));
- SmiIndex index = masm_->SmiToIndex(rbx, rax, kPointerSizeLog2);
- __ movq(rbx,
- FieldOperand(rdx, index.reg, index.scale, FixedArray::kHeaderSize));
-
- // Get the expected map from the stack or a zero map in the
- // permanent slow case rax: current iteration count rbx: i'th entry
- // of the enum cache
- __ movq(rdx, frame_->ElementAt(3));
- // Check if the expected map still matches that of the enumerable.
- // If not, we have to filter the key.
- // rax: current iteration count
- // rbx: i'th entry of the enum cache
- // rdx: expected map value
- __ movq(rcx, frame_->ElementAt(4));
- __ movq(rcx, FieldOperand(rcx, HeapObject::kMapOffset));
- __ cmpq(rcx, rdx);
- end_del_check.Branch(equal);
-
- // Convert the entry to a string (or null if it isn't a property anymore).
- frame_->EmitPush(frame_->ElementAt(4)); // push enumerable
- frame_->EmitPush(rbx); // push entry
- frame_->InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION, 2);
- __ movq(rbx, rax);
-
- // If the property has been removed while iterating, we just skip it.
- __ Cmp(rbx, Smi::FromInt(0));
- node->continue_target()->Branch(equal);
-
- end_del_check.Bind();
- // Store the entry in the 'each' expression and take another spin in the
- // loop. rdx: i'th entry of the enum cache (or string there of)
- frame_->EmitPush(rbx);
- { Reference each(this, node->each());
- // Loading a reference may leave the frame in an unspilled state.
- frame_->SpillAll();
- if (!each.is_illegal()) {
- if (each.size() > 0) {
- frame_->EmitPush(frame_->ElementAt(each.size()));
- each.SetValue(NOT_CONST_INIT);
- frame_->Drop(2); // Drop the original and the copy of the element.
- } else {
- // If the reference has size zero then we can use the value below
- // the reference as if it were above the reference, instead of pushing
- // a new copy of it above the reference.
- each.SetValue(NOT_CONST_INIT);
- frame_->Drop(); // Drop the original of the element.
- }
- }
- }
- // Unloading a reference may leave the frame in an unspilled state.
- frame_->SpillAll();
-
- // Body.
- CheckStack(); // TODO(1222600): ignore if body contains calls.
- VisitAndSpill(node->body());
-
- // Next. Reestablish a spilled frame in case we are coming here via
- // a continue in the body.
- node->continue_target()->Bind();
- frame_->SpillAll();
- frame_->EmitPop(rax);
- __ SmiAddConstant(rax, rax, Smi::FromInt(1));
- frame_->EmitPush(rax);
- entry.Jump();
-
- // Cleanup. No need to spill because VirtualFrame::Drop is safe for
- // any frame.
- node->break_target()->Bind();
- frame_->Drop(5);
-
- // Exit.
- exit.Bind();
-
- node->continue_target()->Unuse();
- node->break_target()->Unuse();
-}
-
-
-void CodeGenerator::VisitTryCatchStatement(TryCatchStatement* node) {
- ASSERT(!in_spilled_code());
- VirtualFrame::SpilledScope spilled_scope;
- Comment cmnt(masm_, "[ TryCatchStatement");
- CodeForStatementPosition(node);
-
- JumpTarget try_block;
- JumpTarget exit;
-
- try_block.Call();
- // --- Catch block ---
- frame_->EmitPush(rax);
-
- // Store the caught exception in the catch variable.
- Variable* catch_var = node->catch_var()->var();
- ASSERT(catch_var != NULL && catch_var->AsSlot() != NULL);
- StoreToSlot(catch_var->AsSlot(), NOT_CONST_INIT);
-
- // Remove the exception from the stack.
- frame_->Drop();
-
- VisitStatementsAndSpill(node->catch_block()->statements());
- if (has_valid_frame()) {
- exit.Jump();
- }
-
-
- // --- Try block ---
- try_block.Bind();
-
- frame_->PushTryHandler(TRY_CATCH_HANDLER);
- int handler_height = frame_->height();
-
- // Shadow the jump targets for all escapes from the try block, including
- // returns. During shadowing, the original target is hidden as the
- // ShadowTarget and operations on the original actually affect the
- // shadowing target.
- //
- // We should probably try to unify the escaping targets and the return
- // target.
- int nof_escapes = node->escaping_targets()->length();
- List<ShadowTarget*> shadows(1 + nof_escapes);
-
- // Add the shadow target for the function return.
- static const int kReturnShadowIndex = 0;
- shadows.Add(new ShadowTarget(&function_return_));
- bool function_return_was_shadowed = function_return_is_shadowed_;
- function_return_is_shadowed_ = true;
- ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
-
- // Add the remaining shadow targets.
- for (int i = 0; i < nof_escapes; i++) {
- shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
- }
-
- // Generate code for the statements in the try block.
- VisitStatementsAndSpill(node->try_block()->statements());
-
- // Stop the introduced shadowing and count the number of required unlinks.
- // After shadowing stops, the original targets are unshadowed and the
- // ShadowTargets represent the formerly shadowing targets.
- bool has_unlinks = false;
- for (int i = 0; i < shadows.length(); i++) {
- shadows[i]->StopShadowing();
- has_unlinks = has_unlinks || shadows[i]->is_linked();
- }
- function_return_is_shadowed_ = function_return_was_shadowed;
-
- // Get an external reference to the handler address.
- ExternalReference handler_address(Isolate::k_handler_address, isolate());
-
- // Make sure that there's nothing left on the stack above the
- // handler structure.
- if (FLAG_debug_code) {
- __ movq(kScratchRegister, handler_address);
- __ cmpq(rsp, Operand(kScratchRegister, 0));
- __ Assert(equal, "stack pointer should point to top handler");
- }
-
- // If we can fall off the end of the try block, unlink from try chain.
- if (has_valid_frame()) {
- // The next handler address is on top of the frame. Unlink from
- // the handler list and drop the rest of this handler from the
- // frame.
- STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
- __ movq(kScratchRegister, handler_address);
- frame_->EmitPop(Operand(kScratchRegister, 0));
- frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
- if (has_unlinks) {
- exit.Jump();
- }
- }
-
- // Generate unlink code for the (formerly) shadowing targets that
- // have been jumped to. Deallocate each shadow target.
- Result return_value;
- for (int i = 0; i < shadows.length(); i++) {
- if (shadows[i]->is_linked()) {
- // Unlink from try chain; be careful not to destroy the TOS if
- // there is one.
- if (i == kReturnShadowIndex) {
- shadows[i]->Bind(&return_value);
- return_value.ToRegister(rax);
- } else {
- shadows[i]->Bind();
- }
- // Because we can be jumping here (to spilled code) from
- // unspilled code, we need to reestablish a spilled frame at
- // this block.
- frame_->SpillAll();
-
- // Reload sp from the top handler, because some statements that we
- // break from (eg, for...in) may have left stuff on the stack.
- __ movq(kScratchRegister, handler_address);
- __ movq(rsp, Operand(kScratchRegister, 0));
- frame_->Forget(frame_->height() - handler_height);
-
- STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
- __ movq(kScratchRegister, handler_address);
- frame_->EmitPop(Operand(kScratchRegister, 0));
- frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
-
- if (i == kReturnShadowIndex) {
- if (!function_return_is_shadowed_) frame_->PrepareForReturn();
- shadows[i]->other_target()->Jump(&return_value);
- } else {
- shadows[i]->other_target()->Jump();
- }
- }
- }
-
- exit.Bind();
-}
-
-
-void CodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* node) {
- ASSERT(!in_spilled_code());
- VirtualFrame::SpilledScope spilled_scope;
- Comment cmnt(masm_, "[ TryFinallyStatement");
- CodeForStatementPosition(node);
-
- // State: Used to keep track of reason for entering the finally
- // block. Should probably be extended to hold information for
- // break/continue from within the try block.
- enum { FALLING, THROWING, JUMPING };
-
- JumpTarget try_block;
- JumpTarget finally_block;
-
- try_block.Call();
-
- frame_->EmitPush(rax);
- // In case of thrown exceptions, this is where we continue.
- __ Move(rcx, Smi::FromInt(THROWING));
- finally_block.Jump();
-
- // --- Try block ---
- try_block.Bind();
-
- frame_->PushTryHandler(TRY_FINALLY_HANDLER);
- int handler_height = frame_->height();
-
- // Shadow the jump targets for all escapes from the try block, including
- // returns. During shadowing, the original target is hidden as the
- // ShadowTarget and operations on the original actually affect the
- // shadowing target.
- //
- // We should probably try to unify the escaping targets and the return
- // target.
- int nof_escapes = node->escaping_targets()->length();
- List<ShadowTarget*> shadows(1 + nof_escapes);
-
- // Add the shadow target for the function return.
- static const int kReturnShadowIndex = 0;
- shadows.Add(new ShadowTarget(&function_return_));
- bool function_return_was_shadowed = function_return_is_shadowed_;
- function_return_is_shadowed_ = true;
- ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_);
-
- // Add the remaining shadow targets.
- for (int i = 0; i < nof_escapes; i++) {
- shadows.Add(new ShadowTarget(node->escaping_targets()->at(i)));
- }
-
- // Generate code for the statements in the try block.
- VisitStatementsAndSpill(node->try_block()->statements());
-
- // Stop the introduced shadowing and count the number of required unlinks.
- // After shadowing stops, the original targets are unshadowed and the
- // ShadowTargets represent the formerly shadowing targets.
- int nof_unlinks = 0;
- for (int i = 0; i < shadows.length(); i++) {
- shadows[i]->StopShadowing();
- if (shadows[i]->is_linked()) nof_unlinks++;
- }
- function_return_is_shadowed_ = function_return_was_shadowed;
-
- // Get an external reference to the handler address.
- ExternalReference handler_address(Isolate::k_handler_address, isolate());
-
- // If we can fall off the end of the try block, unlink from the try
- // chain and set the state on the frame to FALLING.
- if (has_valid_frame()) {
- // The next handler address is on top of the frame.
- STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
- __ movq(kScratchRegister, handler_address);
- frame_->EmitPop(Operand(kScratchRegister, 0));
- frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
-
- // Fake a top of stack value (unneeded when FALLING) and set the
- // state in ecx, then jump around the unlink blocks if any.
- frame_->EmitPush(Heap::kUndefinedValueRootIndex);
- __ Move(rcx, Smi::FromInt(FALLING));
- if (nof_unlinks > 0) {
- finally_block.Jump();
- }
- }
-
- // Generate code to unlink and set the state for the (formerly)
- // shadowing targets that have been jumped to.
- for (int i = 0; i < shadows.length(); i++) {
- if (shadows[i]->is_linked()) {
- // If we have come from the shadowed return, the return value is
- // on the virtual frame. We must preserve it until it is
- // pushed.
- if (i == kReturnShadowIndex) {
- Result return_value;
- shadows[i]->Bind(&return_value);
- return_value.ToRegister(rax);
- } else {
- shadows[i]->Bind();
- }
- // Because we can be jumping here (to spilled code) from
- // unspilled code, we need to reestablish a spilled frame at
- // this block.
- frame_->SpillAll();
-
- // Reload sp from the top handler, because some statements that
- // we break from (eg, for...in) may have left stuff on the
- // stack.
- __ movq(kScratchRegister, handler_address);
- __ movq(rsp, Operand(kScratchRegister, 0));
- frame_->Forget(frame_->height() - handler_height);
-
- // Unlink this handler and drop it from the frame.
- STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
- __ movq(kScratchRegister, handler_address);
- frame_->EmitPop(Operand(kScratchRegister, 0));
- frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
-
- if (i == kReturnShadowIndex) {
- // If this target shadowed the function return, materialize
- // the return value on the stack.
- frame_->EmitPush(rax);
- } else {
- // Fake TOS for targets that shadowed breaks and continues.
- frame_->EmitPush(Heap::kUndefinedValueRootIndex);
- }
- __ Move(rcx, Smi::FromInt(JUMPING + i));
- if (--nof_unlinks > 0) {
- // If this is not the last unlink block, jump around the next.
- finally_block.Jump();
- }
- }
- }
-
- // --- Finally block ---
- finally_block.Bind();
-
- // Push the state on the stack.
- frame_->EmitPush(rcx);
-
- // We keep two elements on the stack - the (possibly faked) result
- // and the state - while evaluating the finally block.
- //
- // Generate code for the statements in the finally block.
- VisitStatementsAndSpill(node->finally_block()->statements());
-
- if (has_valid_frame()) {
- // Restore state and return value or faked TOS.
- frame_->EmitPop(rcx);
- frame_->EmitPop(rax);
- }
-
- // Generate code to jump to the right destination for all used
- // formerly shadowing targets. Deallocate each shadow target.
- for (int i = 0; i < shadows.length(); i++) {
- if (has_valid_frame() && shadows[i]->is_bound()) {
- BreakTarget* original = shadows[i]->other_target();
- __ SmiCompare(rcx, Smi::FromInt(JUMPING + i));
- if (i == kReturnShadowIndex) {
- // The return value is (already) in rax.
- Result return_value = allocator_->Allocate(rax);
- ASSERT(return_value.is_valid());
- if (function_return_is_shadowed_) {
- original->Branch(equal, &return_value);
- } else {
- // Branch around the preparation for return which may emit
- // code.
- JumpTarget skip;
- skip.Branch(not_equal);
- frame_->PrepareForReturn();
- original->Jump(&return_value);
- skip.Bind();
- }
- } else {
- original->Branch(equal);
- }
- }
- }
-
- if (has_valid_frame()) {
- // Check if we need to rethrow the exception.
- JumpTarget exit;
- __ SmiCompare(rcx, Smi::FromInt(THROWING));
- exit.Branch(not_equal);
-
- // Rethrow exception.
- frame_->EmitPush(rax); // undo pop from above
- frame_->CallRuntime(Runtime::kReThrow, 1);
-
- // Done.
- exit.Bind();
- }
-}
-
-
-void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) {
- ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ DebuggerStatement");
- CodeForStatementPosition(node);
-#ifdef ENABLE_DEBUGGER_SUPPORT
- // Spill everything, even constants, to the frame.
- frame_->SpillAll();
-
- frame_->DebugBreak();
- // Ignore the return value.
-#endif
-}
-
-
-void CodeGenerator::InstantiateFunction(
- Handle<SharedFunctionInfo> function_info,
- bool pretenure) {
- // The inevitable call will sync frame elements to memory anyway, so
- // we do it eagerly to allow us to push the arguments directly into
- // place.
- frame_->SyncRange(0, frame_->element_count() - 1);
-
- // Use the fast case closure allocation code that allocates in new
- // space for nested functions that don't need literals cloning.
- if (!pretenure &&
- scope()->is_function_scope() &&
- function_info->num_literals() == 0) {
- FastNewClosureStub stub(
- function_info->strict_mode() ? kStrictMode : kNonStrictMode);
- frame_->Push(function_info);
- Result answer = frame_->CallStub(&stub, 1);
- frame_->Push(&answer);
- } else {
- // Call the runtime to instantiate the function based on the
- // shared function info.
- frame_->EmitPush(rsi);
- frame_->EmitPush(function_info);
- frame_->EmitPush(pretenure
- ? FACTORY->true_value()
- : FACTORY->false_value());
- Result result = frame_->CallRuntime(Runtime::kNewClosure, 3);
- frame_->Push(&result);
- }
-}
-
-
-void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
- Comment cmnt(masm_, "[ FunctionLiteral");
-
- // Build the function info and instantiate it.
- Handle<SharedFunctionInfo> function_info =
- Compiler::BuildFunctionInfo(node, script());
- // Check for stack-overflow exception.
- if (function_info.is_null()) {
- SetStackOverflow();
- return;
- }
- InstantiateFunction(function_info, node->pretenure());
-}
-
-
-void CodeGenerator::VisitSharedFunctionInfoLiteral(
- SharedFunctionInfoLiteral* node) {
- Comment cmnt(masm_, "[ SharedFunctionInfoLiteral");
- InstantiateFunction(node->shared_function_info(), false);
-}
-
-
-void CodeGenerator::VisitConditional(Conditional* node) {
- Comment cmnt(masm_, "[ Conditional");
- JumpTarget then;
- JumpTarget else_;
- JumpTarget exit;
- ControlDestination dest(&then, &else_, true);
- LoadCondition(node->condition(), &dest, true);
-
- if (dest.false_was_fall_through()) {
- // The else target was bound, so we compile the else part first.
- Load(node->else_expression());
-
- if (then.is_linked()) {
- exit.Jump();
- then.Bind();
- Load(node->then_expression());
- }
- } else {
- // The then target was bound, so we compile the then part first.
- Load(node->then_expression());
-
- if (else_.is_linked()) {
- exit.Jump();
- else_.Bind();
- Load(node->else_expression());
- }
- }
-
- exit.Bind();
-}
-
-
-void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
- if (slot->type() == Slot::LOOKUP) {
- ASSERT(slot->var()->is_dynamic());
-
- JumpTarget slow;
- JumpTarget done;
- Result value;
-
- // Generate fast case for loading from slots that correspond to
- // local/global variables or arguments unless they are shadowed by
- // eval-introduced bindings.
- EmitDynamicLoadFromSlotFastCase(slot,
- typeof_state,
- &value,
- &slow,
- &done);
-
- slow.Bind();
- // A runtime call is inevitable. We eagerly sync frame elements
- // to memory so that we can push the arguments directly into place
- // on top of the frame.
- frame_->SyncRange(0, frame_->element_count() - 1);
- frame_->EmitPush(rsi);
- __ movq(kScratchRegister, slot->var()->name(), RelocInfo::EMBEDDED_OBJECT);
- frame_->EmitPush(kScratchRegister);
- if (typeof_state == INSIDE_TYPEOF) {
- value =
- frame_->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
- } else {
- value = frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
- }
-
- done.Bind(&value);
- frame_->Push(&value);
-
- } else if (slot->var()->mode() == Variable::CONST) {
- // Const slots may contain 'the hole' value (the constant hasn't been
- // initialized yet) which needs to be converted into the 'undefined'
- // value.
- //
- // We currently spill the virtual frame because constants use the
- // potentially unsafe direct-frame access of SlotOperand.
- VirtualFrame::SpilledScope spilled_scope;
- Comment cmnt(masm_, "[ Load const");
- JumpTarget exit;
- __ movq(rcx, SlotOperand(slot, rcx));
- __ CompareRoot(rcx, Heap::kTheHoleValueRootIndex);
- exit.Branch(not_equal);
- __ LoadRoot(rcx, Heap::kUndefinedValueRootIndex);
- exit.Bind();
- frame_->EmitPush(rcx);
-
- } else if (slot->type() == Slot::PARAMETER) {
- frame_->PushParameterAt(slot->index());
-
- } else if (slot->type() == Slot::LOCAL) {
- frame_->PushLocalAt(slot->index());
-
- } else {
- // The other remaining slot types (LOOKUP and GLOBAL) cannot reach
- // here.
- //
- // The use of SlotOperand below is safe for an unspilled frame
- // because it will always be a context slot.
- ASSERT(slot->type() == Slot::CONTEXT);
- Result temp = allocator_->Allocate();
- ASSERT(temp.is_valid());
- __ movq(temp.reg(), SlotOperand(slot, temp.reg()));
- frame_->Push(&temp);
- }
-}
-
-
-void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
- TypeofState state) {
- LoadFromSlot(slot, state);
-
- // Bail out quickly if we're not using lazy arguments allocation.
- if (ArgumentsMode() != LAZY_ARGUMENTS_ALLOCATION) return;
-
- // ... or if the slot isn't a non-parameter arguments slot.
- if (slot->type() == Slot::PARAMETER || !slot->is_arguments()) return;
-
- // Pop the loaded value from the stack.
- Result value = frame_->Pop();
-
- // If the loaded value is a constant, we know if the arguments
- // object has been lazily loaded yet.
- if (value.is_constant()) {
- if (value.handle()->IsArgumentsMarker()) {
- Result arguments = StoreArgumentsObject(false);
- frame_->Push(&arguments);
- } else {
- frame_->Push(&value);
- }
- return;
- }
-
- // The loaded value is in a register. If it is the sentinel that
- // indicates that we haven't loaded the arguments object yet, we
- // need to do it now.
- JumpTarget exit;
- __ CompareRoot(value.reg(), Heap::kArgumentsMarkerRootIndex);
- frame_->Push(&value);
- exit.Branch(not_equal);
- Result arguments = StoreArgumentsObject(false);
- frame_->SetElementAt(0, &arguments);
- exit.Bind();
-}
-
-
-Result CodeGenerator::LoadFromGlobalSlotCheckExtensions(
- Slot* slot,
- TypeofState typeof_state,
- JumpTarget* slow) {
- // Check that no extension objects have been created by calls to
- // eval from the current scope to the global scope.
- Register context = rsi;
- Result tmp = allocator_->Allocate();
- ASSERT(tmp.is_valid()); // All non-reserved registers were available.
-
- Scope* s = scope();
- while (s != NULL) {
- if (s->num_heap_slots() > 0) {
- if (s->calls_eval()) {
- // Check that extension is NULL.
- __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX),
- Immediate(0));
- slow->Branch(not_equal, not_taken);
- }
- // Load next context in chain.
- __ movq(tmp.reg(), ContextOperand(context, Context::CLOSURE_INDEX));
- __ movq(tmp.reg(), FieldOperand(tmp.reg(), JSFunction::kContextOffset));
- context = tmp.reg();
- }
- // If no outer scope calls eval, we do not need to check more
- // context extensions. If we have reached an eval scope, we check
- // all extensions from this point.
- if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break;
- s = s->outer_scope();
- }
-
- if (s->is_eval_scope()) {
- // Loop up the context chain. There is no frame effect so it is
- // safe to use raw labels here.
- Label next, fast;
- if (!context.is(tmp.reg())) {
- __ movq(tmp.reg(), context);
- }
- // Load map for comparison into register, outside loop.
- __ LoadRoot(kScratchRegister, Heap::kGlobalContextMapRootIndex);
- __ bind(&next);
- // Terminate at global context.
- __ cmpq(kScratchRegister, FieldOperand(tmp.reg(), HeapObject::kMapOffset));
- __ j(equal, &fast);
- // Check that extension is NULL.
- __ cmpq(ContextOperand(tmp.reg(), Context::EXTENSION_INDEX), Immediate(0));
- slow->Branch(not_equal);
- // Load next context in chain.
- __ movq(tmp.reg(), ContextOperand(tmp.reg(), Context::CLOSURE_INDEX));
- __ movq(tmp.reg(), FieldOperand(tmp.reg(), JSFunction::kContextOffset));
- __ jmp(&next);
- __ bind(&fast);
- }
- tmp.Unuse();
-
- // All extension objects were empty and it is safe to use a global
- // load IC call.
- LoadGlobal();
- frame_->Push(slot->var()->name());
- RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
- ? RelocInfo::CODE_TARGET
- : RelocInfo::CODE_TARGET_CONTEXT;
- Result answer = frame_->CallLoadIC(mode);
- // A test rax instruction following the call signals that the inobject
- // property case was inlined. Ensure that there is not a test rax
- // instruction here.
- masm_->nop();
- return answer;
-}
-
-
-void CodeGenerator::EmitDynamicLoadFromSlotFastCase(Slot* slot,
- TypeofState typeof_state,
- Result* result,
- JumpTarget* slow,
- JumpTarget* done) {
- // Generate fast-case code for variables that might be shadowed by
- // eval-introduced variables. Eval is used a lot without
- // introducing variables. In those cases, we do not want to
- // perform a runtime call for all variables in the scope
- // containing the eval.
- if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
- *result = LoadFromGlobalSlotCheckExtensions(slot, typeof_state, slow);
- done->Jump(result);
-
- } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
- Slot* potential_slot = slot->var()->local_if_not_shadowed()->AsSlot();
- Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite();
- if (potential_slot != NULL) {
- // Generate fast case for locals that rewrite to slots.
- // Allocate a fresh register to use as a temp in
- // ContextSlotOperandCheckExtensions and to hold the result
- // value.
- *result = allocator_->Allocate();
- ASSERT(result->is_valid());
- __ movq(result->reg(),
- ContextSlotOperandCheckExtensions(potential_slot,
- *result,
- slow));
- if (potential_slot->var()->mode() == Variable::CONST) {
- __ CompareRoot(result->reg(), Heap::kTheHoleValueRootIndex);
- done->Branch(not_equal, result);
- __ LoadRoot(result->reg(), Heap::kUndefinedValueRootIndex);
- }
- done->Jump(result);
- } else if (rewrite != NULL) {
- // Generate fast case for argument loads.
- Property* property = rewrite->AsProperty();
- if (property != NULL) {
- VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
- Literal* key_literal = property->key()->AsLiteral();
- if (obj_proxy != NULL &&
- key_literal != NULL &&
- obj_proxy->IsArguments() &&
- key_literal->handle()->IsSmi()) {
- // Load arguments object if there are no eval-introduced
- // variables. Then load the argument from the arguments
- // object using keyed load.
- Result arguments = allocator()->Allocate();
- ASSERT(arguments.is_valid());
- __ movq(arguments.reg(),
- ContextSlotOperandCheckExtensions(obj_proxy->var()->AsSlot(),
- arguments,
- slow));
- frame_->Push(&arguments);
- frame_->Push(key_literal->handle());
- *result = EmitKeyedLoad();
- done->Jump(result);
- }
- }
- }
- }
-}
-
-
-void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) {
- if (slot->type() == Slot::LOOKUP) {
- ASSERT(slot->var()->is_dynamic());
-
- // For now, just do a runtime call. Since the call is inevitable,
- // we eagerly sync the virtual frame so we can directly push the
- // arguments into place.
- frame_->SyncRange(0, frame_->element_count() - 1);
-
- frame_->EmitPush(rsi);
- frame_->EmitPush(slot->var()->name());
-
- Result value;
- if (init_state == CONST_INIT) {
- // Same as the case for a normal store, but ignores attribute
- // (e.g. READ_ONLY) of context slot so that we can initialize const
- // properties (introduced via eval("const foo = (some expr);")). Also,
- // uses the current function context instead of the top context.
- //
- // Note that we must declare the foo upon entry of eval(), via a
- // context slot declaration, but we cannot initialize it at the same
- // time, because the const declaration may be at the end of the eval
- // code (sigh...) and the const variable may have been used before
- // (where its value is 'undefined'). Thus, we can only do the
- // initialization when we actually encounter the expression and when
- // the expression operands are defined and valid, and thus we need the
- // split into 2 operations: declaration of the context slot followed
- // by initialization.
- value = frame_->CallRuntime(Runtime::kInitializeConstContextSlot, 3);
- } else {
- frame_->Push(Smi::FromInt(strict_mode_flag()));
- value = frame_->CallRuntime(Runtime::kStoreContextSlot, 4);
- }
- // Storing a variable must keep the (new) value on the expression
- // stack. This is necessary for compiling chained assignment
- // expressions.
- frame_->Push(&value);
- } else {
- ASSERT(!slot->var()->is_dynamic());
-
- JumpTarget exit;
- if (init_state == CONST_INIT) {
- ASSERT(slot->var()->mode() == Variable::CONST);
- // Only the first const initialization must be executed (the slot
- // still contains 'the hole' value). When the assignment is executed,
- // the code is identical to a normal store (see below).
- //
- // We spill the frame in the code below because the direct-frame
- // access of SlotOperand is potentially unsafe with an unspilled
- // frame.
- VirtualFrame::SpilledScope spilled_scope;
- Comment cmnt(masm_, "[ Init const");
- __ movq(rcx, SlotOperand(slot, rcx));
- __ CompareRoot(rcx, Heap::kTheHoleValueRootIndex);
- exit.Branch(not_equal);
- }
-
- // We must execute the store. Storing a variable must keep the (new)
- // value on the stack. This is necessary for compiling assignment
- // expressions.
- //
- // Note: We will reach here even with slot->var()->mode() ==
- // Variable::CONST because of const declarations which will initialize
- // consts to 'the hole' value and by doing so, end up calling this code.
- if (slot->type() == Slot::PARAMETER) {
- frame_->StoreToParameterAt(slot->index());
- } else if (slot->type() == Slot::LOCAL) {
- frame_->StoreToLocalAt(slot->index());
- } else {
- // The other slot types (LOOKUP and GLOBAL) cannot reach here.
- //
- // The use of SlotOperand below is safe for an unspilled frame
- // because the slot is a context slot.
- ASSERT(slot->type() == Slot::CONTEXT);
- frame_->Dup();
- Result value = frame_->Pop();
- value.ToRegister();
- Result start = allocator_->Allocate();
- ASSERT(start.is_valid());
- __ movq(SlotOperand(slot, start.reg()), value.reg());
- // RecordWrite may destroy the value registers.
- //
- // TODO(204): Avoid actually spilling when the value is not
- // needed (probably the common case).
- frame_->Spill(value.reg());
- int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
- Result temp = allocator_->Allocate();
- ASSERT(temp.is_valid());
- __ RecordWrite(start.reg(), offset, value.reg(), temp.reg());
- // The results start, value, and temp are unused by going out of
- // scope.
- }
-
- exit.Bind();
- }
-}
-
-
-void CodeGenerator::VisitSlot(Slot* node) {
- Comment cmnt(masm_, "[ Slot");
- LoadFromSlotCheckForArguments(node, NOT_INSIDE_TYPEOF);
-}
-
-
-void CodeGenerator::VisitVariableProxy(VariableProxy* node) {
- Comment cmnt(masm_, "[ VariableProxy");
- Variable* var = node->var();
- Expression* expr = var->rewrite();
- if (expr != NULL) {
- Visit(expr);
- } else {
- ASSERT(var->is_global());
- Reference ref(this, node);
- ref.GetValue();
- }
-}
-
-
-void CodeGenerator::VisitLiteral(Literal* node) {
- Comment cmnt(masm_, "[ Literal");
- frame_->Push(node->handle());
-}
-
-
-void CodeGenerator::LoadUnsafeSmi(Register target, Handle<Object> value) {
- UNIMPLEMENTED();
- // TODO(X64): Implement security policy for loads of smis.
-}
-
-
-bool CodeGenerator::IsUnsafeSmi(Handle<Object> value) {
- return false;
-}
-
-
-// Materialize the regexp literal 'node' in the literals array
-// 'literals' of the function. Leave the regexp boilerplate in
-// 'boilerplate'.
-class DeferredRegExpLiteral: public DeferredCode {
- public:
- DeferredRegExpLiteral(Register boilerplate,
- Register literals,
- RegExpLiteral* node)
- : boilerplate_(boilerplate), literals_(literals), node_(node) {
- set_comment("[ DeferredRegExpLiteral");
- }
-
- void Generate();
-
- private:
- Register boilerplate_;
- Register literals_;
- RegExpLiteral* node_;
-};
-
-
-void DeferredRegExpLiteral::Generate() {
- // Since the entry is undefined we call the runtime system to
- // compute the literal.
- // Literal array (0).
- __ push(literals_);
- // Literal index (1).
- __ Push(Smi::FromInt(node_->literal_index()));
- // RegExp pattern (2).
- __ Push(node_->pattern());
- // RegExp flags (3).
- __ Push(node_->flags());
- __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
- if (!boilerplate_.is(rax)) __ movq(boilerplate_, rax);
-}
-
-
-class DeferredAllocateInNewSpace: public DeferredCode {
- public:
- DeferredAllocateInNewSpace(int size,
- Register target,
- int registers_to_save = 0)
- : size_(size), target_(target), registers_to_save_(registers_to_save) {
- ASSERT(size >= kPointerSize && size <= HEAP->MaxObjectSizeInNewSpace());
- set_comment("[ DeferredAllocateInNewSpace");
- }
- void Generate();
-
- private:
- int size_;
- Register target_;
- int registers_to_save_;
-};
-
-
-void DeferredAllocateInNewSpace::Generate() {
- for (int i = 0; i < kNumRegs; i++) {
- if (registers_to_save_ & (1 << i)) {
- Register save_register = { i };
- __ push(save_register);
- }
- }
- __ Push(Smi::FromInt(size_));
- __ CallRuntime(Runtime::kAllocateInNewSpace, 1);
- if (!target_.is(rax)) {
- __ movq(target_, rax);
- }
- for (int i = kNumRegs - 1; i >= 0; i--) {
- if (registers_to_save_ & (1 << i)) {
- Register save_register = { i };
- __ pop(save_register);
- }
- }
-}
-
-
-void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
- Comment cmnt(masm_, "[ RegExp Literal");
-
- // Retrieve the literals array and check the allocated entry. Begin
- // with a writable copy of the function of this activation in a
- // register.
- frame_->PushFunction();
- Result literals = frame_->Pop();
- literals.ToRegister();
- frame_->Spill(literals.reg());
-
- // Load the literals array of the function.
- __ movq(literals.reg(),
- FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
-
- // Load the literal at the ast saved index.
- Result boilerplate = allocator_->Allocate();
- ASSERT(boilerplate.is_valid());
- int literal_offset =
- FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
- __ movq(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset));
-
- // Check whether we need to materialize the RegExp object. If so,
- // jump to the deferred code passing the literals array.
- DeferredRegExpLiteral* deferred =
- new DeferredRegExpLiteral(boilerplate.reg(), literals.reg(), node);
- __ CompareRoot(boilerplate.reg(), Heap::kUndefinedValueRootIndex);
- deferred->Branch(equal);
- deferred->BindExit();
-
- // Register of boilerplate contains RegExp object.
-
- Result tmp = allocator()->Allocate();
- ASSERT(tmp.is_valid());
-
- int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize;
-
- DeferredAllocateInNewSpace* allocate_fallback =
- new DeferredAllocateInNewSpace(size, literals.reg());
- frame_->Push(&boilerplate);
- frame_->SpillTop();
- __ AllocateInNewSpace(size,
- literals.reg(),
- tmp.reg(),
- no_reg,
- allocate_fallback->entry_label(),
- TAG_OBJECT);
- allocate_fallback->BindExit();
- boilerplate = frame_->Pop();
- // Copy from boilerplate to clone and return clone.
-
- for (int i = 0; i < size; i += kPointerSize) {
- __ movq(tmp.reg(), FieldOperand(boilerplate.reg(), i));
- __ movq(FieldOperand(literals.reg(), i), tmp.reg());
- }
- frame_->Push(&literals);
-}
-
-
-void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
- Comment cmnt(masm_, "[ ObjectLiteral");
-
- // Load a writable copy of the function of this activation in a
- // register.
- frame_->PushFunction();
- Result literals = frame_->Pop();
- literals.ToRegister();
- frame_->Spill(literals.reg());
-
- // Load the literals array of the function.
- __ movq(literals.reg(),
- FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
- // Literal array.
- frame_->Push(&literals);
- // Literal index.
- frame_->Push(Smi::FromInt(node->literal_index()));
- // Constant properties.
- frame_->Push(node->constant_properties());
- // Should the object literal have fast elements?
- frame_->Push(Smi::FromInt(node->fast_elements() ? 1 : 0));
- Result clone;
- if (node->depth() > 1) {
- clone = frame_->CallRuntime(Runtime::kCreateObjectLiteral, 4);
- } else {
- clone = frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
- }
- frame_->Push(&clone);
-
- // Mark all computed expressions that are bound to a key that
- // is shadowed by a later occurrence of the same key. For the
- // marked expressions, no store code is emitted.
- node->CalculateEmitStore();
-
- for (int i = 0; i < node->properties()->length(); i++) {
- ObjectLiteral::Property* property = node->properties()->at(i);
- switch (property->kind()) {
- case ObjectLiteral::Property::CONSTANT:
- break;
- case ObjectLiteral::Property::MATERIALIZED_LITERAL:
- if (CompileTimeValue::IsCompileTimeValue(property->value())) break;
- // else fall through.
- case ObjectLiteral::Property::COMPUTED: {
- Handle<Object> key(property->key()->handle());
- if (key->IsSymbol()) {
- // Duplicate the object as the IC receiver.
- frame_->Dup();
- Load(property->value());
- if (property->emit_store()) {
- Result ignored =
- frame_->CallStoreIC(Handle<String>::cast(key), false,
- strict_mode_flag());
- // A test rax instruction following the store IC call would
- // indicate the presence of an inlined version of the
- // store. Add a nop to indicate that there is no such
- // inlined version.
- __ nop();
- } else {
- frame_->Drop(2);
- }
- break;
- }
- // Fall through
- }
- case ObjectLiteral::Property::PROTOTYPE: {
- // Duplicate the object as an argument to the runtime call.
- frame_->Dup();
- Load(property->key());
- Load(property->value());
- if (property->emit_store()) {
- frame_->Push(Smi::FromInt(NONE)); // PropertyAttributes
- // Ignore the result.
- Result ignored = frame_->CallRuntime(Runtime::kSetProperty, 4);
- } else {
- frame_->Drop(3);
- }
- break;
- }
- case ObjectLiteral::Property::SETTER: {
- // Duplicate the object as an argument to the runtime call.
- frame_->Dup();
- Load(property->key());
- frame_->Push(Smi::FromInt(1));
- Load(property->value());
- Result ignored = frame_->CallRuntime(Runtime::kDefineAccessor, 4);
- // Ignore the result.
- break;
- }
- case ObjectLiteral::Property::GETTER: {
- // Duplicate the object as an argument to the runtime call.
- frame_->Dup();
- Load(property->key());
- frame_->Push(Smi::FromInt(0));
- Load(property->value());
- Result ignored = frame_->CallRuntime(Runtime::kDefineAccessor, 4);
- // Ignore the result.
- break;
- }
- default: UNREACHABLE();
- }
- }
-}
-
-
-void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
- Comment cmnt(masm_, "[ ArrayLiteral");
-
- // Load a writable copy of the function of this activation in a
- // register.
- frame_->PushFunction();
- Result literals = frame_->Pop();
- literals.ToRegister();
- frame_->Spill(literals.reg());
-
- // Load the literals array of the function.
- __ movq(literals.reg(),
- FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
-
- frame_->Push(&literals);
- frame_->Push(Smi::FromInt(node->literal_index()));
- frame_->Push(node->constant_elements());
- int length = node->values()->length();
- Result clone;
- if (node->constant_elements()->map() == HEAP->fixed_cow_array_map()) {
- FastCloneShallowArrayStub stub(
- FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, length);
- clone = frame_->CallStub(&stub, 3);
- Counters* counters = masm()->isolate()->counters();
- __ IncrementCounter(counters->cow_arrays_created_stub(), 1);
- } else if (node->depth() > 1) {
- clone = frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3);
- } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) {
- clone = frame_->CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
- } else {
- FastCloneShallowArrayStub stub(
- FastCloneShallowArrayStub::CLONE_ELEMENTS, length);
- clone = frame_->CallStub(&stub, 3);
- }
- frame_->Push(&clone);
-
- // Generate code to set the elements in the array that are not
- // literals.
- for (int i = 0; i < length; i++) {
- Expression* value = node->values()->at(i);
-
- if (!CompileTimeValue::ArrayLiteralElementNeedsInitialization(value)) {
- continue;
- }
-
- // The property must be set by generated code.
- Load(value);
-
- // Get the property value off the stack.
- Result prop_value = frame_->Pop();
- prop_value.ToRegister();
-
- // Fetch the array literal while leaving a copy on the stack and
- // use it to get the elements array.
- frame_->Dup();
- Result elements = frame_->Pop();
- elements.ToRegister();
- frame_->Spill(elements.reg());
- // Get the elements FixedArray.
- __ movq(elements.reg(),
- FieldOperand(elements.reg(), JSObject::kElementsOffset));
-
- // Write to the indexed properties array.
- int offset = i * kPointerSize + FixedArray::kHeaderSize;
- __ movq(FieldOperand(elements.reg(), offset), prop_value.reg());
-
- // Update the write barrier for the array address.
- frame_->Spill(prop_value.reg()); // Overwritten by the write barrier.
- Result scratch = allocator_->Allocate();
- ASSERT(scratch.is_valid());
- __ RecordWrite(elements.reg(), offset, prop_value.reg(), scratch.reg());
- }
-}
-
-
-void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) {
- ASSERT(!in_spilled_code());
- // Call runtime routine to allocate the catch extension object and
- // assign the exception value to the catch variable.
- Comment cmnt(masm_, "[ CatchExtensionObject");
- Load(node->key());
- Load(node->value());
- Result result =
- frame_->CallRuntime(Runtime::kCreateCatchExtensionObject, 2);
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::EmitSlotAssignment(Assignment* node) {
-#ifdef DEBUG
- int original_height = frame()->height();
-#endif
- Comment cmnt(masm(), "[ Variable Assignment");
- Variable* var = node->target()->AsVariableProxy()->AsVariable();
- ASSERT(var != NULL);
- Slot* slot = var->AsSlot();
- ASSERT(slot != NULL);
-
- // Evaluate the right-hand side.
- if (node->is_compound()) {
- // For a compound assignment the right-hand side is a binary operation
- // between the current property value and the actual right-hand side.
- LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
- Load(node->value());
-
- // Perform the binary operation.
- bool overwrite_value = node->value()->ResultOverwriteAllowed();
- // Construct the implicit binary operation.
- BinaryOperation expr(node);
- GenericBinaryOperation(&expr,
- overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
- } else {
- // For non-compound assignment just load the right-hand side.
- Load(node->value());
- }
-
- // Perform the assignment.
- if (var->mode() != Variable::CONST || node->op() == Token::INIT_CONST) {
- CodeForSourcePosition(node->position());
- StoreToSlot(slot,
- node->op() == Token::INIT_CONST ? CONST_INIT : NOT_CONST_INIT);
- }
- ASSERT(frame()->height() == original_height + 1);
-}
-
-
-void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) {
-#ifdef DEBUG
- int original_height = frame()->height();
-#endif
- Comment cmnt(masm(), "[ Named Property Assignment");
- Variable* var = node->target()->AsVariableProxy()->AsVariable();
- Property* prop = node->target()->AsProperty();
- ASSERT(var == NULL || (prop == NULL && var->is_global()));
-
- // Initialize name and evaluate the receiver sub-expression if necessary. If
- // the receiver is trivial it is not placed on the stack at this point, but
- // loaded whenever actually needed.
- Handle<String> name;
- bool is_trivial_receiver = false;
- if (var != NULL) {
- name = var->name();
- } else {
- Literal* lit = prop->key()->AsLiteral();
- ASSERT_NOT_NULL(lit);
- name = Handle<String>::cast(lit->handle());
- // Do not materialize the receiver on the frame if it is trivial.
- is_trivial_receiver = prop->obj()->IsTrivial();
- if (!is_trivial_receiver) Load(prop->obj());
- }
-
- // Change to slow case in the beginning of an initialization block to
- // avoid the quadratic behavior of repeatedly adding fast properties.
- if (node->starts_initialization_block()) {
- // Initialization block consists of assignments of the form expr.x = ..., so
- // this will never be an assignment to a variable, so there must be a
- // receiver object.
- ASSERT_EQ(NULL, var);
- if (is_trivial_receiver) {
- frame()->Push(prop->obj());
- } else {
- frame()->Dup();
- }
- Result ignored = frame()->CallRuntime(Runtime::kToSlowProperties, 1);
- }
-
- // Change to fast case at the end of an initialization block. To prepare for
- // that add an extra copy of the receiver to the frame, so that it can be
- // converted back to fast case after the assignment.
- if (node->ends_initialization_block() && !is_trivial_receiver) {
- frame()->Dup();
- }
-
- // Stack layout:
- // [tos] : receiver (only materialized if non-trivial)
- // [tos+1] : receiver if at the end of an initialization block
-
- // Evaluate the right-hand side.
- if (node->is_compound()) {
- // For a compound assignment the right-hand side is a binary operation
- // between the current property value and the actual right-hand side.
- if (is_trivial_receiver) {
- frame()->Push(prop->obj());
- } else if (var != NULL) {
- // The LoadIC stub expects the object in rax.
- // Freeing rax causes the code generator to load the global into it.
- frame_->Spill(rax);
- LoadGlobal();
- } else {
- frame()->Dup();
- }
- Result value = EmitNamedLoad(name, var != NULL);
- frame()->Push(&value);
- Load(node->value());
-
- bool overwrite_value = node->value()->ResultOverwriteAllowed();
- // Construct the implicit binary operation.
- BinaryOperation expr(node);
- GenericBinaryOperation(&expr,
- overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
- } else {
- // For non-compound assignment just load the right-hand side.
- Load(node->value());
- }
-
- // Stack layout:
- // [tos] : value
- // [tos+1] : receiver (only materialized if non-trivial)
- // [tos+2] : receiver if at the end of an initialization block
-
- // Perform the assignment. It is safe to ignore constants here.
- ASSERT(var == NULL || var->mode() != Variable::CONST);
- ASSERT_NE(Token::INIT_CONST, node->op());
- if (is_trivial_receiver) {
- Result value = frame()->Pop();
- frame()->Push(prop->obj());
- frame()->Push(&value);
- }
- CodeForSourcePosition(node->position());
- bool is_contextual = (var != NULL);
- Result answer = EmitNamedStore(name, is_contextual);
- frame()->Push(&answer);
-
- // Stack layout:
- // [tos] : result
- // [tos+1] : receiver if at the end of an initialization block
-
- if (node->ends_initialization_block()) {
- ASSERT_EQ(NULL, var);
- // The argument to the runtime call is the receiver.
- if (is_trivial_receiver) {
- frame()->Push(prop->obj());
- } else {
- // A copy of the receiver is below the value of the assignment. Swap
- // the receiver and the value of the assignment expression.
- Result result = frame()->Pop();
- Result receiver = frame()->Pop();
- frame()->Push(&result);
- frame()->Push(&receiver);
- }
- Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1);
- }
-
- // Stack layout:
- // [tos] : result
-
- ASSERT_EQ(frame()->height(), original_height + 1);
-}
-
-
-void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) {
-#ifdef DEBUG
- int original_height = frame()->height();
-#endif
- Comment cmnt(masm_, "[ Keyed Property Assignment");
- Property* prop = node->target()->AsProperty();
- ASSERT_NOT_NULL(prop);
-
- // Evaluate the receiver subexpression.
- Load(prop->obj());
-
- // Change to slow case in the beginning of an initialization block to
- // avoid the quadratic behavior of repeatedly adding fast properties.
- if (node->starts_initialization_block()) {
- frame_->Dup();
- Result ignored = frame_->CallRuntime(Runtime::kToSlowProperties, 1);
- }
-
- // Change to fast case at the end of an initialization block. To prepare for
- // that add an extra copy of the receiver to the frame, so that it can be
- // converted back to fast case after the assignment.
- if (node->ends_initialization_block()) {
- frame_->Dup();
- }
-
- // Evaluate the key subexpression.
- Load(prop->key());
-
- // Stack layout:
- // [tos] : key
- // [tos+1] : receiver
- // [tos+2] : receiver if at the end of an initialization block
-
- // Evaluate the right-hand side.
- if (node->is_compound()) {
- // For a compound assignment the right-hand side is a binary operation
- // between the current property value and the actual right-hand side.
- // Duplicate receiver and key for loading the current property value.
- frame()->PushElementAt(1);
- frame()->PushElementAt(1);
- Result value = EmitKeyedLoad();
- frame()->Push(&value);
- Load(node->value());
-
- // Perform the binary operation.
- bool overwrite_value = node->value()->ResultOverwriteAllowed();
- BinaryOperation expr(node);
- GenericBinaryOperation(&expr,
- overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
- } else {
- // For non-compound assignment just load the right-hand side.
- Load(node->value());
- }
-
- // Stack layout:
- // [tos] : value
- // [tos+1] : key
- // [tos+2] : receiver
- // [tos+3] : receiver if at the end of an initialization block
-
- // Perform the assignment. It is safe to ignore constants here.
- ASSERT(node->op() != Token::INIT_CONST);
- CodeForSourcePosition(node->position());
- Result answer = EmitKeyedStore(prop->key()->type());
- frame()->Push(&answer);
-
- // Stack layout:
- // [tos] : result
- // [tos+1] : receiver if at the end of an initialization block
-
- // Change to fast case at the end of an initialization block.
- if (node->ends_initialization_block()) {
- // The argument to the runtime call is the extra copy of the receiver,
- // which is below the value of the assignment. Swap the receiver and
- // the value of the assignment expression.
- Result result = frame()->Pop();
- Result receiver = frame()->Pop();
- frame()->Push(&result);
- frame()->Push(&receiver);
- Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1);
- }
-
- // Stack layout:
- // [tos] : result
-
- ASSERT(frame()->height() == original_height + 1);
-}
-
-
-void CodeGenerator::VisitAssignment(Assignment* node) {
-#ifdef DEBUG
- int original_height = frame()->height();
-#endif
- Variable* var = node->target()->AsVariableProxy()->AsVariable();
- Property* prop = node->target()->AsProperty();
-
- if (var != NULL && !var->is_global()) {
- EmitSlotAssignment(node);
-
- } else if ((prop != NULL && prop->key()->IsPropertyName()) ||
- (var != NULL && var->is_global())) {
- // Properties whose keys are property names and global variables are
- // treated as named property references. We do not need to consider
- // global 'this' because it is not a valid left-hand side.
- EmitNamedPropertyAssignment(node);
-
- } else if (prop != NULL) {
- // Other properties (including rewritten parameters for a function that
- // uses arguments) are keyed property assignments.
- EmitKeyedPropertyAssignment(node);
-
- } else {
- // Invalid left-hand side.
- Load(node->target());
- Result result = frame()->CallRuntime(Runtime::kThrowReferenceError, 1);
- // The runtime call doesn't actually return but the code generator will
- // still generate code and expects a certain frame height.
- frame()->Push(&result);
- }
-
- ASSERT(frame()->height() == original_height + 1);
-}
-
-
-void CodeGenerator::VisitThrow(Throw* node) {
- Comment cmnt(masm_, "[ Throw");
- Load(node->exception());
- Result result = frame_->CallRuntime(Runtime::kThrow, 1);
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::VisitProperty(Property* node) {
- Comment cmnt(masm_, "[ Property");
- Reference property(this, node);
- property.GetValue();
-}
-
-
-void CodeGenerator::VisitCall(Call* node) {
- Comment cmnt(masm_, "[ Call");
-
- ZoneList<Expression*>* args = node->arguments();
-
- // Check if the function is a variable or a property.
- Expression* function = node->expression();
- Variable* var = function->AsVariableProxy()->AsVariable();
- Property* property = function->AsProperty();
-
- // ------------------------------------------------------------------------
- // Fast-case: Use inline caching.
- // ---
- // According to ECMA-262, section 11.2.3, page 44, the function to call
- // must be resolved after the arguments have been evaluated. The IC code
- // automatically handles this by loading the arguments before the function
- // is resolved in cache misses (this also holds for megamorphic calls).
- // ------------------------------------------------------------------------
-
- if (var != NULL && var->is_possibly_eval()) {
- // ----------------------------------
- // JavaScript example: 'eval(arg)' // eval is not known to be shadowed
- // ----------------------------------
-
- // In a call to eval, we first call %ResolvePossiblyDirectEval to
- // resolve the function we need to call and the receiver of the
- // call. Then we call the resolved function using the given
- // arguments.
-
- // Prepare the stack for the call to the resolved function.
- Load(function);
-
- // Allocate a frame slot for the receiver.
- frame_->Push(FACTORY->undefined_value());
-
- // Load the arguments.
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- frame_->SpillTop();
- }
-
- // Result to hold the result of the function resolution and the
- // final result of the eval call.
- Result result;
-
- // If we know that eval can only be shadowed by eval-introduced
- // variables we attempt to load the global eval function directly
- // in generated code. If we succeed, there is no need to perform a
- // context lookup in the runtime system.
- JumpTarget done;
- if (var->AsSlot() != NULL && var->mode() == Variable::DYNAMIC_GLOBAL) {
- ASSERT(var->AsSlot()->type() == Slot::LOOKUP);
- JumpTarget slow;
- // Prepare the stack for the call to
- // ResolvePossiblyDirectEvalNoLookup by pushing the loaded
- // function, the first argument to the eval call and the
- // receiver.
- Result fun = LoadFromGlobalSlotCheckExtensions(var->AsSlot(),
- NOT_INSIDE_TYPEOF,
- &slow);
- frame_->Push(&fun);
- if (arg_count > 0) {
- frame_->PushElementAt(arg_count);
- } else {
- frame_->Push(FACTORY->undefined_value());
- }
- frame_->PushParameterAt(-1);
-
- // Push the strict mode flag.
- frame_->Push(Smi::FromInt(strict_mode_flag()));
-
- // Resolve the call.
- result =
- frame_->CallRuntime(Runtime::kResolvePossiblyDirectEvalNoLookup, 4);
-
- done.Jump(&result);
- slow.Bind();
- }
-
- // Prepare the stack for the call to ResolvePossiblyDirectEval by
- // pushing the loaded function, the first argument to the eval
- // call and the receiver.
- frame_->PushElementAt(arg_count + 1);
- if (arg_count > 0) {
- frame_->PushElementAt(arg_count);
- } else {
- frame_->Push(FACTORY->undefined_value());
- }
- frame_->PushParameterAt(-1);
-
- // Push the strict mode flag.
- frame_->Push(Smi::FromInt(strict_mode_flag()));
-
- // Resolve the call.
- result = frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 4);
-
- // If we generated fast-case code bind the jump-target where fast
- // and slow case merge.
- if (done.is_linked()) done.Bind(&result);
-
- // The runtime call returns a pair of values in rax (function) and
- // rdx (receiver). Touch up the stack with the right values.
- Result receiver = allocator_->Allocate(rdx);
- frame_->SetElementAt(arg_count + 1, &result);
- frame_->SetElementAt(arg_count, &receiver);
- receiver.Unuse();
-
- // Call the function.
- CodeForSourcePosition(node->position());
- InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub call_function(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
- result = frame_->CallStub(&call_function, arg_count + 1);
-
- // Restore the context and overwrite the function on the stack with
- // the result.
- frame_->RestoreContextRegister();
- frame_->SetElementAt(0, &result);
-
- } else if (var != NULL && !var->is_this() && var->is_global()) {
- // ----------------------------------
- // JavaScript example: 'foo(1, 2, 3)' // foo is global
- // ----------------------------------
-
- // Pass the global object as the receiver and let the IC stub
- // patch the stack to use the global proxy as 'this' in the
- // invoked function.
- LoadGlobal();
-
- // Load the arguments.
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- frame_->SpillTop();
- }
-
- // Push the name of the function on the frame.
- frame_->Push(var->name());
-
- // Call the IC initialization code.
- CodeForSourcePosition(node->position());
- Result result = frame_->CallCallIC(RelocInfo::CODE_TARGET_CONTEXT,
- arg_count,
- loop_nesting());
- frame_->RestoreContextRegister();
- // Replace the function on the stack with the result.
- frame_->Push(&result);
-
- } else if (var != NULL && var->AsSlot() != NULL &&
- var->AsSlot()->type() == Slot::LOOKUP) {
- // ----------------------------------
- // JavaScript examples:
- //
- // with (obj) foo(1, 2, 3) // foo may be in obj.
- //
- // function f() {};
- // function g() {
- // eval(...);
- // f(); // f could be in extension object.
- // }
- // ----------------------------------
-
- JumpTarget slow, done;
- Result function;
-
- // Generate fast case for loading functions from slots that
- // correspond to local/global variables or arguments unless they
- // are shadowed by eval-introduced bindings.
- EmitDynamicLoadFromSlotFastCase(var->AsSlot(),
- NOT_INSIDE_TYPEOF,
- &function,
- &slow,
- &done);
-
- slow.Bind();
- // Load the function from the context. Sync the frame so we can
- // push the arguments directly into place.
- frame_->SyncRange(0, frame_->element_count() - 1);
- frame_->EmitPush(rsi);
- frame_->EmitPush(var->name());
- frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
- // The runtime call returns a pair of values in rax and rdx. The
- // looked-up function is in rax and the receiver is in rdx. These
- // register references are not ref counted here. We spill them
- // eagerly since they are arguments to an inevitable call (and are
- // not sharable by the arguments).
- ASSERT(!allocator()->is_used(rax));
- frame_->EmitPush(rax);
-
- // Load the receiver.
- ASSERT(!allocator()->is_used(rdx));
- frame_->EmitPush(rdx);
-
- // If fast case code has been generated, emit code to push the
- // function and receiver and have the slow path jump around this
- // code.
- if (done.is_linked()) {
- JumpTarget call;
- call.Jump();
- done.Bind(&function);
- frame_->Push(&function);
- LoadGlobalReceiver();
- call.Bind();
- }
-
- // Call the function.
- CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
-
- } else if (property != NULL) {
- // Check if the key is a literal string.
- Literal* literal = property->key()->AsLiteral();
-
- if (literal != NULL && literal->handle()->IsSymbol()) {
- // ------------------------------------------------------------------
- // JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
- // ------------------------------------------------------------------
-
- Handle<String> name = Handle<String>::cast(literal->handle());
-
- if (ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION &&
- name->IsEqualTo(CStrVector("apply")) &&
- args->length() == 2 &&
- args->at(1)->AsVariableProxy() != NULL &&
- args->at(1)->AsVariableProxy()->IsArguments()) {
- // Use the optimized Function.prototype.apply that avoids
- // allocating lazily allocated arguments objects.
- CallApplyLazy(property->obj(),
- args->at(0),
- args->at(1)->AsVariableProxy(),
- node->position());
-
- } else {
- // Push the receiver onto the frame.
- Load(property->obj());
-
- // Load the arguments.
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- frame_->SpillTop();
- }
-
- // Push the name of the function onto the frame.
- frame_->Push(name);
-
- // Call the IC initialization code.
- CodeForSourcePosition(node->position());
- Result result = frame_->CallCallIC(RelocInfo::CODE_TARGET,
- arg_count,
- loop_nesting());
- frame_->RestoreContextRegister();
- frame_->Push(&result);
- }
-
- } else {
- // -------------------------------------------
- // JavaScript example: 'array[index](1, 2, 3)'
- // -------------------------------------------
-
- // Load the function to call from the property through a reference.
- if (property->is_synthetic()) {
- Reference ref(this, property, false);
- ref.GetValue();
- // Use global object as receiver.
- LoadGlobalReceiver();
- // Call the function.
- CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
- } else {
- // Push the receiver onto the frame.
- Load(property->obj());
-
- // Load the name of the function.
- Load(property->key());
-
- // Swap the name of the function and the receiver on the stack to follow
- // the calling convention for call ICs.
- Result key = frame_->Pop();
- Result receiver = frame_->Pop();
- frame_->Push(&key);
- frame_->Push(&receiver);
- key.Unuse();
- receiver.Unuse();
-
- // Load the arguments.
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- frame_->SpillTop();
- }
-
- // Place the key on top of stack and call the IC initialization code.
- frame_->PushElementAt(arg_count + 1);
- CodeForSourcePosition(node->position());
- Result result = frame_->CallKeyedCallIC(RelocInfo::CODE_TARGET,
- arg_count,
- loop_nesting());
- frame_->Drop(); // Drop the key still on the stack.
- frame_->RestoreContextRegister();
- frame_->Push(&result);
- }
- }
- } else {
- // ----------------------------------
- // JavaScript example: 'foo(1, 2, 3)' // foo is not global
- // ----------------------------------
-
- // Load the function.
- Load(function);
-
- // Pass the global proxy as the receiver.
- LoadGlobalReceiver();
-
- // Call the function.
- CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
- }
-}
-
-
-void CodeGenerator::VisitCallNew(CallNew* node) {
- Comment cmnt(masm_, "[ CallNew");
-
- // According to ECMA-262, section 11.2.2, page 44, the function
- // expression in new calls must be evaluated before the
- // arguments. This is different from ordinary calls, where the
- // actual function to call is resolved after the arguments have been
- // evaluated.
-
- // Push constructor on the stack. If it's not a function it's used as
- // receiver for CALL_NON_FUNCTION, otherwise the value on the stack is
- // ignored.
- Load(node->expression());
-
- // Push the arguments ("left-to-right") on the stack.
- ZoneList<Expression*>* args = node->arguments();
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- }
-
- // Call the construct call builtin that handles allocation and
- // constructor invocation.
- CodeForSourcePosition(node->position());
- Result result = frame_->CallConstructor(arg_count);
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result value = frame_->Pop();
- value.ToRegister();
- ASSERT(value.is_valid());
- Condition is_smi = masm_->CheckSmi(value.reg());
- value.Unuse();
- destination()->Split(is_smi);
-}
-
-
-void CodeGenerator::GenerateLog(ZoneList<Expression*>* args) {
- // Conditionally generate a log call.
- // Args:
- // 0 (literal string): The type of logging (corresponds to the flags).
- // This is used to determine whether or not to generate the log call.
- // 1 (string): Format string. Access the string at argument index 2
- // with '%2s' (see Logger::LogRuntime for all the formats).
- // 2 (array): Arguments to the format string.
- ASSERT_EQ(args->length(), 3);
-#ifdef ENABLE_LOGGING_AND_PROFILING
- if (ShouldGenerateLog(args->at(0))) {
- Load(args->at(1));
- Load(args->at(2));
- frame_->CallRuntime(Runtime::kLog, 2);
- }
-#endif
- // Finally, we're expected to leave a value on the top of the stack.
- frame_->Push(FACTORY->undefined_value());
-}
-
-
-void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result value = frame_->Pop();
- value.ToRegister();
- ASSERT(value.is_valid());
- Condition non_negative_smi = masm_->CheckNonNegativeSmi(value.reg());
- value.Unuse();
- destination()->Split(non_negative_smi);
-}
-
-
-class DeferredStringCharCodeAt : public DeferredCode {
- public:
- DeferredStringCharCodeAt(Register object,
- Register index,
- Register scratch,
- Register result)
- : result_(result),
- char_code_at_generator_(object,
- index,
- scratch,
- result,
- &need_conversion_,
- &need_conversion_,
- &index_out_of_range_,
- STRING_INDEX_IS_NUMBER) {}
-
- StringCharCodeAtGenerator* fast_case_generator() {
- return &char_code_at_generator_;
- }
-
- virtual void Generate() {
- VirtualFrameRuntimeCallHelper call_helper(frame_state());
- char_code_at_generator_.GenerateSlow(masm(), call_helper);
-
- __ bind(&need_conversion_);
- // Move the undefined value into the result register, which will
- // trigger conversion.
- __ LoadRoot(result_, Heap::kUndefinedValueRootIndex);
- __ jmp(exit_label());
-
- __ bind(&index_out_of_range_);
- // When the index is out of range, the spec requires us to return
- // NaN.
- __ LoadRoot(result_, Heap::kNanValueRootIndex);
- __ jmp(exit_label());
- }
-
- private:
- Register result_;
-
- Label need_conversion_;
- Label index_out_of_range_;
-
- StringCharCodeAtGenerator char_code_at_generator_;
-};
-
-
-// This generates code that performs a String.prototype.charCodeAt() call
-// or returns a smi in order to trigger conversion.
-void CodeGenerator::GenerateStringCharCodeAt(ZoneList<Expression*>* args) {
- Comment(masm_, "[ GenerateStringCharCodeAt");
- ASSERT(args->length() == 2);
-
- Load(args->at(0));
- Load(args->at(1));
- Result index = frame_->Pop();
- Result object = frame_->Pop();
- object.ToRegister();
- index.ToRegister();
- // We might mutate the object register.
- frame_->Spill(object.reg());
-
- // We need two extra registers.
- Result result = allocator()->Allocate();
- ASSERT(result.is_valid());
- Result scratch = allocator()->Allocate();
- ASSERT(scratch.is_valid());
-
- DeferredStringCharCodeAt* deferred =
- new DeferredStringCharCodeAt(object.reg(),
- index.reg(),
- scratch.reg(),
- result.reg());
- deferred->fast_case_generator()->GenerateFast(masm_);
- deferred->BindExit();
- frame_->Push(&result);
-}
-
-
-class DeferredStringCharFromCode : public DeferredCode {
- public:
- DeferredStringCharFromCode(Register code,
- Register result)
- : char_from_code_generator_(code, result) {}
-
- StringCharFromCodeGenerator* fast_case_generator() {
- return &char_from_code_generator_;
- }
-
- virtual void Generate() {
- VirtualFrameRuntimeCallHelper call_helper(frame_state());
- char_from_code_generator_.GenerateSlow(masm(), call_helper);
- }
-
- private:
- StringCharFromCodeGenerator char_from_code_generator_;
-};
-
-
-// Generates code for creating a one-char string from a char code.
-void CodeGenerator::GenerateStringCharFromCode(ZoneList<Expression*>* args) {
- Comment(masm_, "[ GenerateStringCharFromCode");
- ASSERT(args->length() == 1);
-
- Load(args->at(0));
-
- Result code = frame_->Pop();
- code.ToRegister();
- ASSERT(code.is_valid());
-
- Result result = allocator()->Allocate();
- ASSERT(result.is_valid());
-
- DeferredStringCharFromCode* deferred = new DeferredStringCharFromCode(
- code.reg(), result.reg());
- deferred->fast_case_generator()->GenerateFast(masm_);
- deferred->BindExit();
- frame_->Push(&result);
-}
-
-
-class DeferredStringCharAt : public DeferredCode {
- public:
- DeferredStringCharAt(Register object,
- Register index,
- Register scratch1,
- Register scratch2,
- Register result)
- : result_(result),
- char_at_generator_(object,
- index,
- scratch1,
- scratch2,
- result,
- &need_conversion_,
- &need_conversion_,
- &index_out_of_range_,
- STRING_INDEX_IS_NUMBER) {}
-
- StringCharAtGenerator* fast_case_generator() {
- return &char_at_generator_;
- }
-
- virtual void Generate() {
- VirtualFrameRuntimeCallHelper call_helper(frame_state());
- char_at_generator_.GenerateSlow(masm(), call_helper);
-
- __ bind(&need_conversion_);
- // Move smi zero into the result register, which will trigger
- // conversion.
- __ Move(result_, Smi::FromInt(0));
- __ jmp(exit_label());
-
- __ bind(&index_out_of_range_);
- // When the index is out of range, the spec requires us to return
- // the empty string.
- __ LoadRoot(result_, Heap::kEmptyStringRootIndex);
- __ jmp(exit_label());
- }
-
- private:
- Register result_;
-
- Label need_conversion_;
- Label index_out_of_range_;
-
- StringCharAtGenerator char_at_generator_;
-};
-
-
-// This generates code that performs a String.prototype.charAt() call
-// or returns a smi in order to trigger conversion.
-void CodeGenerator::GenerateStringCharAt(ZoneList<Expression*>* args) {
- Comment(masm_, "[ GenerateStringCharAt");
- ASSERT(args->length() == 2);
-
- Load(args->at(0));
- Load(args->at(1));
- Result index = frame_->Pop();
- Result object = frame_->Pop();
- object.ToRegister();
- index.ToRegister();
- // We might mutate the object register.
- frame_->Spill(object.reg());
-
- // We need three extra registers.
- Result result = allocator()->Allocate();
- ASSERT(result.is_valid());
- Result scratch1 = allocator()->Allocate();
- ASSERT(scratch1.is_valid());
- Result scratch2 = allocator()->Allocate();
- ASSERT(scratch2.is_valid());
-
- DeferredStringCharAt* deferred =
- new DeferredStringCharAt(object.reg(),
- index.reg(),
- scratch1.reg(),
- scratch2.reg(),
- result.reg());
- deferred->fast_case_generator()->GenerateFast(masm_);
- deferred->BindExit();
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result value = frame_->Pop();
- value.ToRegister();
- ASSERT(value.is_valid());
- Condition is_smi = masm_->CheckSmi(value.reg());
- destination()->false_target()->Branch(is_smi);
- // It is a heap object - get map.
- // Check if the object is a JS array or not.
- __ CmpObjectType(value.reg(), JS_ARRAY_TYPE, kScratchRegister);
- value.Unuse();
- destination()->Split(equal);
-}
-
-
-void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result value = frame_->Pop();
- value.ToRegister();
- ASSERT(value.is_valid());
- Condition is_smi = masm_->CheckSmi(value.reg());
- destination()->false_target()->Branch(is_smi);
- // It is a heap object - get map.
- // Check if the object is a regexp.
- __ CmpObjectType(value.reg(), JS_REGEXP_TYPE, kScratchRegister);
- value.Unuse();
- destination()->Split(equal);
-}
-
-
-void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
- // This generates a fast version of:
- // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result obj = frame_->Pop();
- obj.ToRegister();
- Condition is_smi = masm_->CheckSmi(obj.reg());
- destination()->false_target()->Branch(is_smi);
-
- __ Move(kScratchRegister, FACTORY->null_value());
- __ cmpq(obj.reg(), kScratchRegister);
- destination()->true_target()->Branch(equal);
-
- __ movq(kScratchRegister, FieldOperand(obj.reg(), HeapObject::kMapOffset));
- // Undetectable objects behave like undefined when tested with typeof.
- __ testb(FieldOperand(kScratchRegister, Map::kBitFieldOffset),
- Immediate(1 << Map::kIsUndetectable));
- destination()->false_target()->Branch(not_zero);
- __ movzxbq(kScratchRegister,
- FieldOperand(kScratchRegister, Map::kInstanceTypeOffset));
- __ cmpq(kScratchRegister, Immediate(FIRST_JS_OBJECT_TYPE));
- destination()->false_target()->Branch(below);
- __ cmpq(kScratchRegister, Immediate(LAST_JS_OBJECT_TYPE));
- obj.Unuse();
- destination()->Split(below_equal);
-}
-
-
-void CodeGenerator::GenerateIsSpecObject(ZoneList<Expression*>* args) {
- // This generates a fast version of:
- // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp' ||
- // typeof(arg) == function).
- // It includes undetectable objects (as opposed to IsObject).
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result value = frame_->Pop();
- value.ToRegister();
- ASSERT(value.is_valid());
- Condition is_smi = masm_->CheckSmi(value.reg());
- destination()->false_target()->Branch(is_smi);
- // Check that this is an object.
- __ CmpObjectType(value.reg(), FIRST_JS_OBJECT_TYPE, kScratchRegister);
- value.Unuse();
- destination()->Split(above_equal);
-}
-
-
-// Deferred code to check whether the String JavaScript object is safe for using
-// default value of. This code is called after the bit caching this information
-// in the map has been checked with the map for the object in the map_result_
-// register. On return the register map_result_ contains 1 for true and 0 for
-// false.
-class DeferredIsStringWrapperSafeForDefaultValueOf : public DeferredCode {
- public:
- DeferredIsStringWrapperSafeForDefaultValueOf(Register object,
- Register map_result,
- Register scratch1,
- Register scratch2)
- : object_(object),
- map_result_(map_result),
- scratch1_(scratch1),
- scratch2_(scratch2) { }
-
- virtual void Generate() {
- Label false_result;
-
- // Check that map is loaded as expected.
- if (FLAG_debug_code) {
- __ cmpq(map_result_, FieldOperand(object_, HeapObject::kMapOffset));
- __ Assert(equal, "Map not in expected register");
- }
-
- // Check for fast case object. Generate false result for slow case object.
- __ movq(scratch1_, FieldOperand(object_, JSObject::kPropertiesOffset));
- __ movq(scratch1_, FieldOperand(scratch1_, HeapObject::kMapOffset));
- __ CompareRoot(scratch1_, Heap::kHashTableMapRootIndex);
- __ j(equal, &false_result);
-
- // Look for valueOf symbol in the descriptor array, and indicate false if
- // found. The type is not checked, so if it is a transition it is a false
- // negative.
- __ movq(map_result_,
- FieldOperand(map_result_, Map::kInstanceDescriptorsOffset));
- __ movq(scratch1_, FieldOperand(map_result_, FixedArray::kLengthOffset));
- // map_result_: descriptor array
- // scratch1_: length of descriptor array
- // Calculate the end of the descriptor array.
- SmiIndex index = masm_->SmiToIndex(scratch2_, scratch1_, kPointerSizeLog2);
- __ lea(scratch1_,
- Operand(
- map_result_, index.reg, index.scale, FixedArray::kHeaderSize));
- // Calculate location of the first key name.
- __ addq(map_result_,
- Immediate(FixedArray::kHeaderSize +
- DescriptorArray::kFirstIndex * kPointerSize));
- // Loop through all the keys in the descriptor array. If one of these is the
- // symbol valueOf the result is false.
- Label entry, loop;
- __ jmp(&entry);
- __ bind(&loop);
- __ movq(scratch2_, FieldOperand(map_result_, 0));
- __ Cmp(scratch2_, FACTORY->value_of_symbol());
- __ j(equal, &false_result);
- __ addq(map_result_, Immediate(kPointerSize));
- __ bind(&entry);
- __ cmpq(map_result_, scratch1_);
- __ j(not_equal, &loop);
-
- // Reload map as register map_result_ was used as temporary above.
- __ movq(map_result_, FieldOperand(object_, HeapObject::kMapOffset));
-
- // If a valueOf property is not found on the object check that it's
- // prototype is the un-modified String prototype. If not result is false.
- __ movq(scratch1_, FieldOperand(map_result_, Map::kPrototypeOffset));
- __ testq(scratch1_, Immediate(kSmiTagMask));
- __ j(zero, &false_result);
- __ movq(scratch1_, FieldOperand(scratch1_, HeapObject::kMapOffset));
- __ movq(scratch2_,
- Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
- __ movq(scratch2_,
- FieldOperand(scratch2_, GlobalObject::kGlobalContextOffset));
- __ cmpq(scratch1_,
- ContextOperand(
- scratch2_, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX));
- __ j(not_equal, &false_result);
- // Set the bit in the map to indicate that it has been checked safe for
- // default valueOf and set true result.
- __ or_(FieldOperand(map_result_, Map::kBitField2Offset),
- Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf));
- __ Set(map_result_, 1);
- __ jmp(exit_label());
- __ bind(&false_result);
- // Set false result.
- __ Set(map_result_, 0);
- }
-
- private:
- Register object_;
- Register map_result_;
- Register scratch1_;
- Register scratch2_;
-};
-
-
-void CodeGenerator::GenerateIsStringWrapperSafeForDefaultValueOf(
- ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result obj = frame_->Pop(); // Pop the string wrapper.
- obj.ToRegister();
- ASSERT(obj.is_valid());
- if (FLAG_debug_code) {
- __ AbortIfSmi(obj.reg());
- }
-
- // Check whether this map has already been checked to be safe for default
- // valueOf.
- Result map_result = allocator()->Allocate();
- ASSERT(map_result.is_valid());
- __ movq(map_result.reg(), FieldOperand(obj.reg(), HeapObject::kMapOffset));
- __ testb(FieldOperand(map_result.reg(), Map::kBitField2Offset),
- Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf));
- destination()->true_target()->Branch(not_zero);
-
- // We need an additional two scratch registers for the deferred code.
- Result temp1 = allocator()->Allocate();
- ASSERT(temp1.is_valid());
- Result temp2 = allocator()->Allocate();
- ASSERT(temp2.is_valid());
-
- DeferredIsStringWrapperSafeForDefaultValueOf* deferred =
- new DeferredIsStringWrapperSafeForDefaultValueOf(
- obj.reg(), map_result.reg(), temp1.reg(), temp2.reg());
- deferred->Branch(zero);
- deferred->BindExit();
- __ testq(map_result.reg(), map_result.reg());
- obj.Unuse();
- map_result.Unuse();
- temp1.Unuse();
- temp2.Unuse();
- destination()->Split(not_equal);
-}
-
-
-void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
- // This generates a fast version of:
- // (%_ClassOf(arg) === 'Function')
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result obj = frame_->Pop();
- obj.ToRegister();
- Condition is_smi = masm_->CheckSmi(obj.reg());
- destination()->false_target()->Branch(is_smi);
- __ CmpObjectType(obj.reg(), JS_FUNCTION_TYPE, kScratchRegister);
- obj.Unuse();
- destination()->Split(equal);
-}
-
-
-void CodeGenerator::GenerateIsUndetectableObject(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result obj = frame_->Pop();
- obj.ToRegister();
- Condition is_smi = masm_->CheckSmi(obj.reg());
- destination()->false_target()->Branch(is_smi);
- __ movq(kScratchRegister, FieldOperand(obj.reg(), HeapObject::kMapOffset));
- __ movzxbl(kScratchRegister,
- FieldOperand(kScratchRegister, Map::kBitFieldOffset));
- __ testl(kScratchRegister, Immediate(1 << Map::kIsUndetectable));
- obj.Unuse();
- destination()->Split(not_zero);
-}
-
-
-void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 0);
-
- // Get the frame pointer for the calling frame.
- Result fp = allocator()->Allocate();
- __ movq(fp.reg(), Operand(rbp, StandardFrameConstants::kCallerFPOffset));
-
- // Skip the arguments adaptor frame if it exists.
- Label check_frame_marker;
- __ Cmp(Operand(fp.reg(), StandardFrameConstants::kContextOffset),
- Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
- __ j(not_equal, &check_frame_marker);
- __ movq(fp.reg(), Operand(fp.reg(), StandardFrameConstants::kCallerFPOffset));
-
- // Check the marker in the calling frame.
- __ bind(&check_frame_marker);
- __ Cmp(Operand(fp.reg(), StandardFrameConstants::kMarkerOffset),
- Smi::FromInt(StackFrame::CONSTRUCT));
- fp.Unuse();
- destination()->Split(equal);
-}
-
-
-void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 0);
-
- Result fp = allocator_->Allocate();
- Result result = allocator_->Allocate();
- ASSERT(fp.is_valid() && result.is_valid());
-
- Label exit;
-
- // Get the number of formal parameters.
- __ Move(result.reg(), Smi::FromInt(scope()->num_parameters()));
-
- // Check if the calling frame is an arguments adaptor frame.
- __ movq(fp.reg(), Operand(rbp, StandardFrameConstants::kCallerFPOffset));
- __ Cmp(Operand(fp.reg(), StandardFrameConstants::kContextOffset),
- Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
- __ j(not_equal, &exit);
-
- // Arguments adaptor case: Read the arguments length from the
- // adaptor frame.
- __ movq(result.reg(),
- Operand(fp.reg(), ArgumentsAdaptorFrameConstants::kLengthOffset));
-
- __ bind(&exit);
- result.set_type_info(TypeInfo::Smi());
- if (FLAG_debug_code) {
- __ AbortIfNotSmi(result.reg());
- }
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::GenerateClassOf(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- JumpTarget leave, null, function, non_function_constructor;
- Load(args->at(0)); // Load the object.
- Result obj = frame_->Pop();
- obj.ToRegister();
- frame_->Spill(obj.reg());
-
- // If the object is a smi, we return null.
- Condition is_smi = masm_->CheckSmi(obj.reg());
- null.Branch(is_smi);
-
- // Check that the object is a JS object but take special care of JS
- // functions to make sure they have 'Function' as their class.
-
- __ CmpObjectType(obj.reg(), FIRST_JS_OBJECT_TYPE, obj.reg());
- null.Branch(below);
-
- // As long as JS_FUNCTION_TYPE is the last instance type and it is
- // right after LAST_JS_OBJECT_TYPE, we can avoid checking for
- // LAST_JS_OBJECT_TYPE.
- ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
- ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
- __ CmpInstanceType(obj.reg(), JS_FUNCTION_TYPE);
- function.Branch(equal);
-
- // Check if the constructor in the map is a function.
- __ movq(obj.reg(), FieldOperand(obj.reg(), Map::kConstructorOffset));
- __ CmpObjectType(obj.reg(), JS_FUNCTION_TYPE, kScratchRegister);
- non_function_constructor.Branch(not_equal);
-
- // The obj register now contains the constructor function. Grab the
- // instance class name from there.
- __ movq(obj.reg(),
- FieldOperand(obj.reg(), JSFunction::kSharedFunctionInfoOffset));
- __ movq(obj.reg(),
- FieldOperand(obj.reg(),
- SharedFunctionInfo::kInstanceClassNameOffset));
- frame_->Push(&obj);
- leave.Jump();
-
- // Functions have class 'Function'.
- function.Bind();
- frame_->Push(FACTORY->function_class_symbol());
- leave.Jump();
-
- // Objects with a non-function constructor have class 'Object'.
- non_function_constructor.Bind();
- frame_->Push(FACTORY->Object_symbol());
- leave.Jump();
-
- // Non-JS objects have class null.
- null.Bind();
- frame_->Push(FACTORY->null_value());
-
- // All done.
- leave.Bind();
-}
-
-
-void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- JumpTarget leave;
- Load(args->at(0)); // Load the object.
- frame_->Dup();
- Result object = frame_->Pop();
- object.ToRegister();
- ASSERT(object.is_valid());
- // if (object->IsSmi()) return object.
- Condition is_smi = masm_->CheckSmi(object.reg());
- leave.Branch(is_smi);
- // It is a heap object - get map.
- Result temp = allocator()->Allocate();
- ASSERT(temp.is_valid());
- // if (!object->IsJSValue()) return object.
- __ CmpObjectType(object.reg(), JS_VALUE_TYPE, temp.reg());
- leave.Branch(not_equal);
- __ movq(temp.reg(), FieldOperand(object.reg(), JSValue::kValueOffset));
- object.Unuse();
- frame_->SetElementAt(0, &temp);
- leave.Bind();
-}
-
-
-void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 2);
- JumpTarget leave;
- Load(args->at(0)); // Load the object.
- Load(args->at(1)); // Load the value.
- Result value = frame_->Pop();
- Result object = frame_->Pop();
- value.ToRegister();
- object.ToRegister();
-
- // if (object->IsSmi()) return value.
- Condition is_smi = masm_->CheckSmi(object.reg());
- leave.Branch(is_smi, &value);
-
- // It is a heap object - get its map.
- Result scratch = allocator_->Allocate();
- ASSERT(scratch.is_valid());
- // if (!object->IsJSValue()) return value.
- __ CmpObjectType(object.reg(), JS_VALUE_TYPE, scratch.reg());
- leave.Branch(not_equal, &value);
-
- // Store the value.
- __ movq(FieldOperand(object.reg(), JSValue::kValueOffset), value.reg());
- // Update the write barrier. Save the value as it will be
- // overwritten by the write barrier code and is needed afterward.
- Result duplicate_value = allocator_->Allocate();
- ASSERT(duplicate_value.is_valid());
- __ movq(duplicate_value.reg(), value.reg());
- // The object register is also overwritten by the write barrier and
- // possibly aliased in the frame.
- frame_->Spill(object.reg());
- __ RecordWrite(object.reg(), JSValue::kValueOffset, duplicate_value.reg(),
- scratch.reg());
- object.Unuse();
- scratch.Unuse();
- duplicate_value.Unuse();
-
- // Leave.
- leave.Bind(&value);
- frame_->Push(&value);
-}
-
-
-void CodeGenerator::GenerateArguments(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
-
- // ArgumentsAccessStub expects the key in rdx and the formal
- // parameter count in rax.
- Load(args->at(0));
- Result key = frame_->Pop();
- // Explicitly create a constant result.
- Result count(Handle<Smi>(Smi::FromInt(scope()->num_parameters())));
- // Call the shared stub to get to arguments[key].
- ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
- Result result = frame_->CallStub(&stub, &key, &count);
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 2);
-
- // Load the two objects into registers and perform the comparison.
- Load(args->at(0));
- Load(args->at(1));
- Result right = frame_->Pop();
- Result left = frame_->Pop();
- right.ToRegister();
- left.ToRegister();
- __ cmpq(right.reg(), left.reg());
- right.Unuse();
- left.Unuse();
- destination()->Split(equal);
-}
-
-
-void CodeGenerator::GenerateGetFramePointer(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 0);
- // RBP value is aligned, so it should be tagged as a smi (without necesarily
- // being padded as a smi, so it should not be treated as a smi.).
- STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
- Result rbp_as_smi = allocator_->Allocate();
- ASSERT(rbp_as_smi.is_valid());
- __ movq(rbp_as_smi.reg(), rbp);
- frame_->Push(&rbp_as_smi);
-}
-
-
-void CodeGenerator::GenerateRandomHeapNumber(
- ZoneList<Expression*>* args) {
- ASSERT(args->length() == 0);
- frame_->SpillAll();
-
- Label slow_allocate_heapnumber;
- Label heapnumber_allocated;
- __ AllocateHeapNumber(rbx, rcx, &slow_allocate_heapnumber);
- __ jmp(&heapnumber_allocated);
-
- __ bind(&slow_allocate_heapnumber);
- // Allocate a heap number.
- __ CallRuntime(Runtime::kNumberAlloc, 0);
- __ movq(rbx, rax);
-
- __ bind(&heapnumber_allocated);
-
- // Return a random uint32 number in rax.
- // The fresh HeapNumber is in rbx, which is callee-save on both x64 ABIs.
- __ PrepareCallCFunction(0);
- __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 0);
-
- // Convert 32 random bits in rax to 0.(32 random bits) in a double
- // by computing:
- // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
- __ movl(rcx, Immediate(0x49800000)); // 1.0 x 2^20 as single.
- __ movd(xmm1, rcx);
- __ movd(xmm0, rax);
- __ cvtss2sd(xmm1, xmm1);
- __ xorpd(xmm0, xmm1);
- __ subsd(xmm0, xmm1);
- __ movsd(FieldOperand(rbx, HeapNumber::kValueOffset), xmm0);
-
- __ movq(rax, rbx);
- Result result = allocator_->Allocate(rax);
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
- ASSERT_EQ(2, args->length());
-
- Load(args->at(0));
- Load(args->at(1));
-
- StringAddStub stub(NO_STRING_ADD_FLAGS);
- Result answer = frame_->CallStub(&stub, 2);
- frame_->Push(&answer);
-}
-
-
-void CodeGenerator::GenerateSubString(ZoneList<Expression*>* args) {
- ASSERT_EQ(3, args->length());
-
- Load(args->at(0));
- Load(args->at(1));
- Load(args->at(2));
-
- SubStringStub stub;
- Result answer = frame_->CallStub(&stub, 3);
- frame_->Push(&answer);
-}
-
-
-void CodeGenerator::GenerateStringCompare(ZoneList<Expression*>* args) {
- ASSERT_EQ(2, args->length());
-
- Load(args->at(0));
- Load(args->at(1));
-
- StringCompareStub stub;
- Result answer = frame_->CallStub(&stub, 2);
- frame_->Push(&answer);
-}
-
-
-void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
- ASSERT_EQ(args->length(), 4);
-
- // Load the arguments on the stack and call the runtime system.
- Load(args->at(0));
- Load(args->at(1));
- Load(args->at(2));
- Load(args->at(3));
- RegExpExecStub stub;
- Result result = frame_->CallStub(&stub, 4);
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
- ASSERT_EQ(3, args->length());
- Load(args->at(0)); // Size of array, smi.
- Load(args->at(1)); // "index" property value.
- Load(args->at(2)); // "input" property value.
- RegExpConstructResultStub stub;
- Result result = frame_->CallStub(&stub, 3);
- frame_->Push(&result);
-}
-
-
-class DeferredSearchCache: public DeferredCode {
- public:
- DeferredSearchCache(Register dst,
- Register cache,
- Register key,
- Register scratch)
- : dst_(dst), cache_(cache), key_(key), scratch_(scratch) {
- set_comment("[ DeferredSearchCache");
- }
-
- virtual void Generate();
-
- private:
- Register dst_; // on invocation index of finger (as int32), on exit
- // holds value being looked up.
- Register cache_; // instance of JSFunctionResultCache.
- Register key_; // key being looked up.
- Register scratch_;
-};
-
-
-// Return a position of the element at |index| + |additional_offset|
-// in FixedArray pointer to which is held in |array|. |index| is int32.
-static Operand ArrayElement(Register array,
- Register index,
- int additional_offset = 0) {
- int offset = FixedArray::kHeaderSize + additional_offset * kPointerSize;
- return FieldOperand(array, index, times_pointer_size, offset);
-}
-
-
-void DeferredSearchCache::Generate() {
- Label first_loop, search_further, second_loop, cache_miss;
-
- Immediate kEntriesIndexImm = Immediate(JSFunctionResultCache::kEntriesIndex);
- Immediate kEntrySizeImm = Immediate(JSFunctionResultCache::kEntrySize);
-
- // Check the cache from finger to start of the cache.
- __ bind(&first_loop);
- __ subl(dst_, kEntrySizeImm);
- __ cmpl(dst_, kEntriesIndexImm);
- __ j(less, &search_further);
-
- __ cmpq(ArrayElement(cache_, dst_), key_);
- __ j(not_equal, &first_loop);
-
- __ Integer32ToSmiField(
- FieldOperand(cache_, JSFunctionResultCache::kFingerOffset), dst_);
- __ movq(dst_, ArrayElement(cache_, dst_, 1));
- __ jmp(exit_label());
-
- __ bind(&search_further);
-
- // Check the cache from end of cache up to finger.
- __ SmiToInteger32(dst_,
- FieldOperand(cache_,
- JSFunctionResultCache::kCacheSizeOffset));
- __ SmiToInteger32(scratch_,
- FieldOperand(cache_, JSFunctionResultCache::kFingerOffset));
-
- __ bind(&second_loop);
- __ subl(dst_, kEntrySizeImm);
- __ cmpl(dst_, scratch_);
- __ j(less_equal, &cache_miss);
-
- __ cmpq(ArrayElement(cache_, dst_), key_);
- __ j(not_equal, &second_loop);
-
- __ Integer32ToSmiField(
- FieldOperand(cache_, JSFunctionResultCache::kFingerOffset), dst_);
- __ movq(dst_, ArrayElement(cache_, dst_, 1));
- __ jmp(exit_label());
-
- __ bind(&cache_miss);
- __ push(cache_); // store a reference to cache
- __ push(key_); // store a key
- __ push(Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
- __ push(key_);
- // On x64 function must be in rdi.
- __ movq(rdi, FieldOperand(cache_, JSFunctionResultCache::kFactoryOffset));
- ParameterCount expected(1);
- __ InvokeFunction(rdi, expected, CALL_FUNCTION);
-
- // Find a place to put new cached value into.
- Label add_new_entry, update_cache;
- __ movq(rcx, Operand(rsp, kPointerSize)); // restore the cache
- // Possible optimization: cache size is constant for the given cache
- // so technically we could use a constant here. However, if we have
- // cache miss this optimization would hardly matter much.
-
- // Check if we could add new entry to cache.
- __ SmiToInteger32(rbx, FieldOperand(rcx, FixedArray::kLengthOffset));
- __ SmiToInteger32(r9,
- FieldOperand(rcx, JSFunctionResultCache::kCacheSizeOffset));
- __ cmpl(rbx, r9);
- __ j(greater, &add_new_entry);
-
- // Check if we could evict entry after finger.
- __ SmiToInteger32(rdx,
- FieldOperand(rcx, JSFunctionResultCache::kFingerOffset));
- __ addl(rdx, kEntrySizeImm);
- Label forward;
- __ cmpl(rbx, rdx);
- __ j(greater, &forward);
- // Need to wrap over the cache.
- __ movl(rdx, kEntriesIndexImm);
- __ bind(&forward);
- __ movl(r9, rdx);
- __ jmp(&update_cache);
-
- __ bind(&add_new_entry);
- // r9 holds cache size as int32.
- __ leal(rbx, Operand(r9, JSFunctionResultCache::kEntrySize));
- __ Integer32ToSmiField(
- FieldOperand(rcx, JSFunctionResultCache::kCacheSizeOffset), rbx);
-
- // Update the cache itself.
- // r9 holds the index as int32.
- __ bind(&update_cache);
- __ pop(rbx); // restore the key
- __ Integer32ToSmiField(
- FieldOperand(rcx, JSFunctionResultCache::kFingerOffset), r9);
- // Store key.
- __ movq(ArrayElement(rcx, r9), rbx);
- __ RecordWrite(rcx, 0, rbx, r9);
-
- // Store value.
- __ pop(rcx); // restore the cache.
- __ SmiToInteger32(rdx,
- FieldOperand(rcx, JSFunctionResultCache::kFingerOffset));
- __ incl(rdx);
- // Backup rax, because the RecordWrite macro clobbers its arguments.
- __ movq(rbx, rax);
- __ movq(ArrayElement(rcx, rdx), rax);
- __ RecordWrite(rcx, 0, rbx, rdx);
-
- if (!dst_.is(rax)) {
- __ movq(dst_, rax);
- }
-}
-
-
-void CodeGenerator::GenerateGetFromCache(ZoneList<Expression*>* args) {
- ASSERT_EQ(2, args->length());
-
- ASSERT_NE(NULL, args->at(0)->AsLiteral());
- int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
-
- Handle<FixedArray> jsfunction_result_caches(
- Isolate::Current()->global_context()->jsfunction_result_caches());
- if (jsfunction_result_caches->length() <= cache_id) {
- __ Abort("Attempt to use undefined cache.");
- frame_->Push(FACTORY->undefined_value());
- return;
- }
-
- Load(args->at(1));
- Result key = frame_->Pop();
- key.ToRegister();
-
- Result cache = allocator()->Allocate();
- ASSERT(cache.is_valid());
- __ movq(cache.reg(), ContextOperand(rsi, Context::GLOBAL_INDEX));
- __ movq(cache.reg(),
- FieldOperand(cache.reg(), GlobalObject::kGlobalContextOffset));
- __ movq(cache.reg(),
- ContextOperand(cache.reg(), Context::JSFUNCTION_RESULT_CACHES_INDEX));
- __ movq(cache.reg(),
- FieldOperand(cache.reg(), FixedArray::OffsetOfElementAt(cache_id)));
-
- Result tmp = allocator()->Allocate();
- ASSERT(tmp.is_valid());
-
- Result scratch = allocator()->Allocate();
- ASSERT(scratch.is_valid());
-
- DeferredSearchCache* deferred = new DeferredSearchCache(tmp.reg(),
- cache.reg(),
- key.reg(),
- scratch.reg());
-
- const int kFingerOffset =
- FixedArray::OffsetOfElementAt(JSFunctionResultCache::kFingerIndex);
- // tmp.reg() now holds finger offset as a smi.
- __ SmiToInteger32(tmp.reg(), FieldOperand(cache.reg(), kFingerOffset));
- __ cmpq(key.reg(), FieldOperand(cache.reg(),
- tmp.reg(), times_pointer_size,
- FixedArray::kHeaderSize));
- deferred->Branch(not_equal);
- __ movq(tmp.reg(), FieldOperand(cache.reg(),
- tmp.reg(), times_pointer_size,
- FixedArray::kHeaderSize + kPointerSize));
-
- deferred->BindExit();
- frame_->Push(&tmp);
-}
-
-
-void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
- ASSERT_EQ(args->length(), 1);
-
- // Load the argument on the stack and jump to the runtime.
- Load(args->at(0));
-
- NumberToStringStub stub;
- Result result = frame_->CallStub(&stub, 1);
- frame_->Push(&result);
-}
-
-
-class DeferredSwapElements: public DeferredCode {
- public:
- DeferredSwapElements(Register object, Register index1, Register index2)
- : object_(object), index1_(index1), index2_(index2) {
- set_comment("[ DeferredSwapElements");
- }
-
- virtual void Generate();
-
- private:
- Register object_, index1_, index2_;
-};
-
-
-void DeferredSwapElements::Generate() {
- __ push(object_);
- __ push(index1_);
- __ push(index2_);
- __ CallRuntime(Runtime::kSwapElements, 3);
-}
-
-
-void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) {
- Comment cmnt(masm_, "[ GenerateSwapElements");
-
- ASSERT_EQ(3, args->length());
-
- Load(args->at(0));
- Load(args->at(1));
- Load(args->at(2));
-
- Result index2 = frame_->Pop();
- index2.ToRegister();
-
- Result index1 = frame_->Pop();
- index1.ToRegister();
-
- Result object = frame_->Pop();
- object.ToRegister();
-
- Result tmp1 = allocator()->Allocate();
- tmp1.ToRegister();
- Result tmp2 = allocator()->Allocate();
- tmp2.ToRegister();
-
- frame_->Spill(object.reg());
- frame_->Spill(index1.reg());
- frame_->Spill(index2.reg());
-
- DeferredSwapElements* deferred = new DeferredSwapElements(object.reg(),
- index1.reg(),
- index2.reg());
-
- // Fetch the map and check if array is in fast case.
- // Check that object doesn't require security checks and
- // has no indexed interceptor.
- __ CmpObjectType(object.reg(), JS_ARRAY_TYPE, tmp1.reg());
- deferred->Branch(not_equal);
- __ testb(FieldOperand(tmp1.reg(), Map::kBitFieldOffset),
- Immediate(KeyedLoadIC::kSlowCaseBitFieldMask));
- deferred->Branch(not_zero);
-
- // Check the object's elements are in fast case and writable.
- __ movq(tmp1.reg(), FieldOperand(object.reg(), JSObject::kElementsOffset));
- __ CompareRoot(FieldOperand(tmp1.reg(), HeapObject::kMapOffset),
- Heap::kFixedArrayMapRootIndex);
- deferred->Branch(not_equal);
-
- // Check that both indices are smis.
- Condition both_smi = masm()->CheckBothSmi(index1.reg(), index2.reg());
- deferred->Branch(NegateCondition(both_smi));
-
- // Check that both indices are valid.
- __ movq(tmp2.reg(), FieldOperand(object.reg(), JSArray::kLengthOffset));
- __ SmiCompare(tmp2.reg(), index1.reg());
- deferred->Branch(below_equal);
- __ SmiCompare(tmp2.reg(), index2.reg());
- deferred->Branch(below_equal);
-
- // Bring addresses into index1 and index2.
- __ SmiToInteger32(index1.reg(), index1.reg());
- __ lea(index1.reg(), FieldOperand(tmp1.reg(),
- index1.reg(),
- times_pointer_size,
- FixedArray::kHeaderSize));
- __ SmiToInteger32(index2.reg(), index2.reg());
- __ lea(index2.reg(), FieldOperand(tmp1.reg(),
- index2.reg(),
- times_pointer_size,
- FixedArray::kHeaderSize));
-
- // Swap elements.
- __ movq(object.reg(), Operand(index1.reg(), 0));
- __ movq(tmp2.reg(), Operand(index2.reg(), 0));
- __ movq(Operand(index2.reg(), 0), object.reg());
- __ movq(Operand(index1.reg(), 0), tmp2.reg());
-
- Label done;
- __ InNewSpace(tmp1.reg(), tmp2.reg(), equal, &done);
- // Possible optimization: do a check that both values are smis
- // (or them and test against Smi mask.)
-
- __ movq(tmp2.reg(), tmp1.reg());
- __ RecordWriteHelper(tmp1.reg(), index1.reg(), object.reg());
- __ RecordWriteHelper(tmp2.reg(), index2.reg(), object.reg());
- __ bind(&done);
-
- deferred->BindExit();
- frame_->Push(FACTORY->undefined_value());
-}
-
-
-void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) {
- Comment cmnt(masm_, "[ GenerateCallFunction");
-
- ASSERT(args->length() >= 2);
-
- int n_args = args->length() - 2; // for receiver and function.
- Load(args->at(0)); // receiver
- for (int i = 0; i < n_args; i++) {
- Load(args->at(i + 1));
- }
- Load(args->at(n_args + 1)); // function
- Result result = frame_->CallJSFunction(n_args);
- frame_->Push(&result);
-}
-
-
-// Generates the Math.pow method. Only handles special cases and
-// branches to the runtime system for everything else. Please note
-// that this function assumes that the callsite has executed ToNumber
-// on both arguments.
-void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 2);
- Load(args->at(0));
- Load(args->at(1));
-
- Label allocate_return;
- // Load the two operands while leaving the values on the frame.
- frame()->Dup();
- Result exponent = frame()->Pop();
- exponent.ToRegister();
- frame()->Spill(exponent.reg());
- frame()->PushElementAt(1);
- Result base = frame()->Pop();
- base.ToRegister();
- frame()->Spill(base.reg());
-
- Result answer = allocator()->Allocate();
- ASSERT(answer.is_valid());
- ASSERT(!exponent.reg().is(base.reg()));
- JumpTarget call_runtime;
-
- // Save 1 in xmm3 - we need this several times later on.
- __ movl(answer.reg(), Immediate(1));
- __ cvtlsi2sd(xmm3, answer.reg());
-
- Label exponent_nonsmi;
- Label base_nonsmi;
- // If the exponent is a heap number go to that specific case.
- __ JumpIfNotSmi(exponent.reg(), &exponent_nonsmi);
- __ JumpIfNotSmi(base.reg(), &base_nonsmi);
-
- // Optimized version when y is an integer.
- Label powi;
- __ SmiToInteger32(base.reg(), base.reg());
- __ cvtlsi2sd(xmm0, base.reg());
- __ jmp(&powi);
- // exponent is smi and base is a heapnumber.
- __ bind(&base_nonsmi);
- __ CompareRoot(FieldOperand(base.reg(), HeapObject::kMapOffset),
- Heap::kHeapNumberMapRootIndex);
- call_runtime.Branch(not_equal);
-
- __ movsd(xmm0, FieldOperand(base.reg(), HeapNumber::kValueOffset));
-
- // Optimized version of pow if y is an integer.
- __ bind(&powi);
- __ SmiToInteger32(exponent.reg(), exponent.reg());
-
- // Save exponent in base as we need to check if exponent is negative later.
- // We know that base and exponent are in different registers.
- __ movl(base.reg(), exponent.reg());
-
- // Get absolute value of exponent.
- Label no_neg;
- __ cmpl(exponent.reg(), Immediate(0));
- __ j(greater_equal, &no_neg);
- __ negl(exponent.reg());
- __ bind(&no_neg);
-
- // Load xmm1 with 1.
- __ movsd(xmm1, xmm3);
- Label while_true;
- Label no_multiply;
-
- __ bind(&while_true);
- __ shrl(exponent.reg(), Immediate(1));
- __ j(not_carry, &no_multiply);
- __ mulsd(xmm1, xmm0);
- __ bind(&no_multiply);
- __ testl(exponent.reg(), exponent.reg());
- __ mulsd(xmm0, xmm0);
- __ j(not_zero, &while_true);
-
- // x has the original value of y - if y is negative return 1/result.
- __ testl(base.reg(), base.reg());
- __ j(positive, &allocate_return);
- // Special case if xmm1 has reached infinity.
- __ movl(answer.reg(), Immediate(0x7FB00000));
- __ movd(xmm0, answer.reg());
- __ cvtss2sd(xmm0, xmm0);
- __ ucomisd(xmm0, xmm1);
- call_runtime.Branch(equal);
- __ divsd(xmm3, xmm1);
- __ movsd(xmm1, xmm3);
- __ jmp(&allocate_return);
-
- // exponent (or both) is a heapnumber - no matter what we should now work
- // on doubles.
- __ bind(&exponent_nonsmi);
- __ CompareRoot(FieldOperand(exponent.reg(), HeapObject::kMapOffset),
- Heap::kHeapNumberMapRootIndex);
- call_runtime.Branch(not_equal);
- __ movsd(xmm1, FieldOperand(exponent.reg(), HeapNumber::kValueOffset));
- // Test if exponent is nan.
- __ ucomisd(xmm1, xmm1);
- call_runtime.Branch(parity_even);
-
- Label base_not_smi;
- Label handle_special_cases;
- __ JumpIfNotSmi(base.reg(), &base_not_smi);
- __ SmiToInteger32(base.reg(), base.reg());
- __ cvtlsi2sd(xmm0, base.reg());
- __ jmp(&handle_special_cases);
- __ bind(&base_not_smi);
- __ CompareRoot(FieldOperand(base.reg(), HeapObject::kMapOffset),
- Heap::kHeapNumberMapRootIndex);
- call_runtime.Branch(not_equal);
- __ movl(answer.reg(), FieldOperand(base.reg(), HeapNumber::kExponentOffset));
- __ andl(answer.reg(), Immediate(HeapNumber::kExponentMask));
- __ cmpl(answer.reg(), Immediate(HeapNumber::kExponentMask));
- // base is NaN or +/-Infinity
- call_runtime.Branch(greater_equal);
- __ movsd(xmm0, FieldOperand(base.reg(), HeapNumber::kValueOffset));
-
- // base is in xmm0 and exponent is in xmm1.
- __ bind(&handle_special_cases);
- Label not_minus_half;
- // Test for -0.5.
- // Load xmm2 with -0.5.
- __ movl(answer.reg(), Immediate(0xBF000000));
- __ movd(xmm2, answer.reg());
- __ cvtss2sd(xmm2, xmm2);
- // xmm2 now has -0.5.
- __ ucomisd(xmm2, xmm1);
- __ j(not_equal, ¬_minus_half);
-
- // Calculates reciprocal of square root.
- // sqrtsd returns -0 when input is -0. ECMA spec requires +0.
- __ xorpd(xmm1, xmm1);
- __ addsd(xmm1, xmm0);
- __ sqrtsd(xmm1, xmm1);
- __ divsd(xmm3, xmm1);
- __ movsd(xmm1, xmm3);
- __ jmp(&allocate_return);
-
- // Test for 0.5.
- __ bind(¬_minus_half);
- // Load xmm2 with 0.5.
- // Since xmm3 is 1 and xmm2 is -0.5 this is simply xmm2 + xmm3.
- __ addsd(xmm2, xmm3);
- // xmm2 now has 0.5.
- __ ucomisd(xmm2, xmm1);
- call_runtime.Branch(not_equal);
-
- // Calculates square root.
- // sqrtsd returns -0 when input is -0. ECMA spec requires +0.
- __ xorpd(xmm1, xmm1);
- __ addsd(xmm1, xmm0);
- __ sqrtsd(xmm1, xmm1);
-
- JumpTarget done;
- Label failure, success;
- __ bind(&allocate_return);
- // Make a copy of the frame to enable us to handle allocation
- // failure after the JumpTarget jump.
- VirtualFrame* clone = new VirtualFrame(frame());
- __ AllocateHeapNumber(answer.reg(), exponent.reg(), &failure);
- __ movsd(FieldOperand(answer.reg(), HeapNumber::kValueOffset), xmm1);
- // Remove the two original values from the frame - we only need those
- // in the case where we branch to runtime.
- frame()->Drop(2);
- exponent.Unuse();
- base.Unuse();
- done.Jump(&answer);
- // Use the copy of the original frame as our current frame.
- RegisterFile empty_regs;
- SetFrame(clone, &empty_regs);
- // If we experience an allocation failure we branch to runtime.
- __ bind(&failure);
- call_runtime.Bind();
- answer = frame()->CallRuntime(Runtime::kMath_pow_cfunction, 2);
-
- done.Bind(&answer);
- frame()->Push(&answer);
-}
-
-
-void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
- ASSERT_EQ(args->length(), 1);
- Load(args->at(0));
- TranscendentalCacheStub stub(TranscendentalCache::SIN,
- TranscendentalCacheStub::TAGGED);
- Result result = frame_->CallStub(&stub, 1);
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
- ASSERT_EQ(args->length(), 1);
- Load(args->at(0));
- TranscendentalCacheStub stub(TranscendentalCache::COS,
- TranscendentalCacheStub::TAGGED);
- Result result = frame_->CallStub(&stub, 1);
- frame_->Push(&result);
-}
-
-
-void CodeGenerator::GenerateMathLog(ZoneList<Expression*>* args) {
- ASSERT_EQ(args->length(), 1);
- Load(args->at(0));
- TranscendentalCacheStub stub(TranscendentalCache::LOG,
- TranscendentalCacheStub::TAGGED);
- Result result = frame_->CallStub(&stub, 1);
- frame_->Push(&result);
-}
-
-
-// Generates the Math.sqrt method. Please note - this function assumes that
-// the callsite has executed ToNumber on the argument.
-void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
-
- // Leave original value on the frame if we need to call runtime.
- frame()->Dup();
- Result result = frame()->Pop();
- result.ToRegister();
- frame()->Spill(result.reg());
- Label runtime;
- Label non_smi;
- Label load_done;
- JumpTarget end;
-
- __ JumpIfNotSmi(result.reg(), &non_smi);
- __ SmiToInteger32(result.reg(), result.reg());
- __ cvtlsi2sd(xmm0, result.reg());
- __ jmp(&load_done);
- __ bind(&non_smi);
- __ CompareRoot(FieldOperand(result.reg(), HeapObject::kMapOffset),
- Heap::kHeapNumberMapRootIndex);
- __ j(not_equal, &runtime);
- __ movsd(xmm0, FieldOperand(result.reg(), HeapNumber::kValueOffset));
-
- __ bind(&load_done);
- __ sqrtsd(xmm0, xmm0);
- // A copy of the virtual frame to allow us to go to runtime after the
- // JumpTarget jump.
- Result scratch = allocator()->Allocate();
- VirtualFrame* clone = new VirtualFrame(frame());
- __ AllocateHeapNumber(result.reg(), scratch.reg(), &runtime);
-
- __ movsd(FieldOperand(result.reg(), HeapNumber::kValueOffset), xmm0);
- frame()->Drop(1);
- scratch.Unuse();
- end.Jump(&result);
- // We only branch to runtime if we have an allocation error.
- // Use the copy of the original frame as our current frame.
- RegisterFile empty_regs;
- SetFrame(clone, &empty_regs);
- __ bind(&runtime);
- result = frame()->CallRuntime(Runtime::kMath_sqrt, 1);
-
- end.Bind(&result);
- frame()->Push(&result);
-}
-
-
-void CodeGenerator::GenerateIsRegExpEquivalent(ZoneList<Expression*>* args) {
- ASSERT_EQ(2, args->length());
- Load(args->at(0));
- Load(args->at(1));
- Result right_res = frame_->Pop();
- Result left_res = frame_->Pop();
- right_res.ToRegister();
- left_res.ToRegister();
- Result tmp_res = allocator()->Allocate();
- ASSERT(tmp_res.is_valid());
- Register right = right_res.reg();
- Register left = left_res.reg();
- Register tmp = tmp_res.reg();
- right_res.Unuse();
- left_res.Unuse();
- tmp_res.Unuse();
- __ cmpq(left, right);
- destination()->true_target()->Branch(equal);
- // Fail if either is a non-HeapObject.
- Condition either_smi =
- masm()->CheckEitherSmi(left, right, tmp);
- destination()->false_target()->Branch(either_smi);
- __ movq(tmp, FieldOperand(left, HeapObject::kMapOffset));
- __ cmpb(FieldOperand(tmp, Map::kInstanceTypeOffset),
- Immediate(JS_REGEXP_TYPE));
- destination()->false_target()->Branch(not_equal);
- __ cmpq(tmp, FieldOperand(right, HeapObject::kMapOffset));
- destination()->false_target()->Branch(not_equal);
- __ movq(tmp, FieldOperand(left, JSRegExp::kDataOffset));
- __ cmpq(tmp, FieldOperand(right, JSRegExp::kDataOffset));
- destination()->Split(equal);
-}
-
-
-void CodeGenerator::GenerateHasCachedArrayIndex(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result value = frame_->Pop();
- value.ToRegister();
- ASSERT(value.is_valid());
- __ testl(FieldOperand(value.reg(), String::kHashFieldOffset),
- Immediate(String::kContainsCachedArrayIndexMask));
- value.Unuse();
- destination()->Split(zero);
-}
-
-
-void CodeGenerator::GenerateGetCachedArrayIndex(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- Load(args->at(0));
- Result string = frame_->Pop();
- string.ToRegister();
-
- Result number = allocator()->Allocate();
- ASSERT(number.is_valid());
- __ movl(number.reg(), FieldOperand(string.reg(), String::kHashFieldOffset));
- __ IndexFromHash(number.reg(), number.reg());
- string.Unuse();
- frame_->Push(&number);
-}
-
-
-void CodeGenerator::GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args) {
- frame_->Push(FACTORY->undefined_value());
-}
-
-
-void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
- if (CheckForInlineRuntimeCall(node)) {
- return;
- }
-
- ZoneList<Expression*>* args = node->arguments();
- Comment cmnt(masm_, "[ CallRuntime");
- const Runtime::Function* function = node->function();
-
- if (function == NULL) {
- // Push the builtins object found in the current global object.
- Result temp = allocator()->Allocate();
- ASSERT(temp.is_valid());
- __ movq(temp.reg(), GlobalObjectOperand());
- __ movq(temp.reg(),
- FieldOperand(temp.reg(), GlobalObject::kBuiltinsOffset));
- frame_->Push(&temp);
- }
-
- // Push the arguments ("left-to-right").
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- }
-
- if (function == NULL) {
- // Call the JS runtime function.
- frame_->Push(node->name());
- Result answer = frame_->CallCallIC(RelocInfo::CODE_TARGET,
- arg_count,
- loop_nesting_);
- frame_->RestoreContextRegister();
- frame_->Push(&answer);
- } else {
- // Call the C runtime function.
- Result answer = frame_->CallRuntime(function, arg_count);
- frame_->Push(&answer);
- }
-}
-
-
-void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
- Comment cmnt(masm_, "[ UnaryOperation");
-
- Token::Value op = node->op();
-
- if (op == Token::NOT) {
- // Swap the true and false targets but keep the same actual label
- // as the fall through.
- destination()->Invert();
- LoadCondition(node->expression(), destination(), true);
- // Swap the labels back.
- destination()->Invert();
-
- } else if (op == Token::DELETE) {
- Property* property = node->expression()->AsProperty();
- if (property != NULL) {
- Load(property->obj());
- Load(property->key());
- frame_->Push(Smi::FromInt(strict_mode_flag()));
- Result answer = frame_->InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION, 3);
- frame_->Push(&answer);
- return;
- }
-
- Variable* variable = node->expression()->AsVariableProxy()->AsVariable();
- if (variable != NULL) {
- // Delete of an unqualified identifier is disallowed in strict mode
- // but "delete this" is.
- ASSERT(strict_mode_flag() == kNonStrictMode || variable->is_this());
- Slot* slot = variable->AsSlot();
- if (variable->is_global()) {
- LoadGlobal();
- frame_->Push(variable->name());
- frame_->Push(Smi::FromInt(kNonStrictMode));
- Result answer = frame_->InvokeBuiltin(Builtins::DELETE,
- CALL_FUNCTION, 3);
- frame_->Push(&answer);
-
- } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
- // Call the runtime to delete from the context holding the named
- // variable. Sync the virtual frame eagerly so we can push the
- // arguments directly into place.
- frame_->SyncRange(0, frame_->element_count() - 1);
- frame_->EmitPush(rsi);
- frame_->EmitPush(variable->name());
- Result answer = frame_->CallRuntime(Runtime::kDeleteContextSlot, 2);
- frame_->Push(&answer);
- } else {
- // Default: Result of deleting non-global, not dynamically
- // introduced variables is false.
- frame_->Push(FACTORY->false_value());
- }
- } else {
- // Default: Result of deleting expressions is true.
- Load(node->expression()); // may have side-effects
- frame_->SetElementAt(0, FACTORY->true_value());
- }
-
- } else if (op == Token::TYPEOF) {
- // Special case for loading the typeof expression; see comment on
- // LoadTypeofExpression().
- LoadTypeofExpression(node->expression());
- Result answer = frame_->CallRuntime(Runtime::kTypeof, 1);
- frame_->Push(&answer);
-
- } else if (op == Token::VOID) {
- Expression* expression = node->expression();
- if (expression && expression->AsLiteral() && (
- expression->AsLiteral()->IsTrue() ||
- expression->AsLiteral()->IsFalse() ||
- expression->AsLiteral()->handle()->IsNumber() ||
- expression->AsLiteral()->handle()->IsString() ||
- expression->AsLiteral()->handle()->IsJSRegExp() ||
- expression->AsLiteral()->IsNull())) {
- // Omit evaluating the value of the primitive literal.
- // It will be discarded anyway, and can have no side effect.
- frame_->Push(FACTORY->undefined_value());
- } else {
- Load(node->expression());
- frame_->SetElementAt(0, FACTORY->undefined_value());
- }
-
- } else {
- bool can_overwrite = node->expression()->ResultOverwriteAllowed();
- UnaryOverwriteMode overwrite =
- can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
- bool no_negative_zero = node->expression()->no_negative_zero();
- Load(node->expression());
- switch (op) {
- case Token::NOT:
- case Token::DELETE:
- case Token::TYPEOF:
- UNREACHABLE(); // handled above
- break;
-
- case Token::SUB: {
- GenericUnaryOpStub stub(
- Token::SUB,
- overwrite,
- NO_UNARY_FLAGS,
- no_negative_zero ? kIgnoreNegativeZero : kStrictNegativeZero);
- Result operand = frame_->Pop();
- Result answer = frame_->CallStub(&stub, &operand);
- answer.set_type_info(TypeInfo::Number());
- frame_->Push(&answer);
- break;
- }
-
- case Token::BIT_NOT: {
- // Smi check.
- JumpTarget smi_label;
- JumpTarget continue_label;
- Result operand = frame_->Pop();
- operand.ToRegister();
-
- Condition is_smi = masm_->CheckSmi(operand.reg());
- smi_label.Branch(is_smi, &operand);
-
- GenericUnaryOpStub stub(Token::BIT_NOT,
- overwrite,
- NO_UNARY_SMI_CODE_IN_STUB);
- Result answer = frame_->CallStub(&stub, &operand);
- continue_label.Jump(&answer);
-
- smi_label.Bind(&answer);
- answer.ToRegister();
- frame_->Spill(answer.reg());
- __ SmiNot(answer.reg(), answer.reg());
- continue_label.Bind(&answer);
- answer.set_type_info(TypeInfo::Smi());
- frame_->Push(&answer);
- break;
- }
-
- case Token::ADD: {
- // Smi check.
- JumpTarget continue_label;
- Result operand = frame_->Pop();
- TypeInfo operand_info = operand.type_info();
- operand.ToRegister();
- Condition is_smi = masm_->CheckSmi(operand.reg());
- continue_label.Branch(is_smi, &operand);
- frame_->Push(&operand);
- Result answer = frame_->InvokeBuiltin(Builtins::TO_NUMBER,
- CALL_FUNCTION, 1);
-
- continue_label.Bind(&answer);
- if (operand_info.IsSmi()) {
- answer.set_type_info(TypeInfo::Smi());
- } else if (operand_info.IsInteger32()) {
- answer.set_type_info(TypeInfo::Integer32());
- } else {
- answer.set_type_info(TypeInfo::Number());
- }
- frame_->Push(&answer);
- break;
- }
- default:
- UNREACHABLE();
- }
- }
-}
-
-
-// The value in dst was optimistically incremented or decremented.
-// The result overflowed or was not smi tagged. Call into the runtime
-// to convert the argument to a number, and call the specialized add
-// or subtract stub. The result is left in dst.
-class DeferredPrefixCountOperation: public DeferredCode {
- public:
- DeferredPrefixCountOperation(Register dst,
- bool is_increment,
- TypeInfo input_type)
- : dst_(dst), is_increment_(is_increment), input_type_(input_type) {
- set_comment("[ DeferredCountOperation");
- }
-
- virtual void Generate();
-
- private:
- Register dst_;
- bool is_increment_;
- TypeInfo input_type_;
-};
-
-
-void DeferredPrefixCountOperation::Generate() {
- Register left;
- if (input_type_.IsNumber()) {
- left = dst_;
- } else {
- __ push(dst_);
- __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
- left = rax;
- }
-
- GenericBinaryOpStub stub(is_increment_ ? Token::ADD : Token::SUB,
- NO_OVERWRITE,
- NO_GENERIC_BINARY_FLAGS,
- TypeInfo::Number());
- stub.GenerateCall(masm_, left, Smi::FromInt(1));
-
- if (!dst_.is(rax)) __ movq(dst_, rax);
-}
-
-
-// The value in dst was optimistically incremented or decremented.
-// The result overflowed or was not smi tagged. Call into the runtime
-// to convert the argument to a number. Update the original value in
-// old. Call the specialized add or subtract stub. The result is
-// left in dst.
-class DeferredPostfixCountOperation: public DeferredCode {
- public:
- DeferredPostfixCountOperation(Register dst,
- Register old,
- bool is_increment,
- TypeInfo input_type)
- : dst_(dst),
- old_(old),
- is_increment_(is_increment),
- input_type_(input_type) {
- set_comment("[ DeferredCountOperation");
- }
-
- virtual void Generate();
-
- private:
- Register dst_;
- Register old_;
- bool is_increment_;
- TypeInfo input_type_;
-};
-
-
-void DeferredPostfixCountOperation::Generate() {
- Register left;
- if (input_type_.IsNumber()) {
- __ push(dst_); // Save the input to use as the old value.
- left = dst_;
- } else {
- __ push(dst_);
- __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
- __ push(rax); // Save the result of ToNumber to use as the old value.
- left = rax;
- }
-
- GenericBinaryOpStub stub(is_increment_ ? Token::ADD : Token::SUB,
- NO_OVERWRITE,
- NO_GENERIC_BINARY_FLAGS,
- TypeInfo::Number());
- stub.GenerateCall(masm_, left, Smi::FromInt(1));
-
- if (!dst_.is(rax)) __ movq(dst_, rax);
- __ pop(old_);
-}
-
-
-void CodeGenerator::VisitCountOperation(CountOperation* node) {
- Comment cmnt(masm_, "[ CountOperation");
-
- bool is_postfix = node->is_postfix();
- bool is_increment = node->op() == Token::INC;
-
- Variable* var = node->expression()->AsVariableProxy()->AsVariable();
- bool is_const = (var != NULL && var->mode() == Variable::CONST);
-
- // Postfix operations need a stack slot under the reference to hold
- // the old value while the new value is being stored. This is so that
- // in the case that storing the new value requires a call, the old
- // value will be in the frame to be spilled.
- if (is_postfix) frame_->Push(Smi::FromInt(0));
-
- // A constant reference is not saved to, so the reference is not a
- // compound assignment reference.
- { Reference target(this, node->expression(), !is_const);
- if (target.is_illegal()) {
- // Spoof the virtual frame to have the expected height (one higher
- // than on entry).
- if (!is_postfix) frame_->Push(Smi::FromInt(0));
- return;
- }
- target.TakeValue();
-
- Result new_value = frame_->Pop();
- new_value.ToRegister();
-
- Result old_value; // Only allocated in the postfix case.
- if (is_postfix) {
- // Allocate a temporary to preserve the old value.
- old_value = allocator_->Allocate();
- ASSERT(old_value.is_valid());
- __ movq(old_value.reg(), new_value.reg());
-
- // The return value for postfix operations is ToNumber(input).
- // Keep more precise type info if the input is some kind of
- // number already. If the input is not a number we have to wait
- // for the deferred code to convert it.
- if (new_value.type_info().IsNumber()) {
- old_value.set_type_info(new_value.type_info());
- }
- }
- // Ensure the new value is writable.
- frame_->Spill(new_value.reg());
-
- DeferredCode* deferred = NULL;
- if (is_postfix) {
- deferred = new DeferredPostfixCountOperation(new_value.reg(),
- old_value.reg(),
- is_increment,
- new_value.type_info());
- } else {
- deferred = new DeferredPrefixCountOperation(new_value.reg(),
- is_increment,
- new_value.type_info());
- }
-
- if (new_value.is_smi()) {
- if (FLAG_debug_code) { __ AbortIfNotSmi(new_value.reg()); }
- } else {
- __ JumpIfNotSmi(new_value.reg(), deferred->entry_label());
- }
- if (is_increment) {
- __ SmiAddConstant(new_value.reg(),
- new_value.reg(),
- Smi::FromInt(1),
- deferred->entry_label());
- } else {
- __ SmiSubConstant(new_value.reg(),
- new_value.reg(),
- Smi::FromInt(1),
- deferred->entry_label());
- }
- deferred->BindExit();
-
- // Postfix count operations return their input converted to
- // number. The case when the input is already a number is covered
- // above in the allocation code for old_value.
- if (is_postfix && !new_value.type_info().IsNumber()) {
- old_value.set_type_info(TypeInfo::Number());
- }
-
- new_value.set_type_info(TypeInfo::Number());
-
- // Postfix: store the old value in the allocated slot under the
- // reference.
- if (is_postfix) frame_->SetElementAt(target.size(), &old_value);
-
- frame_->Push(&new_value);
- // Non-constant: update the reference.
- if (!is_const) target.SetValue(NOT_CONST_INIT);
- }
-
- // Postfix: drop the new value and use the old.
- if (is_postfix) frame_->Drop();
-}
-
-
-void CodeGenerator::GenerateLogicalBooleanOperation(BinaryOperation* node) {
- // According to ECMA-262 section 11.11, page 58, the binary logical
- // operators must yield the result of one of the two expressions
- // before any ToBoolean() conversions. This means that the value
- // produced by a && or || operator is not necessarily a boolean.
-
- // NOTE: If the left hand side produces a materialized value (not
- // control flow), we force the right hand side to do the same. This
- // is necessary because we assume that if we get control flow on the
- // last path out of an expression we got it on all paths.
- if (node->op() == Token::AND) {
- JumpTarget is_true;
- ControlDestination dest(&is_true, destination()->false_target(), true);
- LoadCondition(node->left(), &dest, false);
-
- if (dest.false_was_fall_through()) {
- // The current false target was used as the fall-through. If
- // there are no dangling jumps to is_true then the left
- // subexpression was unconditionally false. Otherwise we have
- // paths where we do have to evaluate the right subexpression.
- if (is_true.is_linked()) {
- // We need to compile the right subexpression. If the jump to
- // the current false target was a forward jump then we have a
- // valid frame, we have just bound the false target, and we
- // have to jump around the code for the right subexpression.
- if (has_valid_frame()) {
- destination()->false_target()->Unuse();
- destination()->false_target()->Jump();
- }
- is_true.Bind();
- // The left subexpression compiled to control flow, so the
- // right one is free to do so as well.
- LoadCondition(node->right(), destination(), false);
- } else {
- // We have actually just jumped to or bound the current false
- // target but the current control destination is not marked as
- // used.
- destination()->Use(false);
- }
-
- } else if (dest.is_used()) {
- // The left subexpression compiled to control flow (and is_true
- // was just bound), so the right is free to do so as well.
- LoadCondition(node->right(), destination(), false);
-
- } else {
- // We have a materialized value on the frame, so we exit with
- // one on all paths. There are possibly also jumps to is_true
- // from nested subexpressions.
- JumpTarget pop_and_continue;
- JumpTarget exit;
-
- // Avoid popping the result if it converts to 'false' using the
- // standard ToBoolean() conversion as described in ECMA-262,
- // section 9.2, page 30.
- //
- // Duplicate the TOS value. The duplicate will be popped by
- // ToBoolean.
- frame_->Dup();
- ControlDestination dest(&pop_and_continue, &exit, true);
- ToBoolean(&dest);
-
- // Pop the result of evaluating the first part.
- frame_->Drop();
-
- // Compile right side expression.
- is_true.Bind();
- Load(node->right());
-
- // Exit (always with a materialized value).
- exit.Bind();
- }
-
- } else {
- ASSERT(node->op() == Token::OR);
- JumpTarget is_false;
- ControlDestination dest(destination()->true_target(), &is_false, false);
- LoadCondition(node->left(), &dest, false);
-
- if (dest.true_was_fall_through()) {
- // The current true target was used as the fall-through. If
- // there are no dangling jumps to is_false then the left
- // subexpression was unconditionally true. Otherwise we have
- // paths where we do have to evaluate the right subexpression.
- if (is_false.is_linked()) {
- // We need to compile the right subexpression. If the jump to
- // the current true target was a forward jump then we have a
- // valid frame, we have just bound the true target, and we
- // have to jump around the code for the right subexpression.
- if (has_valid_frame()) {
- destination()->true_target()->Unuse();
- destination()->true_target()->Jump();
- }
- is_false.Bind();
- // The left subexpression compiled to control flow, so the
- // right one is free to do so as well.
- LoadCondition(node->right(), destination(), false);
- } else {
- // We have just jumped to or bound the current true target but
- // the current control destination is not marked as used.
- destination()->Use(true);
- }
-
- } else if (dest.is_used()) {
- // The left subexpression compiled to control flow (and is_false
- // was just bound), so the right is free to do so as well.
- LoadCondition(node->right(), destination(), false);
-
- } else {
- // We have a materialized value on the frame, so we exit with
- // one on all paths. There are possibly also jumps to is_false
- // from nested subexpressions.
- JumpTarget pop_and_continue;
- JumpTarget exit;
-
- // Avoid popping the result if it converts to 'true' using the
- // standard ToBoolean() conversion as described in ECMA-262,
- // section 9.2, page 30.
- //
- // Duplicate the TOS value. The duplicate will be popped by
- // ToBoolean.
- frame_->Dup();
- ControlDestination dest(&exit, &pop_and_continue, false);
- ToBoolean(&dest);
-
- // Pop the result of evaluating the first part.
- frame_->Drop();
-
- // Compile right side expression.
- is_false.Bind();
- Load(node->right());
-
- // Exit (always with a materialized value).
- exit.Bind();
- }
- }
-}
-
-void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
- Comment cmnt(masm_, "[ BinaryOperation");
-
- if (node->op() == Token::AND || node->op() == Token::OR) {
- GenerateLogicalBooleanOperation(node);
- } else {
- // NOTE: The code below assumes that the slow cases (calls to runtime)
- // never return a constant/immutable object.
- OverwriteMode overwrite_mode = NO_OVERWRITE;
- if (node->left()->ResultOverwriteAllowed()) {
- overwrite_mode = OVERWRITE_LEFT;
- } else if (node->right()->ResultOverwriteAllowed()) {
- overwrite_mode = OVERWRITE_RIGHT;
- }
-
- if (node->left()->IsTrivial()) {
- Load(node->right());
- Result right = frame_->Pop();
- frame_->Push(node->left());
- frame_->Push(&right);
- } else {
- Load(node->left());
- Load(node->right());
- }
- GenericBinaryOperation(node, overwrite_mode);
- }
-}
-
-
-void CodeGenerator::VisitThisFunction(ThisFunction* node) {
- frame_->PushFunction();
-}
-
-
-void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
- Comment cmnt(masm_, "[ CompareOperation");
-
- // Get the expressions from the node.
- Expression* left = node->left();
- Expression* right = node->right();
- Token::Value op = node->op();
- // To make typeof testing for natives implemented in JavaScript really
- // efficient, we generate special code for expressions of the form:
- // 'typeof <expression> == <string>'.
- UnaryOperation* operation = left->AsUnaryOperation();
- if ((op == Token::EQ || op == Token::EQ_STRICT) &&
- (operation != NULL && operation->op() == Token::TYPEOF) &&
- (right->AsLiteral() != NULL &&
- right->AsLiteral()->handle()->IsString())) {
- Handle<String> check(Handle<String>::cast(right->AsLiteral()->handle()));
-
- // Load the operand and move it to a register.
- LoadTypeofExpression(operation->expression());
- Result answer = frame_->Pop();
- answer.ToRegister();
-
- if (check->Equals(HEAP->number_symbol())) {
- Condition is_smi = masm_->CheckSmi(answer.reg());
- destination()->true_target()->Branch(is_smi);
- frame_->Spill(answer.reg());
- __ movq(answer.reg(), FieldOperand(answer.reg(), HeapObject::kMapOffset));
- __ CompareRoot(answer.reg(), Heap::kHeapNumberMapRootIndex);
- answer.Unuse();
- destination()->Split(equal);
-
- } else if (check->Equals(HEAP->string_symbol())) {
- Condition is_smi = masm_->CheckSmi(answer.reg());
- destination()->false_target()->Branch(is_smi);
-
- // It can be an undetectable string object.
- __ movq(kScratchRegister,
- FieldOperand(answer.reg(), HeapObject::kMapOffset));
- __ testb(FieldOperand(kScratchRegister, Map::kBitFieldOffset),
- Immediate(1 << Map::kIsUndetectable));
- destination()->false_target()->Branch(not_zero);
- __ CmpInstanceType(kScratchRegister, FIRST_NONSTRING_TYPE);
- answer.Unuse();
- destination()->Split(below); // Unsigned byte comparison needed.
-
- } else if (check->Equals(HEAP->boolean_symbol())) {
- __ CompareRoot(answer.reg(), Heap::kTrueValueRootIndex);
- destination()->true_target()->Branch(equal);
- __ CompareRoot(answer.reg(), Heap::kFalseValueRootIndex);
- answer.Unuse();
- destination()->Split(equal);
-
- } else if (check->Equals(HEAP->undefined_symbol())) {
- __ CompareRoot(answer.reg(), Heap::kUndefinedValueRootIndex);
- destination()->true_target()->Branch(equal);
-
- Condition is_smi = masm_->CheckSmi(answer.reg());
- destination()->false_target()->Branch(is_smi);
-
- // It can be an undetectable object.
- __ movq(kScratchRegister,
- FieldOperand(answer.reg(), HeapObject::kMapOffset));
- __ testb(FieldOperand(kScratchRegister, Map::kBitFieldOffset),
- Immediate(1 << Map::kIsUndetectable));
- answer.Unuse();
- destination()->Split(not_zero);
-
- } else if (check->Equals(HEAP->function_symbol())) {
- Condition is_smi = masm_->CheckSmi(answer.reg());
- destination()->false_target()->Branch(is_smi);
- frame_->Spill(answer.reg());
- __ CmpObjectType(answer.reg(), JS_FUNCTION_TYPE, answer.reg());
- destination()->true_target()->Branch(equal);
- // Regular expressions are callable so typeof == 'function'.
- __ CmpInstanceType(answer.reg(), JS_REGEXP_TYPE);
- answer.Unuse();
- destination()->Split(equal);
-
- } else if (check->Equals(HEAP->object_symbol())) {
- Condition is_smi = masm_->CheckSmi(answer.reg());
- destination()->false_target()->Branch(is_smi);
- __ CompareRoot(answer.reg(), Heap::kNullValueRootIndex);
- destination()->true_target()->Branch(equal);
-
- // Regular expressions are typeof == 'function', not 'object'.
- __ CmpObjectType(answer.reg(), JS_REGEXP_TYPE, kScratchRegister);
- destination()->false_target()->Branch(equal);
-
- // It can be an undetectable object.
- __ testb(FieldOperand(kScratchRegister, Map::kBitFieldOffset),
- Immediate(1 << Map::kIsUndetectable));
- destination()->false_target()->Branch(not_zero);
- __ CmpInstanceType(kScratchRegister, FIRST_JS_OBJECT_TYPE);
- destination()->false_target()->Branch(below);
- __ CmpInstanceType(kScratchRegister, LAST_JS_OBJECT_TYPE);
- answer.Unuse();
- destination()->Split(below_equal);
- } else {
- // Uncommon case: typeof testing against a string literal that is
- // never returned from the typeof operator.
- answer.Unuse();
- destination()->Goto(false);
- }
- return;
- }
-
- Condition cc = no_condition;
- bool strict = false;
- switch (op) {
- case Token::EQ_STRICT:
- strict = true;
- // Fall through
- case Token::EQ:
- cc = equal;
- break;
- case Token::LT:
- cc = less;
- break;
- case Token::GT:
- cc = greater;
- break;
- case Token::LTE:
- cc = less_equal;
- break;
- case Token::GTE:
- cc = greater_equal;
- break;
- case Token::IN: {
- Load(left);
- Load(right);
- Result answer = frame_->InvokeBuiltin(Builtins::IN, CALL_FUNCTION, 2);
- frame_->Push(&answer); // push the result
- return;
- }
- case Token::INSTANCEOF: {
- Load(left);
- Load(right);
- InstanceofStub stub(InstanceofStub::kNoFlags);
- Result answer = frame_->CallStub(&stub, 2);
- answer.ToRegister();
- __ testq(answer.reg(), answer.reg());
- answer.Unuse();
- destination()->Split(zero);
- return;
- }
- default:
- UNREACHABLE();
- }
-
- if (left->IsTrivial()) {
- Load(right);
- Result right_result = frame_->Pop();
- frame_->Push(left);
- frame_->Push(&right_result);
- } else {
- Load(left);
- Load(right);
- }
-
- Comparison(node, cc, strict, destination());
-}
-
-
-void CodeGenerator::VisitCompareToNull(CompareToNull* node) {
- Comment cmnt(masm_, "[ CompareToNull");
-
- Load(node->expression());
- Result operand = frame_->Pop();
- operand.ToRegister();
- __ CompareRoot(operand.reg(), Heap::kNullValueRootIndex);
- if (node->is_strict()) {
- operand.Unuse();
- destination()->Split(equal);
- } else {
- // The 'null' value is only equal to 'undefined' if using non-strict
- // comparisons.
- destination()->true_target()->Branch(equal);
- __ CompareRoot(operand.reg(), Heap::kUndefinedValueRootIndex);
- destination()->true_target()->Branch(equal);
- Condition is_smi = masm_->CheckSmi(operand.reg());
- destination()->false_target()->Branch(is_smi);
-
- // It can be an undetectable object.
- // Use a scratch register in preference to spilling operand.reg().
- Result temp = allocator()->Allocate();
- ASSERT(temp.is_valid());
- __ movq(temp.reg(),
- FieldOperand(operand.reg(), HeapObject::kMapOffset));
- __ testb(FieldOperand(temp.reg(), Map::kBitFieldOffset),
- Immediate(1 << Map::kIsUndetectable));
- temp.Unuse();
- operand.Unuse();
- destination()->Split(not_zero);
- }
-}
-
-
-#ifdef DEBUG
-bool CodeGenerator::HasValidEntryRegisters() {
- return (allocator()->count(rax) == (frame()->is_used(rax) ? 1 : 0))
- && (allocator()->count(rbx) == (frame()->is_used(rbx) ? 1 : 0))
- && (allocator()->count(rcx) == (frame()->is_used(rcx) ? 1 : 0))
- && (allocator()->count(rdx) == (frame()->is_used(rdx) ? 1 : 0))
- && (allocator()->count(rdi) == (frame()->is_used(rdi) ? 1 : 0))
- && (allocator()->count(r8) == (frame()->is_used(r8) ? 1 : 0))
- && (allocator()->count(r9) == (frame()->is_used(r9) ? 1 : 0))
- && (allocator()->count(r11) == (frame()->is_used(r11) ? 1 : 0))
- && (allocator()->count(r14) == (frame()->is_used(r14) ? 1 : 0))
- && (allocator()->count(r15) == (frame()->is_used(r15) ? 1 : 0));
-}
-#endif
-
-
-
-// Emit a LoadIC call to get the value from receiver and leave it in
-// dst. The receiver register is restored after the call.
-class DeferredReferenceGetNamedValue: public DeferredCode {
- public:
- DeferredReferenceGetNamedValue(Register dst,
- Register receiver,
- Handle<String> name)
- : dst_(dst), receiver_(receiver), name_(name) {
- set_comment("[ DeferredReferenceGetNamedValue");
- }
-
- virtual void Generate();
-
- Label* patch_site() { return &patch_site_; }
-
- private:
- Label patch_site_;
- Register dst_;
- Register receiver_;
- Handle<String> name_;
-};
-
-
-void DeferredReferenceGetNamedValue::Generate() {
- if (!receiver_.is(rax)) {
- __ movq(rax, receiver_);
- }
- __ Move(rcx, name_);
- Handle<Code> ic = Isolate::Current()->builtins()->LoadIC_Initialize();
- __ Call(ic, RelocInfo::CODE_TARGET);
- // The call must be followed by a test rax instruction to indicate
- // that the inobject property case was inlined.
- //
- // Store the delta to the map check instruction here in the test
- // instruction. Use masm_-> instead of the __ macro since the
- // latter can't return a value.
- int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site());
- // Here we use masm_-> instead of the __ macro because this is the
- // instruction that gets patched and coverage code gets in the way.
- masm_->testl(rax, Immediate(-delta_to_patch_site));
- Counters* counters = masm()->isolate()->counters();
- __ IncrementCounter(counters->named_load_inline_miss(), 1);
-
- if (!dst_.is(rax)) __ movq(dst_, rax);
-}
-
-
-class DeferredReferenceGetKeyedValue: public DeferredCode {
- public:
- explicit DeferredReferenceGetKeyedValue(Register dst,
- Register receiver,
- Register key)
- : dst_(dst), receiver_(receiver), key_(key) {
- set_comment("[ DeferredReferenceGetKeyedValue");
- }
-
- virtual void Generate();
-
- Label* patch_site() { return &patch_site_; }
-
- private:
- Label patch_site_;
- Register dst_;
- Register receiver_;
- Register key_;
-};
-
-
-void DeferredReferenceGetKeyedValue::Generate() {
- if (receiver_.is(rdx)) {
- if (!key_.is(rax)) {
- __ movq(rax, key_);
- } // else do nothing.
- } else if (receiver_.is(rax)) {
- if (key_.is(rdx)) {
- __ xchg(rax, rdx);
- } else if (key_.is(rax)) {
- __ movq(rdx, receiver_);
- } else {
- __ movq(rdx, receiver_);
- __ movq(rax, key_);
- }
- } else if (key_.is(rax)) {
- __ movq(rdx, receiver_);
- } else {
- __ movq(rax, key_);
- __ movq(rdx, receiver_);
- }
- // Calculate the delta from the IC call instruction to the map check
- // movq instruction in the inlined version. This delta is stored in
- // a test(rax, delta) instruction after the call so that we can find
- // it in the IC initialization code and patch the movq instruction.
- // This means that we cannot allow test instructions after calls to
- // KeyedLoadIC stubs in other places.
- Handle<Code> ic = Isolate::Current()->builtins()->KeyedLoadIC_Initialize();
- __ Call(ic, RelocInfo::CODE_TARGET);
- // The delta from the start of the map-compare instruction to the
- // test instruction. We use masm_-> directly here instead of the __
- // macro because the macro sometimes uses macro expansion to turn
- // into something that can't return a value. This is encountered
- // when doing generated code coverage tests.
- int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site());
- // Here we use masm_-> instead of the __ macro because this is the
- // instruction that gets patched and coverage code gets in the way.
- // TODO(X64): Consider whether it's worth switching the test to a
- // 7-byte NOP with non-zero immediate (0f 1f 80 xxxxxxxx) which won't
- // be generated normally.
- masm_->testl(rax, Immediate(-delta_to_patch_site));
- Counters* counters = masm()->isolate()->counters();
- __ IncrementCounter(counters->keyed_load_inline_miss(), 1);
-
- if (!dst_.is(rax)) __ movq(dst_, rax);
-}
-
-
-class DeferredReferenceSetKeyedValue: public DeferredCode {
- public:
- DeferredReferenceSetKeyedValue(Register value,
- Register key,
- Register receiver,
- StrictModeFlag strict_mode)
- : value_(value),
- key_(key),
- receiver_(receiver),
- strict_mode_(strict_mode) {
- set_comment("[ DeferredReferenceSetKeyedValue");
- }
-
- virtual void Generate();
-
- Label* patch_site() { return &patch_site_; }
-
- private:
- Register value_;
- Register key_;
- Register receiver_;
- Label patch_site_;
- StrictModeFlag strict_mode_;
-};
-
-
-void DeferredReferenceSetKeyedValue::Generate() {
- Counters* counters = masm()->isolate()->counters();
- __ IncrementCounter(counters->keyed_store_inline_miss(), 1);
- // Move value, receiver, and key to registers rax, rdx, and rcx, as
- // the IC stub expects.
- // Move value to rax, using xchg if the receiver or key is in rax.
- if (!value_.is(rax)) {
- if (!receiver_.is(rax) && !key_.is(rax)) {
- __ movq(rax, value_);
- } else {
- __ xchg(rax, value_);
- // Update receiver_ and key_ if they are affected by the swap.
- if (receiver_.is(rax)) {
- receiver_ = value_;
- } else if (receiver_.is(value_)) {
- receiver_ = rax;
- }
- if (key_.is(rax)) {
- key_ = value_;
- } else if (key_.is(value_)) {
- key_ = rax;
- }
- }
- }
- // Value is now in rax. Its original location is remembered in value_,
- // and the value is restored to value_ before returning.
- // The variables receiver_ and key_ are not preserved.
- // Move receiver and key to rdx and rcx, swapping if necessary.
- if (receiver_.is(rdx)) {
- if (!key_.is(rcx)) {
- __ movq(rcx, key_);
- } // Else everything is already in the right place.
- } else if (receiver_.is(rcx)) {
- if (key_.is(rdx)) {
- __ xchg(rcx, rdx);
- } else if (key_.is(rcx)) {
- __ movq(rdx, receiver_);
- } else {
- __ movq(rdx, receiver_);
- __ movq(rcx, key_);
- }
- } else if (key_.is(rcx)) {
- __ movq(rdx, receiver_);
- } else {
- __ movq(rcx, key_);
- __ movq(rdx, receiver_);
- }
-
- // Call the IC stub.
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- (strict_mode_ == kStrictMode) ? Builtins::kKeyedStoreIC_Initialize_Strict
- : Builtins::kKeyedStoreIC_Initialize));
- __ Call(ic, RelocInfo::CODE_TARGET);
- // The delta from the start of the map-compare instructions (initial movq)
- // to the test instruction. We use masm_-> directly here instead of the
- // __ macro because the macro sometimes uses macro expansion to turn
- // into something that can't return a value. This is encountered
- // when doing generated code coverage tests.
- int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site());
- // Here we use masm_-> instead of the __ macro because this is the
- // instruction that gets patched and coverage code gets in the way.
- masm_->testl(rax, Immediate(-delta_to_patch_site));
- // Restore value (returned from store IC).
- if (!value_.is(rax)) __ movq(value_, rax);
-}
-
-
-Result CodeGenerator::EmitNamedLoad(Handle<String> name, bool is_contextual) {
-#ifdef DEBUG
- int original_height = frame()->height();
-#endif
- Result result;
- // Do not inline the inobject property case for loads from the global
- // object. Also do not inline for unoptimized code. This saves time
- // in the code generator. Unoptimized code is toplevel code or code
- // that is not in a loop.
- if (is_contextual || scope()->is_global_scope() || loop_nesting() == 0) {
- Comment cmnt(masm(), "[ Load from named Property");
- frame()->Push(name);
-
- RelocInfo::Mode mode = is_contextual
- ? RelocInfo::CODE_TARGET_CONTEXT
- : RelocInfo::CODE_TARGET;
- result = frame()->CallLoadIC(mode);
- // A test rax instruction following the call signals that the
- // inobject property case was inlined. Ensure that there is not
- // a test rax instruction here.
- __ nop();
- } else {
- // Inline the inobject property case.
- Comment cmnt(masm(), "[ Inlined named property load");
- Result receiver = frame()->Pop();
- receiver.ToRegister();
- result = allocator()->Allocate();
- ASSERT(result.is_valid());
-
- // r12 is now a reserved register, so it cannot be the receiver.
- // If it was, the distance to the fixup location would not be constant.
- ASSERT(!receiver.reg().is(r12));
-
- DeferredReferenceGetNamedValue* deferred =
- new DeferredReferenceGetNamedValue(result.reg(), receiver.reg(), name);
-
- // Check that the receiver is a heap object.
- __ JumpIfSmi(receiver.reg(), deferred->entry_label());
-
- __ bind(deferred->patch_site());
- // This is the map check instruction that will be patched (so we can't
- // use the double underscore macro that may insert instructions).
- // Initially use an invalid map to force a failure.
- masm()->movq(kScratchRegister, FACTORY->null_value(),
- RelocInfo::EMBEDDED_OBJECT);
- masm()->cmpq(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
- kScratchRegister);
- // This branch is always a forwards branch so it's always a fixed
- // size which allows the assert below to succeed and patching to work.
- // Don't use deferred->Branch(...), since that might add coverage code.
- masm()->j(not_equal, deferred->entry_label());
-
- // The delta from the patch label to the load offset must be
- // statically known.
- ASSERT(masm()->SizeOfCodeGeneratedSince(deferred->patch_site()) ==
- LoadIC::kOffsetToLoadInstruction);
- // The initial (invalid) offset has to be large enough to force
- // a 32-bit instruction encoding to allow patching with an
- // arbitrary offset. Use kMaxInt (minus kHeapObjectTag).
- int offset = kMaxInt;
- masm()->movq(result.reg(), FieldOperand(receiver.reg(), offset));
-
- Counters* counters = masm()->isolate()->counters();
- __ IncrementCounter(counters->named_load_inline(), 1);
- deferred->BindExit();
- }
- ASSERT(frame()->height() == original_height - 1);
- return result;
-}
-
-
-Result CodeGenerator::EmitNamedStore(Handle<String> name, bool is_contextual) {
-#ifdef DEBUG
- int expected_height = frame()->height() - (is_contextual ? 1 : 2);
-#endif
-
- Result result;
- if (is_contextual || scope()->is_global_scope() || loop_nesting() == 0) {
- result = frame()->CallStoreIC(name, is_contextual, strict_mode_flag());
- // A test rax instruction following the call signals that the inobject
- // property case was inlined. Ensure that there is not a test rax
- // instruction here.
- __ nop();
- } else {
- // Inline the in-object property case.
- JumpTarget slow, done;
- Label patch_site;
-
- // Get the value and receiver from the stack.
- Result value = frame()->Pop();
- value.ToRegister();
- Result receiver = frame()->Pop();
- receiver.ToRegister();
-
- // Allocate result register.
- result = allocator()->Allocate();
- ASSERT(result.is_valid() && receiver.is_valid() && value.is_valid());
-
- // Cannot use r12 for receiver, because that changes
- // the distance between a call and a fixup location,
- // due to a special encoding of r12 as r/m in a ModR/M byte.
- if (receiver.reg().is(r12)) {
- frame()->Spill(receiver.reg()); // It will be overwritten with result.
- // Swap receiver and value.
- __ movq(result.reg(), receiver.reg());
- Result temp = receiver;
- receiver = result;
- result = temp;
- }
-
- // Check that the receiver is a heap object.
- Condition is_smi = masm()->CheckSmi(receiver.reg());
- slow.Branch(is_smi, &value, &receiver);
-
- // This is the map check instruction that will be patched.
- // Initially use an invalid map to force a failure. The exact
- // instruction sequence is important because we use the
- // kOffsetToStoreInstruction constant for patching. We avoid using
- // the __ macro for the following two instructions because it
- // might introduce extra instructions.
- __ bind(&patch_site);
- masm()->movq(kScratchRegister, FACTORY->null_value(),
- RelocInfo::EMBEDDED_OBJECT);
- masm()->cmpq(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
- kScratchRegister);
- // This branch is always a forwards branch so it's always a fixed size
- // which allows the assert below to succeed and patching to work.
- slow.Branch(not_equal, &value, &receiver);
-
- // The delta from the patch label to the store offset must be
- // statically known.
- ASSERT(masm()->SizeOfCodeGeneratedSince(&patch_site) ==
- StoreIC::kOffsetToStoreInstruction);
-
- // The initial (invalid) offset has to be large enough to force a 32-bit
- // instruction encoding to allow patching with an arbitrary offset. Use
- // kMaxInt (minus kHeapObjectTag).
- int offset = kMaxInt;
- __ movq(FieldOperand(receiver.reg(), offset), value.reg());
- __ movq(result.reg(), value.reg());
-
- // Allocate scratch register for write barrier.
- Result scratch = allocator()->Allocate();
- ASSERT(scratch.is_valid());
-
- // The write barrier clobbers all input registers, so spill the
- // receiver and the value.
- frame_->Spill(receiver.reg());
- frame_->Spill(value.reg());
-
- // If the receiver and the value share a register allocate a new
- // register for the receiver.
- if (receiver.reg().is(value.reg())) {
- receiver = allocator()->Allocate();
- ASSERT(receiver.is_valid());
- __ movq(receiver.reg(), value.reg());
- }
-
- // Update the write barrier. To save instructions in the inlined
- // version we do not filter smis.
- Label skip_write_barrier;
- __ InNewSpace(receiver.reg(), value.reg(), equal, &skip_write_barrier);
- int delta_to_record_write = masm_->SizeOfCodeGeneratedSince(&patch_site);
- __ lea(scratch.reg(), Operand(receiver.reg(), offset));
- __ RecordWriteHelper(receiver.reg(), scratch.reg(), value.reg());
- if (FLAG_debug_code) {
- __ movq(receiver.reg(), BitCast<int64_t>(kZapValue), RelocInfo::NONE);
- __ movq(value.reg(), BitCast<int64_t>(kZapValue), RelocInfo::NONE);
- __ movq(scratch.reg(), BitCast<int64_t>(kZapValue), RelocInfo::NONE);
- }
- __ bind(&skip_write_barrier);
- value.Unuse();
- scratch.Unuse();
- receiver.Unuse();
- done.Jump(&result);
-
- slow.Bind(&value, &receiver);
- frame()->Push(&receiver);
- frame()->Push(&value);
- result = frame()->CallStoreIC(name, is_contextual, strict_mode_flag());
- // Encode the offset to the map check instruction and the offset
- // to the write barrier store address computation in a test rax
- // instruction.
- int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(&patch_site);
- __ testl(rax,
- Immediate((delta_to_record_write << 16) | delta_to_patch_site));
- done.Bind(&result);
- }
-
- ASSERT_EQ(expected_height, frame()->height());
- return result;
-}
-
-
-Result CodeGenerator::EmitKeyedLoad() {
-#ifdef DEBUG
- int original_height = frame()->height();
-#endif
- Result result;
- // Inline array load code if inside of a loop. We do not know
- // the receiver map yet, so we initially generate the code with
- // a check against an invalid map. In the inline cache code, we
- // patch the map check if appropriate.
- if (loop_nesting() > 0) {
- Comment cmnt(masm_, "[ Inlined load from keyed Property");
-
- // Use a fresh temporary to load the elements without destroying
- // the receiver which is needed for the deferred slow case.
- // Allocate the temporary early so that we use rax if it is free.
- Result elements = allocator()->Allocate();
- ASSERT(elements.is_valid());
-
- Result key = frame_->Pop();
- Result receiver = frame_->Pop();
- key.ToRegister();
- receiver.ToRegister();
-
- // If key and receiver are shared registers on the frame, their values will
- // be automatically saved and restored when going to deferred code.
- // The result is returned in elements, which is not shared.
- DeferredReferenceGetKeyedValue* deferred =
- new DeferredReferenceGetKeyedValue(elements.reg(),
- receiver.reg(),
- key.reg());
-
- __ JumpIfSmi(receiver.reg(), deferred->entry_label());
-
- // Check that the receiver has the expected map.
- // Initially, use an invalid map. The map is patched in the IC
- // initialization code.
- __ bind(deferred->patch_site());
- // Use masm-> here instead of the double underscore macro since extra
- // coverage code can interfere with the patching. Do not use a load
- // from the root array to load null_value, since the load must be patched
- // with the expected receiver map, which is not in the root array.
- masm_->movq(kScratchRegister, FACTORY->null_value(),
- RelocInfo::EMBEDDED_OBJECT);
- masm_->cmpq(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
- kScratchRegister);
- deferred->Branch(not_equal);
-
- __ JumpUnlessNonNegativeSmi(key.reg(), deferred->entry_label());
-
- // Get the elements array from the receiver.
- __ movq(elements.reg(),
- FieldOperand(receiver.reg(), JSObject::kElementsOffset));
- __ AssertFastElements(elements.reg());
-
- // Check that key is within bounds.
- __ SmiCompare(key.reg(),
- FieldOperand(elements.reg(), FixedArray::kLengthOffset));
- deferred->Branch(above_equal);
-
- // Load and check that the result is not the hole. We could
- // reuse the index or elements register for the value.
- //
- // TODO(206): Consider whether it makes sense to try some
- // heuristic about which register to reuse. For example, if
- // one is rax, the we can reuse that one because the value
- // coming from the deferred code will be in rax.
- SmiIndex index =
- masm_->SmiToIndex(kScratchRegister, key.reg(), kPointerSizeLog2);
- __ movq(elements.reg(),
- FieldOperand(elements.reg(),
- index.reg,
- index.scale,
- FixedArray::kHeaderSize));
- result = elements;
- __ CompareRoot(result.reg(), Heap::kTheHoleValueRootIndex);
- deferred->Branch(equal);
- Counters* counters = masm()->isolate()->counters();
- __ IncrementCounter(counters->keyed_load_inline(), 1);
-
- deferred->BindExit();
- } else {
- Comment cmnt(masm_, "[ Load from keyed Property");
- result = frame_->CallKeyedLoadIC(RelocInfo::CODE_TARGET);
- // Make sure that we do not have a test instruction after the
- // call. A test instruction after the call is used to
- // indicate that we have generated an inline version of the
- // keyed load. The explicit nop instruction is here because
- // the push that follows might be peep-hole optimized away.
- __ nop();
- }
- ASSERT(frame()->height() == original_height - 2);
- return result;
-}
-
-
-Result CodeGenerator::EmitKeyedStore(StaticType* key_type) {
-#ifdef DEBUG
- int original_height = frame()->height();
-#endif
- Result result;
- // Generate inlined version of the keyed store if the code is in a loop
- // and the key is likely to be a smi.
- if (loop_nesting() > 0 && key_type->IsLikelySmi()) {
- Comment cmnt(masm(), "[ Inlined store to keyed Property");
-
- // Get the receiver, key and value into registers.
- result = frame()->Pop();
- Result key = frame()->Pop();
- Result receiver = frame()->Pop();
-
- Result tmp = allocator_->Allocate();
- ASSERT(tmp.is_valid());
- Result tmp2 = allocator_->Allocate();
- ASSERT(tmp2.is_valid());
-
- // Determine whether the value is a constant before putting it in a
- // register.
- bool value_is_constant = result.is_constant();
-
- // Make sure that value, key and receiver are in registers.
- result.ToRegister();
- key.ToRegister();
- receiver.ToRegister();
-
- DeferredReferenceSetKeyedValue* deferred =
- new DeferredReferenceSetKeyedValue(result.reg(),
- key.reg(),
- receiver.reg(),
- strict_mode_flag());
-
- // Check that the receiver is not a smi.
- __ JumpIfSmi(receiver.reg(), deferred->entry_label());
-
- // Check that the key is a smi.
- if (!key.is_smi()) {
- __ JumpIfNotSmi(key.reg(), deferred->entry_label());
- } else if (FLAG_debug_code) {
- __ AbortIfNotSmi(key.reg());
- }
-
- // Check that the receiver is a JSArray.
- __ CmpObjectType(receiver.reg(), JS_ARRAY_TYPE, kScratchRegister);
- deferred->Branch(not_equal);
-
- // Get the elements array from the receiver and check that it is not a
- // dictionary.
- __ movq(tmp.reg(),
- FieldOperand(receiver.reg(), JSArray::kElementsOffset));
-
- // Check whether it is possible to omit the write barrier. If the elements
- // array is in new space or the value written is a smi we can safely update
- // the elements array without write barrier.
- Label in_new_space;
- __ InNewSpace(tmp.reg(), tmp2.reg(), equal, &in_new_space);
- if (!value_is_constant) {
- __ JumpIfNotSmi(result.reg(), deferred->entry_label());
- }
-
- __ bind(&in_new_space);
- // Bind the deferred code patch site to be able to locate the fixed
- // array map comparison. When debugging, we patch this comparison to
- // always fail so that we will hit the IC call in the deferred code
- // which will allow the debugger to break for fast case stores.
- __ bind(deferred->patch_site());
- // Avoid using __ to ensure the distance from patch_site
- // to the map address is always the same.
- masm()->movq(kScratchRegister, FACTORY->fixed_array_map(),
- RelocInfo::EMBEDDED_OBJECT);
- __ cmpq(FieldOperand(tmp.reg(), HeapObject::kMapOffset),
- kScratchRegister);
- deferred->Branch(not_equal);
-
- // Check that the key is within bounds. Both the key and the length of
- // the JSArray are smis (because the fixed array check above ensures the
- // elements are in fast case). Use unsigned comparison to handle negative
- // keys.
- __ SmiCompare(FieldOperand(receiver.reg(), JSArray::kLengthOffset),
- key.reg());
- deferred->Branch(below_equal);
-
- // Store the value.
- SmiIndex index =
- masm()->SmiToIndex(kScratchRegister, key.reg(), kPointerSizeLog2);
- __ movq(FieldOperand(tmp.reg(),
- index.reg,
- index.scale,
- FixedArray::kHeaderSize),
- result.reg());
- Counters* counters = masm()->isolate()->counters();
- __ IncrementCounter(counters->keyed_store_inline(), 1);
-
- deferred->BindExit();
- } else {
- result = frame()->CallKeyedStoreIC(strict_mode_flag());
- // Make sure that we do not have a test instruction after the
- // call. A test instruction after the call is used to
- // indicate that we have generated an inline version of the
- // keyed store.
- __ nop();
- }
- ASSERT(frame()->height() == original_height - 3);
- return result;
-}
-
-
-#undef __
-#define __ ACCESS_MASM(masm)
-
-
-Handle<String> Reference::GetName() {
- ASSERT(type_ == NAMED);
- Property* property = expression_->AsProperty();
- if (property == NULL) {
- // Global variable reference treated as a named property reference.
- VariableProxy* proxy = expression_->AsVariableProxy();
- ASSERT(proxy->AsVariable() != NULL);
- ASSERT(proxy->AsVariable()->is_global());
- return proxy->name();
- } else {
- Literal* raw_name = property->key()->AsLiteral();
- ASSERT(raw_name != NULL);
- return Handle<String>(String::cast(*raw_name->handle()));
- }
-}
-
-
-void Reference::GetValue() {
- ASSERT(!cgen_->in_spilled_code());
- ASSERT(cgen_->HasValidEntryRegisters());
- ASSERT(!is_illegal());
- MacroAssembler* masm = cgen_->masm();
-
- // Record the source position for the property load.
- Property* property = expression_->AsProperty();
- if (property != NULL) {
- cgen_->CodeForSourcePosition(property->position());
- }
-
- switch (type_) {
- case SLOT: {
- Comment cmnt(masm, "[ Load from Slot");
- Slot* slot = expression_->AsVariableProxy()->AsVariable()->AsSlot();
- ASSERT(slot != NULL);
- cgen_->LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
- break;
- }
-
- case NAMED: {
- Variable* var = expression_->AsVariableProxy()->AsVariable();
- bool is_global = var != NULL;
- ASSERT(!is_global || var->is_global());
- if (persist_after_get_) {
- cgen_->frame()->Dup();
- }
- Result result = cgen_->EmitNamedLoad(GetName(), is_global);
- cgen_->frame()->Push(&result);
- break;
- }
-
- case KEYED: {
- // A load of a bare identifier (load from global) cannot be keyed.
- ASSERT(expression_->AsVariableProxy()->AsVariable() == NULL);
- if (persist_after_get_) {
- cgen_->frame()->PushElementAt(1);
- cgen_->frame()->PushElementAt(1);
- }
- Result value = cgen_->EmitKeyedLoad();
- cgen_->frame()->Push(&value);
- break;
- }
-
- default:
- UNREACHABLE();
- }
-
- if (!persist_after_get_) {
- set_unloaded();
- }
-}
-
-
-void Reference::TakeValue() {
- // TODO(X64): This function is completely architecture independent. Move
- // it somewhere shared.
-
- // For non-constant frame-allocated slots, we invalidate the value in the
- // slot. For all others, we fall back on GetValue.
- ASSERT(!cgen_->in_spilled_code());
- ASSERT(!is_illegal());
- if (type_ != SLOT) {
- GetValue();
- return;
- }
-
- Slot* slot = expression_->AsVariableProxy()->AsVariable()->AsSlot();
- ASSERT(slot != NULL);
- if (slot->type() == Slot::LOOKUP ||
- slot->type() == Slot::CONTEXT ||
- slot->var()->mode() == Variable::CONST ||
- slot->is_arguments()) {
- GetValue();
- return;
- }
-
- // Only non-constant, frame-allocated parameters and locals can reach
- // here. Be careful not to use the optimizations for arguments
- // object access since it may not have been initialized yet.
- ASSERT(!slot->is_arguments());
- if (slot->type() == Slot::PARAMETER) {
- cgen_->frame()->TakeParameterAt(slot->index());
- } else {
- ASSERT(slot->type() == Slot::LOCAL);
- cgen_->frame()->TakeLocalAt(slot->index());
- }
-
- ASSERT(persist_after_get_);
- // Do not unload the reference, because it is used in SetValue.
-}
-
-
-void Reference::SetValue(InitState init_state) {
- ASSERT(cgen_->HasValidEntryRegisters());
- ASSERT(!is_illegal());
- MacroAssembler* masm = cgen_->masm();
- switch (type_) {
- case SLOT: {
- Comment cmnt(masm, "[ Store to Slot");
- Slot* slot = expression_->AsVariableProxy()->AsVariable()->AsSlot();
- ASSERT(slot != NULL);
- cgen_->StoreToSlot(slot, init_state);
- set_unloaded();
- break;
- }
-
- case NAMED: {
- Comment cmnt(masm, "[ Store to named Property");
- Result answer = cgen_->EmitNamedStore(GetName(), false);
- cgen_->frame()->Push(&answer);
- set_unloaded();
- break;
- }
-
- case KEYED: {
- Comment cmnt(masm, "[ Store to keyed Property");
- Property* property = expression()->AsProperty();
- ASSERT(property != NULL);
-
- Result answer = cgen_->EmitKeyedStore(property->key()->type());
- cgen_->frame()->Push(&answer);
- set_unloaded();
- break;
- }
-
- case UNLOADED:
- case ILLEGAL:
- UNREACHABLE();
- }
-}
-
-
-Result CodeGenerator::GenerateGenericBinaryOpStubCall(GenericBinaryOpStub* stub,
- Result* left,
- Result* right) {
- if (stub->ArgsInRegistersSupported()) {
- stub->SetArgsInRegisters();
- return frame_->CallStub(stub, left, right);
- } else {
- frame_->Push(left);
- frame_->Push(right);
- return frame_->CallStub(stub, 2);
- }
-}
-
-#undef __
-
#define __ masm.
#ifdef _WIN64
@@ -8758,7 +58,7 @@
&actual_size,
true));
CHECK(buffer);
- Assembler masm(buffer, static_cast<int>(actual_size));
+ Assembler masm(NULL, buffer, static_cast<int>(actual_size));
// Generated code is put into a fixed, unmovable, buffer, and not into
// the V8 heap. We can't, and don't, refer to any relocatable addresses
// (e.g. the JavaScript nan-object).
@@ -8832,7 +132,7 @@
CodeDesc desc;
masm.GetCode(&desc);
- // Call the function from C++.
+ // Call the function from C++ through this pointer.
return FUNCTION_CAST<ModuloFunction>(buffer);
}
diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h
index 9a70907..94c7850 100644
--- a/src/x64/codegen-x64.h
+++ b/src/x64/codegen-x64.h
@@ -30,270 +30,17 @@
#include "ast.h"
#include "ic-inl.h"
-#include "jump-target-heavy.h"
namespace v8 {
namespace internal {
// Forward declarations
class CompilationInfo;
-class DeferredCode;
-class RegisterAllocator;
-class RegisterFile;
-enum InitState { CONST_INIT, NOT_CONST_INIT };
enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
// -------------------------------------------------------------------------
-// Reference support
-
-// A reference is a C++ stack-allocated object that puts a
-// reference on the virtual frame. The reference may be consumed
-// by GetValue, TakeValue, SetValue, and Codegen::UnloadReference.
-// When the lifetime (scope) of a valid reference ends, it must have
-// been consumed, and be in state UNLOADED.
-class Reference BASE_EMBEDDED {
- public:
- // The values of the types is important, see size().
- enum Type { UNLOADED = -2, ILLEGAL = -1, SLOT = 0, NAMED = 1, KEYED = 2 };
-
- Reference(CodeGenerator* cgen,
- Expression* expression,
- bool persist_after_get = false);
- ~Reference();
-
- Expression* expression() const { return expression_; }
- Type type() const { return type_; }
- void set_type(Type value) {
- ASSERT_EQ(ILLEGAL, type_);
- type_ = value;
- }
-
- void set_unloaded() {
- ASSERT_NE(ILLEGAL, type_);
- ASSERT_NE(UNLOADED, type_);
- type_ = UNLOADED;
- }
- // The size the reference takes up on the stack.
- int size() const {
- return (type_ < SLOT) ? 0 : type_;
- }
-
- bool is_illegal() const { return type_ == ILLEGAL; }
- bool is_slot() const { return type_ == SLOT; }
- bool is_property() const { return type_ == NAMED || type_ == KEYED; }
- bool is_unloaded() const { return type_ == UNLOADED; }
-
- // Return the name. Only valid for named property references.
- Handle<String> GetName();
-
- // Generate code to push the value of the reference on top of the
- // expression stack. The reference is expected to be already on top of
- // the expression stack, and it is consumed by the call unless the
- // reference is for a compound assignment.
- // If the reference is not consumed, it is left in place under its value.
- void GetValue();
-
- // Like GetValue except that the slot is expected to be written to before
- // being read from again. The value of the reference may be invalidated,
- // causing subsequent attempts to read it to fail.
- void TakeValue();
-
- // Generate code to store the value on top of the expression stack in the
- // reference. The reference is expected to be immediately below the value
- // on the expression stack. The value is stored in the location specified
- // by the reference, and is left on top of the stack, after the reference
- // is popped from beneath it (unloaded).
- void SetValue(InitState init_state);
-
- private:
- CodeGenerator* cgen_;
- Expression* expression_;
- Type type_;
- bool persist_after_get_;
-};
-
-
-// -------------------------------------------------------------------------
-// Control destinations.
-
-// A control destination encapsulates a pair of jump targets and a
-// flag indicating which one is the preferred fall-through. The
-// preferred fall-through must be unbound, the other may be already
-// bound (ie, a backward target).
-//
-// The true and false targets may be jumped to unconditionally or
-// control may split conditionally. Unconditional jumping and
-// splitting should be emitted in tail position (as the last thing
-// when compiling an expression) because they can cause either label
-// to be bound or the non-fall through to be jumped to leaving an
-// invalid virtual frame.
-//
-// The labels in the control destination can be extracted and
-// manipulated normally without affecting the state of the
-// destination.
-
-class ControlDestination BASE_EMBEDDED {
- public:
- ControlDestination(JumpTarget* true_target,
- JumpTarget* false_target,
- bool true_is_fall_through)
- : true_target_(true_target),
- false_target_(false_target),
- true_is_fall_through_(true_is_fall_through),
- is_used_(false) {
- ASSERT(true_is_fall_through ? !true_target->is_bound()
- : !false_target->is_bound());
- }
-
- // Accessors for the jump targets. Directly jumping or branching to
- // or binding the targets will not update the destination's state.
- JumpTarget* true_target() const { return true_target_; }
- JumpTarget* false_target() const { return false_target_; }
-
- // True if the the destination has been jumped to unconditionally or
- // control has been split to both targets. This predicate does not
- // test whether the targets have been extracted and manipulated as
- // raw jump targets.
- bool is_used() const { return is_used_; }
-
- // True if the destination is used and the true target (respectively
- // false target) was the fall through. If the target is backward,
- // "fall through" included jumping unconditionally to it.
- bool true_was_fall_through() const {
- return is_used_ && true_is_fall_through_;
- }
-
- bool false_was_fall_through() const {
- return is_used_ && !true_is_fall_through_;
- }
-
- // Emit a branch to one of the true or false targets, and bind the
- // other target. Because this binds the fall-through target, it
- // should be emitted in tail position (as the last thing when
- // compiling an expression).
- void Split(Condition cc) {
- ASSERT(!is_used_);
- if (true_is_fall_through_) {
- false_target_->Branch(NegateCondition(cc));
- true_target_->Bind();
- } else {
- true_target_->Branch(cc);
- false_target_->Bind();
- }
- is_used_ = true;
- }
-
- // Emit an unconditional jump in tail position, to the true target
- // (if the argument is true) or the false target. The "jump" will
- // actually bind the jump target if it is forward, jump to it if it
- // is backward.
- void Goto(bool where) {
- ASSERT(!is_used_);
- JumpTarget* target = where ? true_target_ : false_target_;
- if (target->is_bound()) {
- target->Jump();
- } else {
- target->Bind();
- }
- is_used_ = true;
- true_is_fall_through_ = where;
- }
-
- // Mark this jump target as used as if Goto had been called, but
- // without generating a jump or binding a label (the control effect
- // should have already happened). This is used when the left
- // subexpression of the short-circuit boolean operators are
- // compiled.
- void Use(bool where) {
- ASSERT(!is_used_);
- ASSERT((where ? true_target_ : false_target_)->is_bound());
- is_used_ = true;
- true_is_fall_through_ = where;
- }
-
- // Swap the true and false targets but keep the same actual label as
- // the fall through. This is used when compiling negated
- // expressions, where we want to swap the targets but preserve the
- // state.
- void Invert() {
- JumpTarget* temp_target = true_target_;
- true_target_ = false_target_;
- false_target_ = temp_target;
-
- true_is_fall_through_ = !true_is_fall_through_;
- }
-
- private:
- // True and false jump targets.
- JumpTarget* true_target_;
- JumpTarget* false_target_;
-
- // Before using the destination: true if the true target is the
- // preferred fall through, false if the false target is. After
- // using the destination: true if the true target was actually used
- // as the fall through, false if the false target was.
- bool true_is_fall_through_;
-
- // True if the Split or Goto functions have been called.
- bool is_used_;
-};
-
-
-// -------------------------------------------------------------------------
-// Code generation state
-
-// The state is passed down the AST by the code generator (and back up, in
-// the form of the state of the jump target pair). It is threaded through
-// the call stack. Constructing a state implicitly pushes it on the owning
-// code generator's stack of states, and destroying one implicitly pops it.
-//
-// The code generator state is only used for expressions, so statements have
-// the initial state.
-
-class CodeGenState BASE_EMBEDDED {
- public:
- // Create an initial code generator state. Destroying the initial state
- // leaves the code generator with a NULL state.
- explicit CodeGenState(CodeGenerator* owner);
-
- // Create a code generator state based on a code generator's current
- // state. The new state has its own control destination.
- CodeGenState(CodeGenerator* owner, ControlDestination* destination);
-
- // Destroy a code generator state and restore the owning code generator's
- // previous state.
- ~CodeGenState();
-
- // Accessors for the state.
- ControlDestination* destination() const { return destination_; }
-
- private:
- // The owning code generator.
- CodeGenerator* owner_;
-
- // A control destination in case the expression has a control-flow
- // effect.
- ControlDestination* destination_;
-
- // The previous state of the owning code generator, restored when
- // this state is destroyed.
- CodeGenState* previous_;
-};
-
-
-// -------------------------------------------------------------------------
-// Arguments allocation mode
-
-enum ArgumentsAllocationMode {
- NO_ARGUMENTS_ALLOCATION,
- EAGER_ARGUMENTS_ALLOCATION,
- LAZY_ARGUMENTS_ALLOCATION
-};
-
-
-// -------------------------------------------------------------------------
// CodeGenerator
class CodeGenerator: public AstVisitor {
@@ -319,431 +66,7 @@
int pos,
bool right_here = false);
- // Accessors
- MacroAssembler* masm() { return masm_; }
- VirtualFrame* frame() const { return frame_; }
- inline Handle<Script> script();
-
- bool has_valid_frame() const { return frame_ != NULL; }
-
- // Set the virtual frame to be new_frame, with non-frame register
- // reference counts given by non_frame_registers. The non-frame
- // register reference counts of the old frame are returned in
- // non_frame_registers.
- void SetFrame(VirtualFrame* new_frame, RegisterFile* non_frame_registers);
-
- void DeleteFrame();
-
- RegisterAllocator* allocator() const { return allocator_; }
-
- CodeGenState* state() { return state_; }
- void set_state(CodeGenState* state) { state_ = state; }
-
- void AddDeferred(DeferredCode* code) { deferred_.Add(code); }
-
- bool in_spilled_code() const { return in_spilled_code_; }
- void set_in_spilled_code(bool flag) { in_spilled_code_ = flag; }
-
private:
- // Type of a member function that generates inline code for a native function.
- typedef void (CodeGenerator::*InlineFunctionGenerator)
- (ZoneList<Expression*>*);
-
- static const InlineFunctionGenerator kInlineFunctionGenerators[];
-
- // Construction/Destruction
- explicit CodeGenerator(MacroAssembler* masm);
-
- // Accessors
- inline bool is_eval();
- inline Scope* scope();
- inline bool is_strict_mode();
- inline StrictModeFlag strict_mode_flag();
-
- // Generating deferred code.
- void ProcessDeferred();
-
- // State
- ControlDestination* destination() const { return state_->destination(); }
-
- // Track loop nesting level.
- int loop_nesting() const { return loop_nesting_; }
- void IncrementLoopNesting() { loop_nesting_++; }
- void DecrementLoopNesting() { loop_nesting_--; }
-
-
- // Node visitors.
- void VisitStatements(ZoneList<Statement*>* statements);
-
- virtual void VisitSlot(Slot* node);
-#define DEF_VISIT(type) \
- virtual void Visit##type(type* node);
- AST_NODE_LIST(DEF_VISIT)
-#undef DEF_VISIT
-
- // Visit a statement and then spill the virtual frame if control flow can
- // reach the end of the statement (ie, it does not exit via break,
- // continue, return, or throw). This function is used temporarily while
- // the code generator is being transformed.
- void VisitAndSpill(Statement* statement);
-
- // Visit a list of statements and then spill the virtual frame if control
- // flow can reach the end of the list.
- void VisitStatementsAndSpill(ZoneList<Statement*>* statements);
-
- // Main code generation function
- void Generate(CompilationInfo* info);
-
- // Generate the return sequence code. Should be called no more than
- // once per compiled function, immediately after binding the return
- // target (which can not be done more than once).
- void GenerateReturnSequence(Result* return_value);
-
- // Generate code for a fast smi loop.
- void GenerateFastSmiLoop(ForStatement* node);
-
- // Returns the arguments allocation mode.
- ArgumentsAllocationMode ArgumentsMode();
-
- // Store the arguments object and allocate it if necessary.
- Result StoreArgumentsObject(bool initial);
-
- // The following are used by class Reference.
- void LoadReference(Reference* ref);
- void UnloadReference(Reference* ref);
-
- Operand SlotOperand(Slot* slot, Register tmp);
-
- Operand ContextSlotOperandCheckExtensions(Slot* slot,
- Result tmp,
- JumpTarget* slow);
-
- // Expressions
- void LoadCondition(Expression* x,
- ControlDestination* destination,
- bool force_control);
- void Load(Expression* expr);
- void LoadGlobal();
- void LoadGlobalReceiver();
-
- // Generate code to push the value of an expression on top of the frame
- // and then spill the frame fully to memory. This function is used
- // temporarily while the code generator is being transformed.
- void LoadAndSpill(Expression* expression);
-
- // Read a value from a slot and leave it on top of the expression stack.
- void LoadFromSlot(Slot* slot, TypeofState typeof_state);
- void LoadFromSlotCheckForArguments(Slot* slot, TypeofState state);
- Result LoadFromGlobalSlotCheckExtensions(Slot* slot,
- TypeofState typeof_state,
- JumpTarget* slow);
-
- // Support for loading from local/global variables and arguments
- // whose location is known unless they are shadowed by
- // eval-introduced bindings. Generates no code for unsupported slot
- // types and therefore expects to fall through to the slow jump target.
- void EmitDynamicLoadFromSlotFastCase(Slot* slot,
- TypeofState typeof_state,
- Result* result,
- JumpTarget* slow,
- JumpTarget* done);
-
- // Store the value on top of the expression stack into a slot, leaving the
- // value in place.
- void StoreToSlot(Slot* slot, InitState init_state);
-
- // Support for compiling assignment expressions.
- void EmitSlotAssignment(Assignment* node);
- void EmitNamedPropertyAssignment(Assignment* node);
- void EmitKeyedPropertyAssignment(Assignment* node);
-
- // Receiver is passed on the frame and not consumed.
- Result EmitNamedLoad(Handle<String> name, bool is_contextual);
-
- // If the store is contextual, value is passed on the frame and consumed.
- // Otherwise, receiver and value are passed on the frame and consumed.
- Result EmitNamedStore(Handle<String> name, bool is_contextual);
-
- // Load a property of an object, returning it in a Result.
- // The object and the property name are passed on the stack, and
- // not changed.
- Result EmitKeyedLoad();
-
- // Receiver, key, and value are passed on the frame and consumed.
- Result EmitKeyedStore(StaticType* key_type);
-
- // Special code for typeof expressions: Unfortunately, we must
- // be careful when loading the expression in 'typeof'
- // expressions. We are not allowed to throw reference errors for
- // non-existing properties of the global object, so we must make it
- // look like an explicit property access, instead of an access
- // through the context chain.
- void LoadTypeofExpression(Expression* x);
-
- // Translate the value on top of the frame into control flow to the
- // control destination.
- void ToBoolean(ControlDestination* destination);
-
- // Generate code that computes a shortcutting logical operation.
- void GenerateLogicalBooleanOperation(BinaryOperation* node);
-
- void GenericBinaryOperation(BinaryOperation* expr,
- OverwriteMode overwrite_mode);
-
- // Generate a stub call from the virtual frame.
- Result GenerateGenericBinaryOpStubCall(GenericBinaryOpStub* stub,
- Result* left,
- Result* right);
-
- // Emits code sequence that jumps to a JumpTarget if the inputs
- // are both smis. Cannot be in MacroAssembler because it takes
- // advantage of TypeInfo to skip unneeded checks.
- void JumpIfBothSmiUsingTypeInfo(Result* left,
- Result* right,
- JumpTarget* both_smi);
-
- // Emits code sequence that jumps to deferred code if the input
- // is not a smi. Cannot be in MacroAssembler because it takes
- // advantage of TypeInfo to skip unneeded checks.
- void JumpIfNotSmiUsingTypeInfo(Register reg,
- TypeInfo type,
- DeferredCode* deferred);
-
- // Emits code sequence that jumps to deferred code if the inputs
- // are not both smis. Cannot be in MacroAssembler because it takes
- // advantage of TypeInfo to skip unneeded checks.
- void JumpIfNotBothSmiUsingTypeInfo(Register left,
- Register right,
- TypeInfo left_info,
- TypeInfo right_info,
- DeferredCode* deferred);
-
- // If possible, combine two constant smi values using op to produce
- // a smi result, and push it on the virtual frame, all at compile time.
- // Returns true if it succeeds. Otherwise it has no effect.
- bool FoldConstantSmis(Token::Value op, int left, int right);
-
- // Emit code to perform a binary operation on a constant
- // smi and a likely smi. Consumes the Result *operand.
- Result ConstantSmiBinaryOperation(BinaryOperation* expr,
- Result* operand,
- Handle<Object> constant_operand,
- bool reversed,
- OverwriteMode overwrite_mode);
-
- // Emit code to perform a binary operation on two likely smis.
- // The code to handle smi arguments is produced inline.
- // Consumes the Results *left and *right.
- Result LikelySmiBinaryOperation(BinaryOperation* expr,
- Result* left,
- Result* right,
- OverwriteMode overwrite_mode);
-
- void Comparison(AstNode* node,
- Condition cc,
- bool strict,
- ControlDestination* destination);
-
- // If at least one of the sides is a constant smi, generate optimized code.
- void ConstantSmiComparison(Condition cc,
- bool strict,
- ControlDestination* destination,
- Result* left_side,
- Result* right_side,
- bool left_side_constant_smi,
- bool right_side_constant_smi,
- bool is_loop_condition);
-
- void GenerateInlineNumberComparison(Result* left_side,
- Result* right_side,
- Condition cc,
- ControlDestination* dest);
-
- // To prevent long attacker-controlled byte sequences, integer constants
- // from the JavaScript source are loaded in two parts if they are larger
- // than 16 bits.
- static const int kMaxSmiInlinedBits = 16;
- bool IsUnsafeSmi(Handle<Object> value);
- // Load an integer constant x into a register target using
- // at most 16 bits of user-controlled data per assembly operation.
- void LoadUnsafeSmi(Register target, Handle<Object> value);
-
- void CallWithArguments(ZoneList<Expression*>* arguments,
- CallFunctionFlags flags,
- int position);
-
- // An optimized implementation of expressions of the form
- // x.apply(y, arguments). We call x the applicand and y the receiver.
- // The optimization avoids allocating an arguments object if possible.
- void CallApplyLazy(Expression* applicand,
- Expression* receiver,
- VariableProxy* arguments,
- int position);
-
- void CheckStack();
-
- bool CheckForInlineRuntimeCall(CallRuntime* node);
-
- void ProcessDeclarations(ZoneList<Declaration*>* declarations);
-
- // Declare global variables and functions in the given array of
- // name/value pairs.
- void DeclareGlobals(Handle<FixedArray> pairs);
-
- // Instantiate the function based on the shared function info.
- void InstantiateFunction(Handle<SharedFunctionInfo> function_info,
- bool pretenure);
-
- // Support for type checks.
- void GenerateIsSmi(ZoneList<Expression*>* args);
- void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
- void GenerateIsArray(ZoneList<Expression*>* args);
- void GenerateIsRegExp(ZoneList<Expression*>* args);
- void GenerateIsObject(ZoneList<Expression*>* args);
- void GenerateIsSpecObject(ZoneList<Expression*>* args);
- void GenerateIsFunction(ZoneList<Expression*>* args);
- void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
- void GenerateIsStringWrapperSafeForDefaultValueOf(
- ZoneList<Expression*>* args);
-
- // Support for construct call checks.
- void GenerateIsConstructCall(ZoneList<Expression*>* args);
-
- // Support for arguments.length and arguments[?].
- void GenerateArgumentsLength(ZoneList<Expression*>* args);
- void GenerateArguments(ZoneList<Expression*>* args);
-
- // Support for accessing the class and value fields of an object.
- void GenerateClassOf(ZoneList<Expression*>* args);
- void GenerateValueOf(ZoneList<Expression*>* args);
- void GenerateSetValueOf(ZoneList<Expression*>* args);
-
- // Fast support for charCodeAt(n).
- void GenerateStringCharCodeAt(ZoneList<Expression*>* args);
-
- // Fast support for string.charAt(n) and string[n].
- void GenerateStringCharFromCode(ZoneList<Expression*>* args);
-
- // Fast support for string.charAt(n) and string[n].
- void GenerateStringCharAt(ZoneList<Expression*>* args);
-
- // Fast support for object equality testing.
- void GenerateObjectEquals(ZoneList<Expression*>* args);
-
- void GenerateLog(ZoneList<Expression*>* args);
-
- void GenerateGetFramePointer(ZoneList<Expression*>* args);
-
- // Fast support for Math.random().
- void GenerateRandomHeapNumber(ZoneList<Expression*>* args);
-
- // Fast support for StringAdd.
- void GenerateStringAdd(ZoneList<Expression*>* args);
-
- // Fast support for SubString.
- void GenerateSubString(ZoneList<Expression*>* args);
-
- // Fast support for StringCompare.
- void GenerateStringCompare(ZoneList<Expression*>* args);
-
- // Support for direct calls from JavaScript to native RegExp code.
- void GenerateRegExpExec(ZoneList<Expression*>* args);
-
- void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
-
- // Support for fast native caches.
- void GenerateGetFromCache(ZoneList<Expression*>* args);
-
- // Fast support for number to string.
- void GenerateNumberToString(ZoneList<Expression*>* args);
-
- // Fast swapping of elements. Takes three expressions, the object and two
- // indices. This should only be used if the indices are known to be
- // non-negative and within bounds of the elements array at the call site.
- void GenerateSwapElements(ZoneList<Expression*>* args);
-
- // Fast call for custom callbacks.
- void GenerateCallFunction(ZoneList<Expression*>* args);
-
- // Fast call to math functions.
- void GenerateMathPow(ZoneList<Expression*>* args);
- void GenerateMathSin(ZoneList<Expression*>* args);
- void GenerateMathCos(ZoneList<Expression*>* args);
- void GenerateMathSqrt(ZoneList<Expression*>* args);
- void GenerateMathLog(ZoneList<Expression*>* args);
-
- // Check whether two RegExps are equivalent.
- void GenerateIsRegExpEquivalent(ZoneList<Expression*>* args);
-
- void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args);
- void GenerateGetCachedArrayIndex(ZoneList<Expression*>* args);
- void GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args);
-
- // Simple condition analysis.
- enum ConditionAnalysis {
- ALWAYS_TRUE,
- ALWAYS_FALSE,
- DONT_KNOW
- };
- ConditionAnalysis AnalyzeCondition(Expression* cond);
-
- // Methods used to indicate which source code is generated for. Source
- // positions are collected by the assembler and emitted with the relocation
- // information.
- void CodeForFunctionPosition(FunctionLiteral* fun);
- void CodeForReturnPosition(FunctionLiteral* fun);
- void CodeForStatementPosition(Statement* node);
- void CodeForDoWhileConditionPosition(DoWhileStatement* stmt);
- void CodeForSourcePosition(int pos);
-
- void SetTypeForStackSlot(Slot* slot, TypeInfo info);
-
-#ifdef DEBUG
- // True if the registers are valid for entry to a block. There should
- // be no frame-external references to (non-reserved) registers.
- bool HasValidEntryRegisters();
-#endif
-
- ZoneList<DeferredCode*> deferred_;
-
- // Assembler
- MacroAssembler* masm_; // to generate code
-
- CompilationInfo* info_;
-
- // Code generation state
- VirtualFrame* frame_;
- RegisterAllocator* allocator_;
- CodeGenState* state_;
- int loop_nesting_;
-
- // Jump targets.
- // The target of the return from the function.
- BreakTarget function_return_;
-
- // True if the function return is shadowed (ie, jumping to the target
- // function_return_ does not jump to the true function return, but rather
- // to some unlinking code).
- bool function_return_is_shadowed_;
-
- // True when we are in code that expects the virtual frame to be fully
- // spilled. Some virtual frame function are disabled in DEBUG builds when
- // called from spilled code, because they do not leave the virtual frame
- // in a spilled state.
- bool in_spilled_code_;
-
- friend class VirtualFrame;
- friend class Isolate;
- friend class JumpTarget;
- friend class Reference;
- friend class Result;
- friend class FastCodeGenerator;
- friend class FullCodeGenerator;
- friend class FullCodeGenSyntaxChecker;
-
- friend class CodeGeneratorPatcher; // Used in test-log-stack-tracer.cc
- friend class InlineRuntimeFunctionsTable;
-
DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
};
diff --git a/src/x64/cpu-x64.cc b/src/x64/cpu-x64.cc
index b49fb1c..e637ba1 100644
--- a/src/x64/cpu-x64.cc
+++ b/src/x64/cpu-x64.cc
@@ -42,10 +42,12 @@
namespace internal {
void CPU::Setup() {
- Isolate::Current()->cpu_features()->Probe(true);
- if (Serializer::enabled()) {
- V8::DisableCrankshaft();
- }
+ CpuFeatures::Probe();
+}
+
+
+bool CPU::SupportsCrankshaft() {
+ return true; // Yay!
}
diff --git a/src/x64/debug-x64.cc b/src/x64/debug-x64.cc
index 0398465..423e6f2 100644
--- a/src/x64/debug-x64.cc
+++ b/src/x64/debug-x64.cc
@@ -29,7 +29,8 @@
#if defined(V8_TARGET_ARCH_X64)
-#include "codegen-inl.h"
+#include "assembler.h"
+#include "codegen.h"
#include "debug.h"
diff --git a/src/x64/deoptimizer-x64.cc b/src/x64/deoptimizer-x64.cc
index 2080c61..abac2b6 100644
--- a/src/x64/deoptimizer-x64.cc
+++ b/src/x64/deoptimizer-x64.cc
@@ -600,7 +600,6 @@
void Deoptimizer::EntryGenerator::Generate() {
GeneratePrologue();
- CpuFeatures::Scope scope(SSE2);
// Save all general purpose registers before messing with them.
const int kNumberOfRegisters = Register::kNumRegisters;
@@ -663,23 +662,26 @@
__ neg(arg5);
// Allocate a new deoptimizer object.
- __ PrepareCallCFunction(5);
+ __ PrepareCallCFunction(6);
__ movq(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
__ movq(arg1, rax);
- __ movq(arg2, Immediate(type()));
+ __ Set(arg2, type());
// Args 3 and 4 are already in the right registers.
- // On windows put the argument on the stack (PrepareCallCFunction have
- // created space for this). On linux pass the argument in r8.
+ // On windows put the arguments on the stack (PrepareCallCFunction
+ // has created space for this). On linux pass the arguments in r8 and r9.
#ifdef _WIN64
__ movq(Operand(rsp, 4 * kPointerSize), arg5);
+ __ LoadAddress(arg5, ExternalReference::isolate_address());
+ __ movq(Operand(rsp, 5 * kPointerSize), arg5);
#else
__ movq(r8, arg5);
+ __ LoadAddress(r9, ExternalReference::isolate_address());
#endif
Isolate* isolate = masm()->isolate();
- __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate), 5);
+ __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate), 6);
// Preserve deoptimizer object in register rax and get the input
// frame descriptor pointer.
__ movq(rbx, Operand(rax, Deoptimizer::input_offset()));
@@ -722,10 +724,11 @@
// Compute the output frame in the deoptimizer.
__ push(rax);
- __ PrepareCallCFunction(1);
+ __ PrepareCallCFunction(2);
__ movq(arg1, rax);
+ __ LoadAddress(arg2, ExternalReference::isolate_address());
__ CallCFunction(
- ExternalReference::compute_output_frames_function(isolate), 1);
+ ExternalReference::compute_output_frames_function(isolate), 2);
__ pop(rax);
// Replace the current frame with the output frames.
diff --git a/src/x64/disasm-x64.cc b/src/x64/disasm-x64.cc
index 189ee42..82bc6ef 100644
--- a/src/x64/disasm-x64.cc
+++ b/src/x64/disasm-x64.cc
@@ -652,6 +652,9 @@
case 2:
mnem = "adc";
break;
+ case 3:
+ mnem = "sbb";
+ break;
case 4:
mnem = "and";
break;
@@ -1018,12 +1021,26 @@
current += PrintRightOperand(current);
AppendToBuffer(", %s, %d", NameOfCPURegister(regop), (*current) & 3);
current += 1;
+ } else if (third_byte == 0x0b) {
+ get_modrm(*current, &mod, ®op, &rm);
+ // roundsd xmm, xmm/m64, imm8
+ AppendToBuffer("roundsd %s, ", NameOfCPURegister(regop));
+ current += PrintRightOperand(current);
+ AppendToBuffer(", %d", (*current) & 3);
+ current += 1;
} else {
UnimplementedInstruction();
}
} else {
get_modrm(*current, &mod, ®op, &rm);
- if (opcode == 0x6E) {
+ if (opcode == 0x28) {
+ AppendToBuffer("movapd %s, ", NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
+ } else if (opcode == 0x29) {
+ AppendToBuffer("movapd ");
+ current += PrintRightXMMOperand(current);
+ AppendToBuffer(", %s", NameOfXMMRegister(regop));
+ } else if (opcode == 0x6E) {
AppendToBuffer("mov%c %s,",
rex_w() ? 'q' : 'd',
NameOfXMMRegister(regop));
@@ -1041,6 +1058,10 @@
AppendToBuffer("movdqa ");
current += PrintRightXMMOperand(current);
AppendToBuffer(", %s", NameOfXMMRegister(regop));
+ } else if (opcode == 0xD6) {
+ AppendToBuffer("movq ");
+ current += PrintRightXMMOperand(current);
+ AppendToBuffer(", %s", NameOfXMMRegister(regop));
} else {
const char* mnemonic = "?";
if (opcode == 0x50) {
@@ -1142,6 +1163,11 @@
get_modrm(*current, &mod, ®op, &rm);
AppendToBuffer("cvtss2sd %s,", NameOfXMMRegister(regop));
current += PrintRightXMMOperand(current);
+ } else if (opcode == 0x7E) {
+ int mod, regop, rm;
+ get_modrm(*current, &mod, ®op, &rm);
+ AppendToBuffer("movq %s, ", NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
} else {
UnimplementedInstruction();
}
@@ -1159,6 +1185,22 @@
current += 4;
} // else no immediate displacement.
AppendToBuffer("nop");
+
+ } else if (opcode == 28) {
+ // movaps xmm, xmm/m128
+ int mod, regop, rm;
+ get_modrm(*current, &mod, ®op, &rm);
+ AppendToBuffer("movaps %s, ", NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
+
+ } else if (opcode == 29) {
+ // movaps xmm/m128, xmm
+ int mod, regop, rm;
+ get_modrm(*current, &mod, ®op, &rm);
+ AppendToBuffer("movaps");
+ current += PrintRightXMMOperand(current);
+ AppendToBuffer(", %s", NameOfXMMRegister(regop));
+
} else if (opcode == 0xA2 || opcode == 0x31) {
// RDTSC or CPUID
AppendToBuffer("%s", mnemonic);
@@ -1170,6 +1212,13 @@
byte_size_operand_ = idesc.byte_size_operation;
current += PrintOperands(idesc.mnem, idesc.op_order_, current);
+ } else if (opcode == 57) {
+ // xoprps xmm, xmm/m128
+ int mod, regop, rm;
+ get_modrm(*current, &mod, ®op, &rm);
+ AppendToBuffer("xorps %s, ", NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
+
} else if ((opcode & 0xF0) == 0x80) {
// Jcc: Conditional jump (branch).
current = data + JumpConditional(data);
@@ -1502,7 +1551,39 @@
data++;
}
break;
-
+ case 0xB0:
+ case 0xB1:
+ case 0xB2:
+ case 0xB3:
+ case 0xB4:
+ case 0xB5:
+ case 0xB6:
+ case 0xB7:
+ case 0xB8:
+ case 0xB9:
+ case 0xBA:
+ case 0xBB:
+ case 0xBC:
+ case 0xBD:
+ case 0xBE:
+ case 0xBF: {
+ // mov reg8,imm8 or mov reg32,imm32
+ byte opcode = *data;
+ data++;
+ bool is_32bit = (opcode >= 0xB8);
+ int reg = (opcode & 0x7) | (rex_b() ? 8 : 0);
+ if (is_32bit) {
+ AppendToBuffer("mov%c %s, ",
+ operand_size_code(),
+ NameOfCPURegister(reg));
+ data += PrintImmediate(data, DOUBLEWORD_SIZE);
+ } else {
+ AppendToBuffer("movb %s, ",
+ NameOfByteCPURegister(reg));
+ data += PrintImmediate(data, BYTE_SIZE);
+ }
+ break;
+ }
case 0xFE: {
data++;
int mod, regop, rm;
@@ -1513,9 +1594,8 @@
} else {
UnimplementedInstruction();
}
- }
break;
-
+ }
case 0x68:
AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data + 1));
data += 5;
diff --git a/src/x64/frames-x64.h b/src/x64/frames-x64.h
index 81be819..b14267c 100644
--- a/src/x64/frames-x64.h
+++ b/src/x64/frames-x64.h
@@ -99,7 +99,7 @@
public:
// FP-relative.
static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset;
- static const int kSavedRegistersOffset = +2 * kPointerSize;
+ static const int kLastParameterOffset = +2 * kPointerSize;
static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset;
// Caller SP-relative.
diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc
index 90afd85..d5fb7da 100644
--- a/src/x64/full-codegen-x64.cc
+++ b/src/x64/full-codegen-x64.cc
@@ -30,7 +30,7 @@
#if defined(V8_TARGET_ARCH_X64)
#include "code-stubs.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "compiler.h"
#include "debug.h"
#include "full-codegen.h"
@@ -232,7 +232,7 @@
}
{ Comment cmnt(masm_, "[ Stack check");
- PrepareForBailout(info->function(), NO_REGISTERS);
+ PrepareForBailoutForId(AstNode::kFunctionEntryId, NO_REGISTERS);
NearLabel ok;
__ CompareRoot(rsp, Heap::kStackLimitRootIndex);
__ j(above_equal, &ok);
@@ -781,7 +781,7 @@
// Compile all the tests with branches to their bodies.
for (int i = 0; i < clauses->length(); i++) {
CaseClause* clause = clauses->at(i);
- clause->body_target()->entry_label()->Unuse();
+ clause->body_target()->Unuse();
// The default is not a test, but remember it as final fall through.
if (clause->is_default()) {
@@ -809,7 +809,7 @@
__ cmpq(rdx, rax);
__ j(not_equal, &next_test);
__ Drop(1); // Switch value is no longer needed.
- __ jmp(clause->body_target()->entry_label());
+ __ jmp(clause->body_target());
__ bind(&slow_case);
}
@@ -821,7 +821,7 @@
__ testq(rax, rax);
__ j(not_equal, &next_test);
__ Drop(1); // Switch value is no longer needed.
- __ jmp(clause->body_target()->entry_label());
+ __ jmp(clause->body_target());
}
// Discard the test value and jump to the default if present, otherwise to
@@ -831,14 +831,14 @@
if (default_clause == NULL) {
__ jmp(nested_statement.break_target());
} else {
- __ jmp(default_clause->body_target()->entry_label());
+ __ jmp(default_clause->body_target());
}
// Compile all the case bodies.
for (int i = 0; i < clauses->length(); i++) {
Comment cmnt(masm_, "[ Case body");
CaseClause* clause = clauses->at(i);
- __ bind(clause->body_target()->entry_label());
+ __ bind(clause->body_target());
PrepareForBailoutForId(clause->EntryId(), NO_REGISTERS);
VisitStatements(clause->statements());
}
@@ -1576,27 +1576,26 @@
}
}
+ // For compound assignments we need another deoptimization point after the
+ // variable/property load.
if (expr->is_compound()) {
{ AccumulatorValueContext context(this);
switch (assign_type) {
case VARIABLE:
EmitVariableLoad(expr->target()->AsVariableProxy()->var());
+ PrepareForBailout(expr->target(), TOS_REG);
break;
case NAMED_PROPERTY:
EmitNamedPropertyLoad(property);
+ PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
break;
case KEYED_PROPERTY:
EmitKeyedPropertyLoad(property);
+ PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
break;
}
}
- // For property compound assignments we need another deoptimization
- // point after the property load.
- if (property != NULL) {
- PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
- }
-
Token::Value op = expr->binary_op();
__ push(rax); // Left operand goes on the stack.
VisitForAccumulatorValue(expr->value());
@@ -2248,15 +2247,6 @@
}
}
} else {
- // Call to some other expression. If the expression is an anonymous
- // function literal not called in a loop, mark it as one that should
- // also use the full code generator.
- FunctionLiteral* lit = fun->AsFunctionLiteral();
- if (lit != NULL &&
- lit->name()->Equals(isolate()->heap()->empty_string()) &&
- loop_depth() == 0) {
- lit->set_try_full_codegen(true);
- }
{ PreservePositionScope scope(masm()->positions_recorder());
VisitForStackValue(fun);
}
@@ -2435,11 +2425,71 @@
context()->PrepareTest(&materialize_true, &materialize_false,
&if_true, &if_false, &fall_through);
- // Just indicate false, as %_IsStringWrapperSafeForDefaultValueOf() is only
- // used in a few functions in runtime.js which should not normally be hit by
- // this compiler.
+ if (FLAG_debug_code) __ AbortIfSmi(rax);
+
+ // Check whether this map has already been checked to be safe for default
+ // valueOf.
+ __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
+ __ testb(FieldOperand(rbx, Map::kBitField2Offset),
+ Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf));
+ __ j(not_zero, if_true);
+
+ // Check for fast case object. Generate false result for slow case object.
+ __ movq(rcx, FieldOperand(rax, JSObject::kPropertiesOffset));
+ __ movq(rcx, FieldOperand(rcx, HeapObject::kMapOffset));
+ __ CompareRoot(rcx, Heap::kHashTableMapRootIndex);
+ __ j(equal, if_false);
+
+ // Look for valueOf symbol in the descriptor array, and indicate false if
+ // found. The type is not checked, so if it is a transition it is a false
+ // negative.
+ __ movq(rbx, FieldOperand(rbx, Map::kInstanceDescriptorsOffset));
+ __ movq(rcx, FieldOperand(rbx, FixedArray::kLengthOffset));
+ // rbx: descriptor array
+ // rcx: length of descriptor array
+ // Calculate the end of the descriptor array.
+ SmiIndex index = masm_->SmiToIndex(rdx, rcx, kPointerSizeLog2);
+ __ lea(rcx,
+ Operand(
+ rbx, index.reg, index.scale, FixedArray::kHeaderSize));
+ // Calculate location of the first key name.
+ __ addq(rbx,
+ Immediate(FixedArray::kHeaderSize +
+ DescriptorArray::kFirstIndex * kPointerSize));
+ // Loop through all the keys in the descriptor array. If one of these is the
+ // symbol valueOf the result is false.
+ Label entry, loop;
+ __ jmp(&entry);
+ __ bind(&loop);
+ __ movq(rdx, FieldOperand(rbx, 0));
+ __ Cmp(rdx, FACTORY->value_of_symbol());
+ __ j(equal, if_false);
+ __ addq(rbx, Immediate(kPointerSize));
+ __ bind(&entry);
+ __ cmpq(rbx, rcx);
+ __ j(not_equal, &loop);
+
+ // Reload map as register rbx was used as temporary above.
+ __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
+
+ // If a valueOf property is not found on the object check that it's
+ // prototype is the un-modified String prototype. If not result is false.
+ __ movq(rcx, FieldOperand(rbx, Map::kPrototypeOffset));
+ __ testq(rcx, Immediate(kSmiTagMask));
+ __ j(zero, if_false);
+ __ movq(rcx, FieldOperand(rcx, HeapObject::kMapOffset));
+ __ movq(rdx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalContextOffset));
+ __ cmpq(rcx,
+ ContextOperand(rdx, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX));
+ __ j(not_equal, if_false);
+ // Set the bit in the map to indicate that it has been checked safe for
+ // default valueOf and set true result.
+ __ or_(FieldOperand(rbx, Map::kBitField2Offset),
+ Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf));
+ __ jmp(if_true);
+
PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
- __ jmp(if_false);
context()->Plug(if_true, if_false);
}
@@ -2693,8 +2743,13 @@
// Return a random uint32 number in rax.
// The fresh HeapNumber is in rbx, which is callee-save on both x64 ABIs.
- __ PrepareCallCFunction(0);
- __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 0);
+ __ PrepareCallCFunction(1);
+#ifdef _WIN64
+ __ LoadAddress(rcx, ExternalReference::isolate_address());
+#else
+ __ LoadAddress(rdi, ExternalReference::isolate_address());
+#endif
+ __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1);
// Convert 32 random bits in rax to 0.(32 random bits) in a double
// by computing:
@@ -2703,7 +2758,7 @@
__ movd(xmm1, rcx);
__ movd(xmm0, rax);
__ cvtss2sd(xmm1, xmm1);
- __ xorpd(xmm0, xmm1);
+ __ xorps(xmm0, xmm1);
__ subsd(xmm0, xmm1);
__ movsd(FieldOperand(rbx, HeapNumber::kValueOffset), xmm0);
@@ -2988,15 +3043,14 @@
void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) {
ASSERT(args->length() >= 2);
- int arg_count = args->length() - 2; // For receiver and function.
- VisitForStackValue(args->at(0)); // Receiver.
- for (int i = 0; i < arg_count; i++) {
- VisitForStackValue(args->at(i + 1));
+ int arg_count = args->length() - 2; // 2 ~ receiver and function.
+ for (int i = 0; i < arg_count + 1; i++) {
+ VisitForStackValue(args->at(i));
}
- VisitForAccumulatorValue(args->at(arg_count + 1)); // Function.
+ VisitForAccumulatorValue(args->last()); // Function.
- // InvokeFunction requires function in rdi. Move it in there.
- if (!result_register().is(rdi)) __ movq(rdi, result_register());
+ // InvokeFunction requires the function in rdi. Move it in there.
+ __ movq(rdi, result_register());
ParameterCount count(arg_count);
__ InvokeFunction(rdi, count, CALL_FUNCTION);
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
@@ -3753,7 +3807,11 @@
// We need a second deoptimization point after loading the value
// in case evaluating the property load my have a side effect.
- PrepareForBailout(expr->increment(), TOS_REG);
+ if (assign_type == VARIABLE) {
+ PrepareForBailout(expr->expression(), TOS_REG);
+ } else {
+ PrepareForBailoutForId(expr->CountId(), TOS_REG);
+ }
// Call ToNumber only if operand is not a smi.
NearLabel no_conversion;
@@ -4173,30 +4231,7 @@
default:
break;
}
-
__ call(ic, mode);
-
- // Crankshaft doesn't need patching of inlined loads and stores.
- // When compiling the snapshot we need to produce code that works
- // with and without Crankshaft.
- if (V8::UseCrankshaft() && !Serializer::enabled()) {
- return;
- }
-
- // If we're calling a (keyed) load or store stub, we have to mark
- // the call as containing no inlined code so we will not attempt to
- // patch it.
- switch (ic->kind()) {
- case Code::LOAD_IC:
- case Code::KEYED_LOAD_IC:
- case Code::STORE_IC:
- case Code::KEYED_STORE_IC:
- __ nop(); // Signals no inlined code.
- break;
- default:
- // Do nothing.
- break;
- }
}
@@ -4217,7 +4252,6 @@
default:
break;
}
-
__ call(ic, RelocInfo::CODE_TARGET);
if (patch_site != NULL && patch_site->is_bound()) {
patch_site->EmitPatchInfo();
diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc
index 9180465..5ed89b5 100644
--- a/src/x64/ic-x64.cc
+++ b/src/x64/ic-x64.cc
@@ -29,7 +29,7 @@
#if defined(V8_TARGET_ARCH_X64)
-#include "codegen-inl.h"
+#include "codegen.h"
#include "ic-inl.h"
#include "runtime.h"
#include "stub-cache.h"
@@ -381,11 +381,6 @@
}
-// The offset from the inlined patch site to the start of the inlined
-// load instruction.
-const int LoadIC::kOffsetToLoadInstruction = 20;
-
-
void LoadIC::GenerateArrayLength(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : receiver
@@ -1010,7 +1005,7 @@
// Call the entry.
CEntryStub stub(1);
- __ movq(rax, Immediate(2));
+ __ Set(rax, 2);
__ LoadAddress(rbx, ExternalReference(IC_Utility(id), masm->isolate()));
__ CallStub(&stub);
@@ -1297,130 +1292,6 @@
}
-bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) {
- if (V8::UseCrankshaft()) return false;
-
- // The address of the instruction following the call.
- Address test_instruction_address =
- address + Assembler::kCallTargetAddressOffset;
- // If the instruction following the call is not a test rax, nothing
- // was inlined.
- if (*test_instruction_address != Assembler::kTestEaxByte) return false;
-
- Address delta_address = test_instruction_address + 1;
- // The delta to the start of the map check instruction.
- int delta = *reinterpret_cast<int*>(delta_address);
-
- // The map address is the last 8 bytes of the 10-byte
- // immediate move instruction, so we add 2 to get the
- // offset to the last 8 bytes.
- Address map_address = test_instruction_address + delta + 2;
- *(reinterpret_cast<Object**>(map_address)) = map;
-
- // The offset is in the 32-bit displacement of a seven byte
- // memory-to-register move instruction (REX.W 0x88 ModR/M disp32),
- // so we add 3 to get the offset of the displacement.
- Address offset_address =
- test_instruction_address + delta + kOffsetToLoadInstruction + 3;
- *reinterpret_cast<int*>(offset_address) = offset - kHeapObjectTag;
- return true;
-}
-
-
-bool LoadIC::PatchInlinedContextualLoad(Address address,
- Object* map,
- Object* cell,
- bool is_dont_delete) {
- // TODO(<bug#>): implement this.
- return false;
-}
-
-
-bool StoreIC::PatchInlinedStore(Address address, Object* map, int offset) {
- if (V8::UseCrankshaft()) return false;
-
- // The address of the instruction following the call.
- Address test_instruction_address =
- address + Assembler::kCallTargetAddressOffset;
-
- // If the instruction following the call is not a test rax, nothing
- // was inlined.
- if (*test_instruction_address != Assembler::kTestEaxByte) return false;
-
- // Extract the encoded deltas from the test rax instruction.
- Address encoded_offsets_address = test_instruction_address + 1;
- int encoded_offsets = *reinterpret_cast<int*>(encoded_offsets_address);
- int delta_to_map_check = -(encoded_offsets & 0xFFFF);
- int delta_to_record_write = encoded_offsets >> 16;
-
- // Patch the map to check. The map address is the last 8 bytes of
- // the 10-byte immediate move instruction.
- Address map_check_address = test_instruction_address + delta_to_map_check;
- Address map_address = map_check_address + 2;
- *(reinterpret_cast<Object**>(map_address)) = map;
-
- // Patch the offset in the store instruction. The offset is in the
- // last 4 bytes of a 7 byte register-to-memory move instruction.
- Address offset_address =
- map_check_address + StoreIC::kOffsetToStoreInstruction + 3;
- // The offset should have initial value (kMaxInt - 1), cleared value
- // (-1) or we should be clearing the inlined version.
- ASSERT(*reinterpret_cast<int*>(offset_address) == kMaxInt - 1 ||
- *reinterpret_cast<int*>(offset_address) == -1 ||
- (offset == 0 && map == HEAP->null_value()));
- *reinterpret_cast<int*>(offset_address) = offset - kHeapObjectTag;
-
- // Patch the offset in the write-barrier code. The offset is the
- // last 4 bytes of a 7 byte lea instruction.
- offset_address = map_check_address + delta_to_record_write + 3;
- // The offset should have initial value (kMaxInt), cleared value
- // (-1) or we should be clearing the inlined version.
- ASSERT(*reinterpret_cast<int*>(offset_address) == kMaxInt ||
- *reinterpret_cast<int*>(offset_address) == -1 ||
- (offset == 0 && map == HEAP->null_value()));
- *reinterpret_cast<int*>(offset_address) = offset - kHeapObjectTag;
-
- return true;
-}
-
-
-static bool PatchInlinedMapCheck(Address address, Object* map) {
- if (V8::UseCrankshaft()) return false;
-
- // Arguments are address of start of call sequence that called
- // the IC,
- Address test_instruction_address =
- address + Assembler::kCallTargetAddressOffset;
- // The keyed load has a fast inlined case if the IC call instruction
- // is immediately followed by a test instruction.
- if (*test_instruction_address != Assembler::kTestEaxByte) return false;
-
- // Fetch the offset from the test instruction to the map compare
- // instructions (starting with the 64-bit immediate mov of the map
- // address). This offset is stored in the last 4 bytes of the 5
- // byte test instruction.
- Address delta_address = test_instruction_address + 1;
- int delta = *reinterpret_cast<int*>(delta_address);
- // Compute the map address. The map address is in the last 8 bytes
- // of the 10-byte immediate mov instruction (incl. REX prefix), so we add 2
- // to the offset to get the map address.
- Address map_address = test_instruction_address + delta + 2;
- // Patch the map check.
- *(reinterpret_cast<Object**>(map_address)) = map;
- return true;
-}
-
-
-bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) {
- return PatchInlinedMapCheck(address, map);
-}
-
-
-bool KeyedStoreIC::PatchInlinedStore(Address address, Object* map) {
- return PatchInlinedMapCheck(address, map);
-}
-
-
void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : key
@@ -1503,11 +1374,6 @@
}
-// The offset from the inlined patch site to the start of the inlined
-// store instruction.
-const int StoreIC::kOffsetToStoreInstruction = 20;
-
-
void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : value
diff --git a/src/x64/jump-target-x64.cc b/src/x64/jump-target-x64.cc
deleted file mode 100644
index e715604..0000000
--- a/src/x64/jump-target-x64.cc
+++ /dev/null
@@ -1,437 +0,0 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "v8.h"
-
-#if defined(V8_TARGET_ARCH_X64)
-
-#include "codegen-inl.h"
-#include "jump-target-inl.h"
-#include "register-allocator-inl.h"
-#include "virtual-frame-inl.h"
-
-namespace v8 {
-namespace internal {
-
-// -------------------------------------------------------------------------
-// JumpTarget implementation.
-
-#define __ ACCESS_MASM(cgen()->masm())
-
-void JumpTarget::DoJump() {
- ASSERT(cgen()->has_valid_frame());
- // Live non-frame registers are not allowed at unconditional jumps
- // because we have no way of invalidating the corresponding results
- // which are still live in the C++ code.
- ASSERT(cgen()->HasValidEntryRegisters());
-
- if (is_bound()) {
- // Backward jump. There is an expected frame to merge to.
- ASSERT(direction_ == BIDIRECTIONAL);
- cgen()->frame()->PrepareMergeTo(entry_frame_);
- cgen()->frame()->MergeTo(entry_frame_);
- cgen()->DeleteFrame();
- __ jmp(&entry_label_);
- } else if (entry_frame_ != NULL) {
- // Forward jump with a preconfigured entry frame. Assert the
- // current frame matches the expected one and jump to the block.
- ASSERT(cgen()->frame()->Equals(entry_frame_));
- cgen()->DeleteFrame();
- __ jmp(&entry_label_);
- } else {
- // Forward jump. Remember the current frame and emit a jump to
- // its merge code.
- AddReachingFrame(cgen()->frame());
- RegisterFile empty;
- cgen()->SetFrame(NULL, &empty);
- __ jmp(&merge_labels_.last());
- }
-}
-
-
-void JumpTarget::DoBranch(Condition cc, Hint b) {
- ASSERT(cgen() != NULL);
- ASSERT(cgen()->has_valid_frame());
-
- if (is_bound()) {
- ASSERT(direction_ == BIDIRECTIONAL);
- // Backward branch. We have an expected frame to merge to on the
- // backward edge.
-
- // Swap the current frame for a copy (we do the swapping to get
- // the off-frame registers off the fall through) to use for the
- // branch.
- VirtualFrame* fall_through_frame = cgen()->frame();
- VirtualFrame* branch_frame = new VirtualFrame(fall_through_frame);
- RegisterFile non_frame_registers;
- cgen()->SetFrame(branch_frame, &non_frame_registers);
-
- // Check if we can avoid merge code.
- cgen()->frame()->PrepareMergeTo(entry_frame_);
- if (cgen()->frame()->Equals(entry_frame_)) {
- // Branch right in to the block.
- cgen()->DeleteFrame();
- __ j(cc, &entry_label_);
- cgen()->SetFrame(fall_through_frame, &non_frame_registers);
- return;
- }
-
- // Check if we can reuse existing merge code.
- for (int i = 0; i < reaching_frames_.length(); i++) {
- if (reaching_frames_[i] != NULL &&
- cgen()->frame()->Equals(reaching_frames_[i])) {
- // Branch to the merge code.
- cgen()->DeleteFrame();
- __ j(cc, &merge_labels_[i]);
- cgen()->SetFrame(fall_through_frame, &non_frame_registers);
- return;
- }
- }
-
- // To emit the merge code here, we negate the condition and branch
- // around the merge code on the fall through path.
- Label original_fall_through;
- __ j(NegateCondition(cc), &original_fall_through);
- cgen()->frame()->MergeTo(entry_frame_);
- cgen()->DeleteFrame();
- __ jmp(&entry_label_);
- cgen()->SetFrame(fall_through_frame, &non_frame_registers);
- __ bind(&original_fall_through);
-
- } else if (entry_frame_ != NULL) {
- // Forward branch with a preconfigured entry frame. Assert the
- // current frame matches the expected one and branch to the block.
- ASSERT(cgen()->frame()->Equals(entry_frame_));
- // Explicitly use the macro assembler instead of __ as forward
- // branches are expected to be a fixed size (no inserted
- // coverage-checking instructions please). This is used in
- // Reference::GetValue.
- cgen()->masm()->j(cc, &entry_label_);
-
- } else {
- // Forward branch. A copy of the current frame is remembered and
- // a branch to the merge code is emitted. Explicitly use the
- // macro assembler instead of __ as forward branches are expected
- // to be a fixed size (no inserted coverage-checking instructions
- // please). This is used in Reference::GetValue.
- AddReachingFrame(new VirtualFrame(cgen()->frame()));
- cgen()->masm()->j(cc, &merge_labels_.last());
- }
-}
-
-
-void JumpTarget::Call() {
- // Call is used to push the address of the catch block on the stack as
- // a return address when compiling try/catch and try/finally. We
- // fully spill the frame before making the call. The expected frame
- // at the label (which should be the only one) is the spilled current
- // frame plus an in-memory return address. The "fall-through" frame
- // at the return site is the spilled current frame.
- ASSERT(cgen() != NULL);
- ASSERT(cgen()->has_valid_frame());
- // There are no non-frame references across the call.
- ASSERT(cgen()->HasValidEntryRegisters());
- ASSERT(!is_linked());
-
- cgen()->frame()->SpillAll();
- VirtualFrame* target_frame = new VirtualFrame(cgen()->frame());
- target_frame->Adjust(1);
- // We do not expect a call with a preconfigured entry frame.
- ASSERT(entry_frame_ == NULL);
- AddReachingFrame(target_frame);
- __ call(&merge_labels_.last());
-}
-
-
-void JumpTarget::DoBind() {
- ASSERT(cgen() != NULL);
- ASSERT(!is_bound());
-
- // Live non-frame registers are not allowed at the start of a basic
- // block.
- ASSERT(!cgen()->has_valid_frame() || cgen()->HasValidEntryRegisters());
-
- // Fast case: the jump target was manually configured with an entry
- // frame to use.
- if (entry_frame_ != NULL) {
- // Assert no reaching frames to deal with.
- ASSERT(reaching_frames_.is_empty());
- ASSERT(!cgen()->has_valid_frame());
-
- RegisterFile empty;
- if (direction_ == BIDIRECTIONAL) {
- // Copy the entry frame so the original can be used for a
- // possible backward jump.
- cgen()->SetFrame(new VirtualFrame(entry_frame_), &empty);
- } else {
- // Take ownership of the entry frame.
- cgen()->SetFrame(entry_frame_, &empty);
- entry_frame_ = NULL;
- }
- __ bind(&entry_label_);
- return;
- }
-
- if (!is_linked()) {
- ASSERT(cgen()->has_valid_frame());
- if (direction_ == FORWARD_ONLY) {
- // Fast case: no forward jumps and no possible backward jumps.
- // The stack pointer can be floating above the top of the
- // virtual frame before the bind. Afterward, it should not.
- VirtualFrame* frame = cgen()->frame();
- int difference = frame->stack_pointer_ - (frame->element_count() - 1);
- if (difference > 0) {
- frame->stack_pointer_ -= difference;
- __ addq(rsp, Immediate(difference * kPointerSize));
- }
- } else {
- ASSERT(direction_ == BIDIRECTIONAL);
- // Fast case: no forward jumps, possible backward ones. Remove
- // constants and copies above the watermark on the fall-through
- // frame and use it as the entry frame.
- cgen()->frame()->MakeMergable();
- entry_frame_ = new VirtualFrame(cgen()->frame());
- }
- __ bind(&entry_label_);
- return;
- }
-
- if (direction_ == FORWARD_ONLY &&
- !cgen()->has_valid_frame() &&
- reaching_frames_.length() == 1) {
- // Fast case: no fall-through, a single forward jump, and no
- // possible backward jumps. Pick up the only reaching frame, take
- // ownership of it, and use it for the block about to be emitted.
- VirtualFrame* frame = reaching_frames_[0];
- RegisterFile empty;
- cgen()->SetFrame(frame, &empty);
- reaching_frames_[0] = NULL;
- __ bind(&merge_labels_[0]);
-
- // The stack pointer can be floating above the top of the
- // virtual frame before the bind. Afterward, it should not.
- int difference = frame->stack_pointer_ - (frame->element_count() - 1);
- if (difference > 0) {
- frame->stack_pointer_ -= difference;
- __ addq(rsp, Immediate(difference * kPointerSize));
- }
-
- __ bind(&entry_label_);
- return;
- }
-
- // If there is a current frame, record it as the fall-through. It
- // is owned by the reaching frames for now.
- bool had_fall_through = false;
- if (cgen()->has_valid_frame()) {
- had_fall_through = true;
- AddReachingFrame(cgen()->frame()); // Return value ignored.
- RegisterFile empty;
- cgen()->SetFrame(NULL, &empty);
- }
-
- // Compute the frame to use for entry to the block.
- ComputeEntryFrame();
-
- // Some moves required to merge to an expected frame require purely
- // frame state changes, and do not require any code generation.
- // Perform those first to increase the possibility of finding equal
- // frames below.
- for (int i = 0; i < reaching_frames_.length(); i++) {
- if (reaching_frames_[i] != NULL) {
- reaching_frames_[i]->PrepareMergeTo(entry_frame_);
- }
- }
-
- if (is_linked()) {
- // There were forward jumps. Handle merging the reaching frames
- // to the entry frame.
-
- // Loop over the (non-null) reaching frames and process any that
- // need merge code. Iterate backwards through the list to handle
- // the fall-through frame first. Set frames that will be
- // processed after 'i' to NULL if we want to avoid processing
- // them.
- for (int i = reaching_frames_.length() - 1; i >= 0; i--) {
- VirtualFrame* frame = reaching_frames_[i];
-
- if (frame != NULL) {
- // Does the frame (probably) need merge code?
- if (!frame->Equals(entry_frame_)) {
- // We could have a valid frame as the fall through to the
- // binding site or as the fall through from a previous merge
- // code block. Jump around the code we are about to
- // generate.
- if (cgen()->has_valid_frame()) {
- cgen()->DeleteFrame();
- __ jmp(&entry_label_);
- }
- // Pick up the frame for this block. Assume ownership if
- // there cannot be backward jumps.
- RegisterFile empty;
- if (direction_ == BIDIRECTIONAL) {
- cgen()->SetFrame(new VirtualFrame(frame), &empty);
- } else {
- cgen()->SetFrame(frame, &empty);
- reaching_frames_[i] = NULL;
- }
- __ bind(&merge_labels_[i]);
-
- // Loop over the remaining (non-null) reaching frames,
- // looking for any that can share merge code with this one.
- for (int j = 0; j < i; j++) {
- VirtualFrame* other = reaching_frames_[j];
- if (other != NULL && other->Equals(cgen()->frame())) {
- // Set the reaching frame element to null to avoid
- // processing it later, and then bind its entry label.
- reaching_frames_[j] = NULL;
- __ bind(&merge_labels_[j]);
- }
- }
-
- // Emit the merge code.
- cgen()->frame()->MergeTo(entry_frame_);
- } else if (i == reaching_frames_.length() - 1 && had_fall_through) {
- // If this is the fall through frame, and it didn't need
- // merge code, we need to pick up the frame so we can jump
- // around subsequent merge blocks if necessary.
- RegisterFile empty;
- cgen()->SetFrame(frame, &empty);
- reaching_frames_[i] = NULL;
- }
- }
- }
-
- // The code generator may not have a current frame if there was no
- // fall through and none of the reaching frames needed merging.
- // In that case, clone the entry frame as the current frame.
- if (!cgen()->has_valid_frame()) {
- RegisterFile empty;
- cgen()->SetFrame(new VirtualFrame(entry_frame_), &empty);
- }
-
- // There may be unprocessed reaching frames that did not need
- // merge code. They will have unbound merge labels. Bind their
- // merge labels to be the same as the entry label and deallocate
- // them.
- for (int i = 0; i < reaching_frames_.length(); i++) {
- if (!merge_labels_[i].is_bound()) {
- reaching_frames_[i] = NULL;
- __ bind(&merge_labels_[i]);
- }
- }
-
- // There are non-NULL reaching frames with bound labels for each
- // merge block, but only on backward targets.
- } else {
- // There were no forward jumps. There must be a current frame and
- // this must be a bidirectional target.
- ASSERT(reaching_frames_.length() == 1);
- ASSERT(reaching_frames_[0] != NULL);
- ASSERT(direction_ == BIDIRECTIONAL);
-
- // Use a copy of the reaching frame so the original can be saved
- // for possible reuse as a backward merge block.
- RegisterFile empty;
- cgen()->SetFrame(new VirtualFrame(reaching_frames_[0]), &empty);
- __ bind(&merge_labels_[0]);
- cgen()->frame()->MergeTo(entry_frame_);
- }
-
- __ bind(&entry_label_);
-}
-
-
-void BreakTarget::Jump() {
- // Drop leftover statement state from the frame before merging, without
- // emitting code.
- ASSERT(cgen()->has_valid_frame());
- int count = cgen()->frame()->height() - expected_height_;
- cgen()->frame()->ForgetElements(count);
- DoJump();
-}
-
-
-void BreakTarget::Jump(Result* arg) {
- // Drop leftover statement state from the frame before merging, without
- // emitting code.
- ASSERT(cgen()->has_valid_frame());
- int count = cgen()->frame()->height() - expected_height_;
- cgen()->frame()->ForgetElements(count);
- cgen()->frame()->Push(arg);
- DoJump();
-}
-
-
-void BreakTarget::Bind() {
-#ifdef DEBUG
- // All the forward-reaching frames should have been adjusted at the
- // jumps to this target.
- for (int i = 0; i < reaching_frames_.length(); i++) {
- ASSERT(reaching_frames_[i] == NULL ||
- reaching_frames_[i]->height() == expected_height_);
- }
-#endif
- // Drop leftover statement state from the frame before merging, even on
- // the fall through. This is so we can bind the return target with state
- // on the frame.
- if (cgen()->has_valid_frame()) {
- int count = cgen()->frame()->height() - expected_height_;
- cgen()->frame()->ForgetElements(count);
- }
- DoBind();
-}
-
-
-void BreakTarget::Bind(Result* arg) {
-#ifdef DEBUG
- // All the forward-reaching frames should have been adjusted at the
- // jumps to this target.
- for (int i = 0; i < reaching_frames_.length(); i++) {
- ASSERT(reaching_frames_[i] == NULL ||
- reaching_frames_[i]->height() == expected_height_ + 1);
- }
-#endif
- // Drop leftover statement state from the frame before merging, even on
- // the fall through. This is so we can bind the return target with state
- // on the frame.
- if (cgen()->has_valid_frame()) {
- int count = cgen()->frame()->height() - expected_height_;
- cgen()->frame()->ForgetElements(count);
- cgen()->frame()->Push(arg);
- }
- DoBind();
- *arg = cgen()->frame()->Pop();
-}
-
-
-#undef __
-
-
-} } // namespace v8::internal
-
-#endif // V8_TARGET_ARCH_X64
diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc
index 86a7e83..c242874 100644
--- a/src/x64/lithium-codegen-x64.cc
+++ b/src/x64/lithium-codegen-x64.cc
@@ -91,7 +91,7 @@
void LCodeGen::FinishCode(Handle<Code> code) {
ASSERT(is_done());
- code->set_stack_slots(StackSlotCount());
+ code->set_stack_slots(GetStackSlotCount());
code->set_safepoint_table_offset(safepoints_.GetCodeOffset());
PopulateDeoptimizationData(code);
Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(code);
@@ -146,10 +146,10 @@
__ push(rdi); // Callee's JS function.
// Reserve space for the stack slots needed by the code.
- int slots = StackSlotCount();
+ int slots = GetStackSlotCount();
if (slots > 0) {
if (FLAG_debug_code) {
- __ movl(rax, Immediate(slots));
+ __ Set(rax, slots);
__ movq(kScratchRegister, kSlotsZapValue, RelocInfo::NONE);
Label loop;
__ bind(&loop);
@@ -290,7 +290,7 @@
while (byte_count-- > 0) {
__ int3();
}
- safepoints_.Emit(masm(), StackSlotCount());
+ safepoints_.Emit(masm(), GetStackSlotCount());
return !is_aborted();
}
@@ -418,7 +418,7 @@
translation->StoreDoubleStackSlot(op->index());
} else if (op->IsArgument()) {
ASSERT(is_tagged);
- int src_index = StackSlotCount() + op->index();
+ int src_index = GetStackSlotCount() + op->index();
translation->StoreStackSlot(src_index);
} else if (op->IsRegister()) {
Register reg = ToRegister(op);
@@ -440,14 +440,16 @@
}
-void LCodeGen::CallCode(Handle<Code> code,
- RelocInfo::Mode mode,
- LInstruction* instr) {
+void LCodeGen::CallCodeGeneric(Handle<Code> code,
+ RelocInfo::Mode mode,
+ LInstruction* instr,
+ SafepointMode safepoint_mode,
+ int argc) {
ASSERT(instr != NULL);
LPointerMap* pointers = instr->pointer_map();
RecordPosition(pointers->position());
__ call(code, mode);
- RegisterLazyDeoptimization(instr);
+ RegisterLazyDeoptimization(instr, safepoint_mode, argc);
// Signal that we don't inline smi code before these stubs in the
// optimizing code generator.
@@ -458,6 +460,13 @@
}
+void LCodeGen::CallCode(Handle<Code> code,
+ RelocInfo::Mode mode,
+ LInstruction* instr) {
+ CallCodeGeneric(code, mode, instr, RECORD_SIMPLE_SAFEPOINT, 0);
+}
+
+
void LCodeGen::CallRuntime(const Runtime::Function* function,
int num_arguments,
LInstruction* instr) {
@@ -467,11 +476,23 @@
RecordPosition(pointers->position());
__ CallRuntime(function, num_arguments);
- RegisterLazyDeoptimization(instr);
+ RegisterLazyDeoptimization(instr, RECORD_SIMPLE_SAFEPOINT, 0);
}
-void LCodeGen::RegisterLazyDeoptimization(LInstruction* instr) {
+void LCodeGen::CallRuntimeFromDeferred(Runtime::FunctionId id,
+ int argc,
+ LInstruction* instr) {
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ __ CallRuntimeSaveDoubles(id);
+ RecordSafepointWithRegisters(
+ instr->pointer_map(), argc, Safepoint::kNoDeoptimizationIndex);
+}
+
+
+void LCodeGen::RegisterLazyDeoptimization(LInstruction* instr,
+ SafepointMode safepoint_mode,
+ int argc) {
// Create the environment to bailout to. If the call has side effects
// execution has to continue after the call otherwise execution can continue
// from a previous bailout point repeating the call.
@@ -483,8 +504,17 @@
}
RegisterEnvironmentForDeoptimization(deoptimization_environment);
- RecordSafepoint(instr->pointer_map(),
- deoptimization_environment->deoptimization_index());
+ if (safepoint_mode == RECORD_SIMPLE_SAFEPOINT) {
+ ASSERT(argc == 0);
+ RecordSafepoint(instr->pointer_map(),
+ deoptimization_environment->deoptimization_index());
+ } else {
+ ASSERT(safepoint_mode == RECORD_SAFEPOINT_WITH_REGISTERS);
+ RecordSafepointWithRegisters(
+ instr->pointer_map(),
+ argc,
+ deoptimization_environment->deoptimization_index());
+ }
}
@@ -534,7 +564,7 @@
// jump entry if this is the case.
if (jump_table_.is_empty() ||
jump_table_.last().address != entry) {
- jump_table_.Add(entry);
+ jump_table_.Add(JumpTableEntry(entry));
}
__ j(cc, &jump_table_.last().label);
}
@@ -605,6 +635,8 @@
Safepoint::Kind kind,
int arguments,
int deoptimization_index) {
+ ASSERT(kind == expected_safepoint_kind_);
+
const ZoneList<LOperand*>* operands = pointers->operands();
Safepoint safepoint = safepoints_.DefineSafepoint(masm(),
@@ -1067,7 +1099,7 @@
void LCodeGen::DoConstantI(LConstantI* instr) {
ASSERT(instr->result()->IsRegister());
- __ movl(ToRegister(instr->result()), Immediate(instr->value()));
+ __ Set(ToRegister(instr->result()), instr->value());
}
@@ -1079,7 +1111,7 @@
// Use xor to produce +0.0 in a fast and compact way, but avoid to
// do so if the constant is -0.0.
if (int_val == 0) {
- __ xorpd(res, res);
+ __ xorps(res, res);
} else {
Register tmp = ToRegister(instr->TempAt(0));
__ Set(tmp, int_val);
@@ -1191,12 +1223,12 @@
break;
case Token::MOD:
__ PrepareCallCFunction(2);
- __ movsd(xmm0, left);
+ __ movaps(xmm0, left);
ASSERT(right.is(xmm1));
__ CallCFunction(
ExternalReference::double_fp_operation(Token::MOD, isolate()), 2);
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
- __ movsd(result, xmm0);
+ __ movaps(result, xmm0);
break;
default:
UNREACHABLE();
@@ -1255,7 +1287,7 @@
EmitBranch(true_block, false_block, not_zero);
} else if (r.IsDouble()) {
XMMRegister reg = ToDoubleRegister(instr->InputAt(0));
- __ xorpd(xmm0, xmm0);
+ __ xorps(xmm0, xmm0);
__ ucomisd(reg, xmm0);
EmitBranch(true_block, false_block, not_equal);
} else {
@@ -1290,7 +1322,7 @@
// HeapNumber => false iff +0, -0, or NaN. These three cases set the
// zero flag when compared to zero using ucomisd.
- __ xorpd(xmm0, xmm0);
+ __ xorps(xmm0, xmm0);
__ ucomisd(xmm0, FieldOperand(reg, HeapNumber::kValueOffset));
__ j(zero, false_label);
__ jmp(true_label);
@@ -1328,11 +1360,8 @@
void LCodeGen::DoDeferredStackCheck(LGoto* instr) {
- __ Pushad();
- __ CallRuntimeSaveDoubles(Runtime::kStackGuard);
- RecordSafepointWithRegisters(
- instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
- __ Popad();
+ PushSafepointRegistersScope scope(this);
+ CallRuntimeFromDeferred(Runtime::kStackGuard, 0, instr);
}
@@ -1485,10 +1514,11 @@
__ CompareRoot(reg, Heap::kNullValueRootIndex);
if (instr->is_strict()) {
+ ASSERT(Heap::kTrueValueRootIndex >= 0);
__ movl(result, Immediate(Heap::kTrueValueRootIndex));
NearLabel load;
__ j(equal, &load);
- __ movl(result, Immediate(Heap::kFalseValueRootIndex));
+ __ Set(result, Heap::kFalseValueRootIndex);
__ bind(&load);
__ LoadRootIndexed(result, result, 0);
} else {
@@ -1937,23 +1967,36 @@
void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
Label* map_check) {
- __ PushSafepointRegisters();
- InstanceofStub::Flags flags = static_cast<InstanceofStub::Flags>(
- InstanceofStub::kNoFlags | InstanceofStub::kCallSiteInlineCheck);
- InstanceofStub stub(flags);
+ {
+ PushSafepointRegistersScope scope(this);
+ InstanceofStub::Flags flags = static_cast<InstanceofStub::Flags>(
+ InstanceofStub::kNoFlags | InstanceofStub::kCallSiteInlineCheck);
+ InstanceofStub stub(flags);
- __ push(ToRegister(instr->InputAt(0)));
- __ Push(instr->function());
- Register temp = ToRegister(instr->TempAt(0));
- ASSERT(temp.is(rdi));
- static const int kAdditionalDelta = 16;
- int delta =
- masm_->SizeOfCodeGeneratedSince(map_check) + kAdditionalDelta;
- __ movq(temp, Immediate(delta));
- __ push(temp);
- CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
- __ movq(kScratchRegister, rax);
- __ PopSafepointRegisters();
+ __ push(ToRegister(instr->InputAt(0)));
+ __ Push(instr->function());
+
+ Register temp = ToRegister(instr->TempAt(0));
+ static const int kAdditionalDelta = 10;
+ int delta =
+ masm_->SizeOfCodeGeneratedSince(map_check) + kAdditionalDelta;
+ ASSERT(delta >= 0);
+ __ push_imm32(delta);
+
+ // We are pushing three values on the stack but recording a
+ // safepoint with two arguments because stub is going to
+ // remove the third argument from the stack before jumping
+ // to instanceof builtin on the slow path.
+ CallCodeGeneric(stub.GetCode(),
+ RelocInfo::CODE_TARGET,
+ instr,
+ RECORD_SAFEPOINT_WITH_REGISTERS,
+ 2);
+ ASSERT(delta == masm_->SizeOfCodeGeneratedSince(map_check));
+ // Move result to a register that survives the end of the
+ // PushSafepointRegisterScope.
+ __ movq(kScratchRegister, rax);
+ }
__ testq(kScratchRegister, kScratchRegister);
Label load_false;
Label done;
@@ -2015,11 +2058,11 @@
}
__ movq(rsp, rbp);
__ pop(rbp);
- __ Ret((ParameterCount() + 1) * kPointerSize, rcx);
+ __ Ret((GetParameterCount() + 1) * kPointerSize, rcx);
}
-void LCodeGen::DoLoadGlobal(LLoadGlobal* instr) {
+void LCodeGen::DoLoadGlobalCell(LLoadGlobalCell* instr) {
Register result = ToRegister(instr->result());
if (result.is(rax)) {
__ load_rax(instr->hydrogen()->cell().location(),
@@ -2035,7 +2078,19 @@
}
-void LCodeGen::DoStoreGlobal(LStoreGlobal* instr) {
+void LCodeGen::DoLoadGlobalGeneric(LLoadGlobalGeneric* instr) {
+ ASSERT(ToRegister(instr->global_object()).is(rax));
+ ASSERT(ToRegister(instr->result()).is(rax));
+
+ __ Move(rcx, instr->name());
+ RelocInfo::Mode mode = instr->for_typeof() ? RelocInfo::CODE_TARGET :
+ RelocInfo::CODE_TARGET_CONTEXT;
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ CallCode(ic, mode, instr);
+}
+
+
+void LCodeGen::DoStoreGlobalCell(LStoreGlobalCell* instr) {
Register value = ToRegister(instr->InputAt(0));
Register temp = ToRegister(instr->TempAt(0));
ASSERT(!value.is(temp));
@@ -2058,6 +2113,18 @@
}
+void LCodeGen::DoStoreGlobalGeneric(LStoreGlobalGeneric* instr) {
+ ASSERT(ToRegister(instr->global_object()).is(rdx));
+ ASSERT(ToRegister(instr->value()).is(rax));
+
+ __ Move(rcx, instr->name());
+ Handle<Code> ic = instr->strict_mode()
+ ? isolate()->builtins()->StoreIC_Initialize_Strict()
+ : isolate()->builtins()->StoreIC_Initialize();
+ CallCode(ic, RelocInfo::CODE_TARGET_CONTEXT, instr);
+}
+
+
void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
Register context = ToRegister(instr->context());
Register result = ToRegister(instr->result());
@@ -2362,14 +2429,14 @@
} else {
__ cmpq(rbp, ToOperand(instr->InputAt(0)));
}
- __ movq(result, Immediate(scope()->num_parameters()));
+ __ movl(result, Immediate(scope()->num_parameters()));
__ j(equal, &done);
// Arguments adaptor frame present. Get argument length from there.
__ movq(result, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
- __ movq(result, Operand(result,
- ArgumentsAdaptorFrameConstants::kLengthOffset));
- __ SmiToInteger32(result, result);
+ __ SmiToInteger32(result,
+ Operand(result,
+ ArgumentsAdaptorFrameConstants::kLengthOffset));
// Argument length is in result register.
__ bind(&done);
@@ -2440,25 +2507,19 @@
env->deoptimization_index());
v8::internal::ParameterCount actual(rax);
__ InvokeFunction(function, actual, CALL_FUNCTION, &safepoint_generator);
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
}
void LCodeGen::DoPushArgument(LPushArgument* instr) {
LOperand* argument = instr->InputAt(0);
- if (argument->IsConstantOperand()) {
- EmitPushConstantOperand(argument);
- } else if (argument->IsRegister()) {
- __ push(ToRegister(argument));
- } else {
- ASSERT(!argument->IsDoubleRegister());
- __ push(ToOperand(argument));
- }
+ EmitPushTaggedOperand(argument);
}
void LCodeGen::DoContext(LContext* instr) {
Register result = ToRegister(instr->result());
- __ movq(result, Operand(rbp, StandardFrameConstants::kContextOffset));
+ __ movq(result, rsi);
}
@@ -2513,7 +2574,7 @@
}
// Setup deoptimization.
- RegisterLazyDeoptimization(instr);
+ RegisterLazyDeoptimization(instr, RECORD_SIMPLE_SAFEPOINT, 0);
// Restore context.
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
@@ -2538,7 +2599,7 @@
Register tmp2 = tmp.is(rcx) ? rdx : input_reg.is(rcx) ? rdx : rcx;
// Preserve the value of all registers.
- __ PushSafepointRegisters();
+ PushSafepointRegistersScope scope(this);
Label negative;
__ movl(tmp, FieldOperand(input_reg, HeapNumber::kExponentOffset));
@@ -2559,9 +2620,7 @@
// Slow case: Call the runtime system to do the number allocation.
__ bind(&slow);
- __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber);
- RecordSafepointWithRegisters(
- instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
+ CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr);
// Set the pointer to the new heap number in tmp.
if (!tmp.is(rax)) {
__ movq(tmp, rax);
@@ -2578,7 +2637,6 @@
__ StoreToSafepointRegisterSlot(input_reg, tmp);
__ bind(&done);
- __ PopSafepointRegisters();
}
@@ -2613,7 +2671,7 @@
if (r.IsDouble()) {
XMMRegister scratch = xmm0;
XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0));
- __ xorpd(scratch, scratch);
+ __ xorps(scratch, scratch);
__ subsd(scratch, input_reg);
__ andpd(input_reg, scratch);
} else if (r.IsInteger32()) {
@@ -2624,7 +2682,9 @@
Register input_reg = ToRegister(instr->InputAt(0));
// Smi check.
__ JumpIfNotSmi(input_reg, deferred->entry());
+ __ SmiToInteger32(input_reg, input_reg);
EmitIntegerMathAbs(instr);
+ __ Integer32ToSmi(input_reg, input_reg);
__ bind(deferred->exit());
}
}
@@ -2634,21 +2694,36 @@
XMMRegister xmm_scratch = xmm0;
Register output_reg = ToRegister(instr->result());
XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0));
- __ xorpd(xmm_scratch, xmm_scratch); // Zero the register.
- __ ucomisd(input_reg, xmm_scratch);
- if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
- DeoptimizeIf(below_equal, instr->environment());
+ if (CpuFeatures::IsSupported(SSE4_1)) {
+ CpuFeatures::Scope scope(SSE4_1);
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ // Deoptimize if minus zero.
+ __ movq(output_reg, input_reg);
+ __ subq(output_reg, Immediate(1));
+ DeoptimizeIf(overflow, instr->environment());
+ }
+ __ roundsd(xmm_scratch, input_reg, Assembler::kRoundDown);
+ __ cvttsd2si(output_reg, xmm_scratch);
+ __ cmpl(output_reg, Immediate(0x80000000));
+ DeoptimizeIf(equal, instr->environment());
} else {
- DeoptimizeIf(below, instr->environment());
+ __ xorps(xmm_scratch, xmm_scratch); // Zero the register.
+ __ ucomisd(input_reg, xmm_scratch);
+
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(below_equal, instr->environment());
+ } else {
+ DeoptimizeIf(below, instr->environment());
+ }
+
+ // Use truncating instruction (OK because input is positive).
+ __ cvttsd2si(output_reg, input_reg);
+
+ // Overflow is signalled with minint.
+ __ cmpl(output_reg, Immediate(0x80000000));
+ DeoptimizeIf(equal, instr->environment());
}
-
- // Use truncating instruction (OK because input is positive).
- __ cvttsd2si(output_reg, input_reg);
-
- // Overflow is signalled with minint.
- __ cmpl(output_reg, Immediate(0x80000000));
- DeoptimizeIf(equal, instr->environment());
}
@@ -2657,33 +2732,44 @@
Register output_reg = ToRegister(instr->result());
XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0));
+ Label done;
// xmm_scratch = 0.5
__ movq(kScratchRegister, V8_INT64_C(0x3FE0000000000000), RelocInfo::NONE);
__ movq(xmm_scratch, kScratchRegister);
-
+ NearLabel below_half;
+ __ ucomisd(xmm_scratch, input_reg);
+ __ j(above, &below_half); // If input_reg is NaN, this doesn't jump.
// input = input + 0.5
+ // This addition might give a result that isn't the correct for
+ // rounding, due to loss of precision, but only for a number that's
+ // so big that the conversion below will overflow anyway.
__ addsd(input_reg, xmm_scratch);
-
- // We need to return -0 for the input range [-0.5, 0[, otherwise
- // compute Math.floor(value + 0.5).
- if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
- __ ucomisd(input_reg, xmm_scratch);
- DeoptimizeIf(below_equal, instr->environment());
- } else {
- // If we don't need to bailout on -0, we check only bailout
- // on negative inputs.
- __ xorpd(xmm_scratch, xmm_scratch); // Zero the register.
- __ ucomisd(input_reg, xmm_scratch);
- DeoptimizeIf(below, instr->environment());
- }
-
- // Compute Math.floor(value + 0.5).
+ // Compute Math.floor(input).
// Use truncating instruction (OK because input is positive).
__ cvttsd2si(output_reg, input_reg);
-
// Overflow is signalled with minint.
__ cmpl(output_reg, Immediate(0x80000000));
DeoptimizeIf(equal, instr->environment());
+ __ jmp(&done);
+
+ __ bind(&below_half);
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ // Bailout if negative (including -0).
+ __ movq(output_reg, input_reg);
+ __ testq(output_reg, output_reg);
+ DeoptimizeIf(negative, instr->environment());
+ } else {
+ // Bailout if below -0.5, otherwise round to (positive) zero, even
+ // if negative.
+ // xmm_scrach = -0.5
+ __ movq(kScratchRegister, V8_INT64_C(0xBFE0000000000000), RelocInfo::NONE);
+ __ movq(xmm_scratch, kScratchRegister);
+ __ ucomisd(input_reg, xmm_scratch);
+ DeoptimizeIf(below, instr->environment());
+ }
+ __ xorl(output_reg, output_reg);
+
+ __ bind(&done);
}
@@ -2698,7 +2784,7 @@
XMMRegister xmm_scratch = xmm0;
XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0));
ASSERT(ToDoubleRegister(instr->result()).is(input_reg));
- __ xorpd(xmm_scratch, xmm_scratch);
+ __ xorps(xmm_scratch, xmm_scratch);
__ addsd(input_reg, xmm_scratch); // Convert -0 to +0.
__ sqrtsd(input_reg, input_reg);
}
@@ -2714,7 +2800,7 @@
if (exponent_type.IsDouble()) {
__ PrepareCallCFunction(2);
// Move arguments to correct registers
- __ movsd(xmm0, left_reg);
+ __ movaps(xmm0, left_reg);
ASSERT(ToDoubleRegister(right).is(xmm1));
__ CallCFunction(
ExternalReference::power_double_double_function(isolate()), 2);
@@ -2722,7 +2808,7 @@
__ PrepareCallCFunction(2);
// Move arguments to correct registers: xmm0 and edi (not rdi).
// On Windows, the registers are xmm0 and edx.
- __ movsd(xmm0, left_reg);
+ __ movaps(xmm0, left_reg);
#ifdef _WIN64
ASSERT(ToRegister(right).is(rdx));
#else
@@ -2732,7 +2818,6 @@
ExternalReference::power_double_int_function(isolate()), 2);
} else {
ASSERT(exponent_type.IsTagged());
- CpuFeatures::Scope scope(SSE2);
Register right_reg = ToRegister(right);
Label non_smi, call;
@@ -2749,13 +2834,13 @@
__ bind(&call);
__ PrepareCallCFunction(2);
// Move arguments to correct registers xmm0 and xmm1.
- __ movsd(xmm0, left_reg);
+ __ movaps(xmm0, left_reg);
// Right argument is already in xmm1.
__ CallCFunction(
ExternalReference::power_double_double_function(isolate()), 2);
}
// Return value is in xmm0.
- __ movsd(result_reg, xmm0);
+ __ movaps(result_reg, xmm0);
// Restore context register.
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
}
@@ -2818,6 +2903,21 @@
}
+void LCodeGen::DoInvokeFunction(LInvokeFunction* instr) {
+ ASSERT(ToRegister(instr->function()).is(rdi));
+ ASSERT(instr->HasPointerMap());
+ ASSERT(instr->HasDeoptimizationEnvironment());
+ LPointerMap* pointers = instr->pointer_map();
+ LEnvironment* env = instr->deoptimization_environment();
+ RecordPosition(pointers->position());
+ RegisterEnvironmentForDeoptimization(env);
+ SafepointGenerator generator(this, pointers, env->deoptimization_index());
+ ParameterCount count(instr->arity());
+ __ InvokeFunction(rdi, count, CALL_FUNCTION, &generator);
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+}
+
+
void LCodeGen::DoCallKeyed(LCallKeyed* instr) {
ASSERT(ToRegister(instr->key()).is(rcx));
ASSERT(ToRegister(instr->result()).is(rax));
@@ -2921,7 +3021,7 @@
ASSERT(ToRegister(instr->value()).is(rax));
__ Move(rcx, instr->hydrogen()->name());
- Handle<Code> ic = info_->is_strict()
+ Handle<Code> ic = instr->strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
@@ -3017,13 +3117,21 @@
ASSERT(ToRegister(instr->key()).is(rcx));
ASSERT(ToRegister(instr->value()).is(rax));
- Handle<Code> ic = info_->is_strict()
+ Handle<Code> ic = instr->strict_mode()
? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
: isolate()->builtins()->KeyedStoreIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
}
+void LCodeGen::DoStringAdd(LStringAdd* instr) {
+ EmitPushTaggedOperand(instr->left());
+ EmitPushTaggedOperand(instr->right());
+ StringAddStub stub(NO_STRING_CHECK_IN_STUB);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+}
+
+
void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
class DeferredStringCharCodeAt: public LDeferredCode {
public:
@@ -3138,7 +3246,7 @@
// contained in the register pointer map.
__ Set(result, 0);
- __ PushSafepointRegisters();
+ PushSafepointRegistersScope scope(this);
__ push(string);
// Push the index as a smi. This is safe because of the checks in
// DoStringCharCodeAt above.
@@ -3151,16 +3259,12 @@
__ Integer32ToSmi(index, index);
__ push(index);
}
- __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
- __ CallRuntimeSaveDoubles(Runtime::kStringCharCodeAt);
- RecordSafepointWithRegisters(
- instr->pointer_map(), 2, Safepoint::kNoDeoptimizationIndex);
+ CallRuntimeFromDeferred(Runtime::kStringCharCodeAt, 2, instr);
if (FLAG_debug_code) {
__ AbortIfNotSmi(rax);
}
__ SmiToInteger32(rax, rax);
__ StoreToSafepointRegisterSlot(result, rax);
- __ PopSafepointRegisters();
}
@@ -3203,14 +3307,11 @@
// contained in the register pointer map.
__ Set(result, 0);
- __ PushSafepointRegisters();
+ PushSafepointRegistersScope scope(this);
__ Integer32ToSmi(char_code, char_code);
__ push(char_code);
- __ CallRuntimeSaveDoubles(Runtime::kCharFromCode);
- RecordSafepointWithRegisters(
- instr->pointer_map(), 1, Safepoint::kNoDeoptimizationIndex);
+ CallRuntimeFromDeferred(Runtime::kCharFromCode, 1, instr);
__ StoreToSafepointRegisterSlot(result, rax);
- __ PopSafepointRegisters();
}
@@ -3275,13 +3376,12 @@
Register reg = ToRegister(instr->result());
__ Move(reg, Smi::FromInt(0));
- __ PushSafepointRegisters();
- __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber);
- RecordSafepointWithRegisters(
- instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
- // Ensure that value in rax survives popping registers.
- __ movq(kScratchRegister, rax);
- __ PopSafepointRegisters();
+ {
+ PushSafepointRegistersScope scope(this);
+ CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr);
+ // Ensure that value in rax survives popping registers.
+ __ movq(kScratchRegister, rax);
+ }
__ movq(reg, kScratchRegister);
}
@@ -3322,7 +3422,7 @@
DeoptimizeIf(not_equal, env);
// Convert undefined to NaN. Compute NaN as 0/0.
- __ xorpd(result_reg, result_reg);
+ __ xorps(result_reg, result_reg);
__ divsd(result_reg, result_reg);
__ jmp(&done);
@@ -3363,7 +3463,7 @@
// conversions.
__ CompareRoot(input_reg, Heap::kUndefinedValueRootIndex);
DeoptimizeIf(not_equal, instr->environment());
- __ movl(input_reg, Immediate(0));
+ __ Set(input_reg, 0);
__ jmp(&done);
__ bind(&heap_number);
@@ -3371,7 +3471,7 @@
__ movsd(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
__ cvttsd2siq(input_reg, xmm0);
__ Set(kScratchRegister, V8_UINT64_C(0x8000000000000000));
- __ cmpl(input_reg, kScratchRegister);
+ __ cmpq(input_reg, kScratchRegister);
DeoptimizeIf(equal, instr->environment());
} else {
// Deoptimize if we don't have a heap number.
@@ -3436,7 +3536,7 @@
// the JS bitwise operations.
__ cvttsd2siq(result_reg, input_reg);
__ movq(kScratchRegister, V8_INT64_C(0x8000000000000000), RelocInfo::NONE);
- __ cmpl(result_reg, kScratchRegister);
+ __ cmpq(result_reg, kScratchRegister);
DeoptimizeIf(equal, instr->environment());
} else {
__ cvttsd2si(result_reg, input_reg);
@@ -3691,14 +3791,7 @@
void LCodeGen::DoTypeof(LTypeof* instr) {
LOperand* input = instr->InputAt(0);
- if (input->IsConstantOperand()) {
- __ Push(ToHandle(LConstantOperand::cast(input)));
- } else if (input->IsRegister()) {
- __ push(ToRegister(input));
- } else {
- ASSERT(input->IsStackSlot());
- __ push(ToOperand(input));
- }
+ EmitPushTaggedOperand(input);
CallRuntime(Runtime::kTypeof, 1, instr);
}
@@ -3726,19 +3819,14 @@
}
-void LCodeGen::EmitPushConstantOperand(LOperand* operand) {
- ASSERT(operand->IsConstantOperand());
- LConstantOperand* const_op = LConstantOperand::cast(operand);
- Handle<Object> literal = chunk_->LookupLiteral(const_op);
- Representation r = chunk_->LookupLiteralRepresentation(const_op);
- if (r.IsInteger32()) {
- ASSERT(literal->IsNumber());
- __ push(Immediate(static_cast<int32_t>(literal->Number())));
- } else if (r.IsDouble()) {
- Abort("unsupported double immediate");
+void LCodeGen::EmitPushTaggedOperand(LOperand* operand) {
+ ASSERT(!operand->IsDoubleRegister());
+ if (operand->IsConstantOperand()) {
+ __ Push(ToHandle(LConstantOperand::cast(operand)));
+ } else if (operand->IsRegister()) {
+ __ push(ToRegister(operand));
} else {
- ASSERT(r.IsTagged());
- __ Push(literal);
+ __ push(ToOperand(operand));
}
}
@@ -3884,20 +3972,8 @@
void LCodeGen::DoDeleteProperty(LDeleteProperty* instr) {
LOperand* obj = instr->object();
LOperand* key = instr->key();
- // Push object.
- if (obj->IsRegister()) {
- __ push(ToRegister(obj));
- } else {
- __ push(ToOperand(obj));
- }
- // Push key.
- if (key->IsConstantOperand()) {
- EmitPushConstantOperand(key);
- } else if (key->IsRegister()) {
- __ push(ToRegister(key));
- } else {
- __ push(ToOperand(key));
- }
+ EmitPushTaggedOperand(obj);
+ EmitPushTaggedOperand(key);
ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment());
LPointerMap* pointers = instr->pointer_map();
LEnvironment* env = instr->deoptimization_environment();
diff --git a/src/x64/lithium-codegen-x64.h b/src/x64/lithium-codegen-x64.h
index f44fdb9..96e0a0f 100644
--- a/src/x64/lithium-codegen-x64.h
+++ b/src/x64/lithium-codegen-x64.h
@@ -60,7 +60,8 @@
status_(UNUSED),
deferred_(8),
osr_pc_offset_(-1),
- resolver_(this) {
+ resolver_(this),
+ expected_safepoint_kind_(Safepoint::kSimple) {
PopulateDeoptimizationLiteralsWithInlinedFunctions();
}
@@ -124,7 +125,7 @@
bool is_aborted() const { return status_ == ABORTED; }
int strict_mode_flag() const {
- return info()->is_strict() ? kStrictMode : kNonStrictMode;
+ return info()->is_strict_mode() ? kStrictMode : kNonStrictMode;
}
LChunk* chunk() const { return chunk_; }
@@ -140,8 +141,8 @@
Register input,
Register temporary);
- int StackSlotCount() const { return chunk()->spill_slot_count(); }
- int ParameterCount() const { return scope()->num_parameters(); }
+ int GetStackSlotCount() const { return chunk()->spill_slot_count(); }
+ int GetParameterCount() const { return scope()->num_parameters(); }
void Abort(const char* format, ...);
void Comment(const char* format, ...);
@@ -156,12 +157,26 @@
bool GenerateJumpTable();
bool GenerateSafepointTable();
+ enum SafepointMode {
+ RECORD_SIMPLE_SAFEPOINT,
+ RECORD_SAFEPOINT_WITH_REGISTERS
+ };
+
+ void CallCodeGeneric(Handle<Code> code,
+ RelocInfo::Mode mode,
+ LInstruction* instr,
+ SafepointMode safepoint_mode,
+ int argc);
+
+
void CallCode(Handle<Code> code,
RelocInfo::Mode mode,
LInstruction* instr);
+
void CallRuntime(const Runtime::Function* function,
int num_arguments,
LInstruction* instr);
+
void CallRuntime(Runtime::FunctionId id,
int num_arguments,
LInstruction* instr) {
@@ -169,6 +184,11 @@
CallRuntime(function, num_arguments, instr);
}
+ void CallRuntimeFromDeferred(Runtime::FunctionId id,
+ int argc,
+ LInstruction* instr);
+
+
// Generate a direct call to a known function. Expects the function
// to be in edi.
void CallKnownFunction(Handle<JSFunction> function,
@@ -177,7 +197,9 @@
void LoadHeapObject(Register result, Handle<HeapObject> object);
- void RegisterLazyDeoptimization(LInstruction* instr);
+ void RegisterLazyDeoptimization(LInstruction* instr,
+ SafepointMode safepoint_mode,
+ int argc);
void RegisterEnvironmentForDeoptimization(LEnvironment* environment);
void DeoptimizeIf(Condition cc, LEnvironment* environment);
@@ -246,11 +268,12 @@
Handle<Map> type,
Handle<String> name);
- // Emits code for pushing a constant operand.
- void EmitPushConstantOperand(LOperand* operand);
+ // Emits code for pushing either a tagged constant, a (non-double)
+ // register, or a stack slot operand.
+ void EmitPushTaggedOperand(LOperand* operand);
struct JumpTableEntry {
- inline JumpTableEntry(Address entry)
+ explicit inline JumpTableEntry(Address entry)
: label(),
address(entry) { }
Label label;
@@ -281,6 +304,27 @@
// Compiler from a set of parallel moves to a sequential list of moves.
LGapResolver resolver_;
+ Safepoint::Kind expected_safepoint_kind_;
+
+ class PushSafepointRegistersScope BASE_EMBEDDED {
+ public:
+ explicit PushSafepointRegistersScope(LCodeGen* codegen)
+ : codegen_(codegen) {
+ ASSERT(codegen_->expected_safepoint_kind_ == Safepoint::kSimple);
+ codegen_->masm_->PushSafepointRegisters();
+ codegen_->expected_safepoint_kind_ = Safepoint::kWithRegisters;
+ }
+
+ ~PushSafepointRegistersScope() {
+ ASSERT(codegen_->expected_safepoint_kind_ == Safepoint::kWithRegisters);
+ codegen_->masm_->PopSafepointRegisters();
+ codegen_->expected_safepoint_kind_ = Safepoint::kSimple;
+ }
+
+ private:
+ LCodeGen* codegen_;
+ };
+
friend class LDeferredCode;
friend class LEnvironment;
friend class SafepointGenerator;
diff --git a/src/x64/lithium-gap-resolver-x64.cc b/src/x64/lithium-gap-resolver-x64.cc
index cedd025..c3c617c 100644
--- a/src/x64/lithium-gap-resolver-x64.cc
+++ b/src/x64/lithium-gap-resolver-x64.cc
@@ -214,7 +214,7 @@
} else if (source->IsDoubleRegister()) {
XMMRegister src = cgen_->ToDoubleRegister(source);
if (destination->IsDoubleRegister()) {
- __ movsd(cgen_->ToDoubleRegister(destination), src);
+ __ movaps(cgen_->ToDoubleRegister(destination), src);
} else {
ASSERT(destination->IsDoubleStackSlot());
__ movsd(cgen_->ToOperand(destination), src);
@@ -273,9 +273,9 @@
// Swap two double registers.
XMMRegister source_reg = cgen_->ToDoubleRegister(source);
XMMRegister destination_reg = cgen_->ToDoubleRegister(destination);
- __ movsd(xmm0, source_reg);
- __ movsd(source_reg, destination_reg);
- __ movsd(destination_reg, xmm0);
+ __ movaps(xmm0, source_reg);
+ __ movaps(source_reg, destination_reg);
+ __ movaps(destination_reg, xmm0);
} else if (source->IsDoubleRegister() || destination->IsDoubleRegister()) {
// Swap a double register and a double stack slot.
diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc
index c47cd72..620bbc9 100644
--- a/src/x64/lithium-x64.cc
+++ b/src/x64/lithium-x64.cc
@@ -71,22 +71,21 @@
#ifdef DEBUG
void LInstruction::VerifyCall() {
- // Call instructions can use only fixed registers as
- // temporaries and outputs because all registers
- // are blocked by the calling convention.
- // Inputs must use a fixed register.
+ // Call instructions can use only fixed registers as temporaries and
+ // outputs because all registers are blocked by the calling convention.
+ // Inputs operands must use a fixed register or use-at-start policy or
+ // a non-register policy.
ASSERT(Output() == NULL ||
LUnallocated::cast(Output())->HasFixedPolicy() ||
!LUnallocated::cast(Output())->HasRegisterPolicy());
for (UseIterator it(this); it.HasNext(); it.Advance()) {
- LOperand* operand = it.Next();
- ASSERT(LUnallocated::cast(operand)->HasFixedPolicy() ||
- !LUnallocated::cast(operand)->HasRegisterPolicy());
+ LUnallocated* operand = LUnallocated::cast(it.Next());
+ ASSERT(operand->HasFixedPolicy() ||
+ operand->IsUsedAtStart());
}
for (TempIterator it(this); it.HasNext(); it.Advance()) {
- LOperand* operand = it.Next();
- ASSERT(LUnallocated::cast(operand)->HasFixedPolicy() ||
- !LUnallocated::cast(operand)->HasRegisterPolicy());
+ LUnallocated* operand = LUnallocated::cast(it.Next());
+ ASSERT(operand->HasFixedPolicy() ||!operand->HasRegisterPolicy());
}
}
#endif
@@ -303,6 +302,13 @@
}
+void LInvokeFunction::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ InputAt(0)->PrintTo(stream);
+ stream->Add(" #%d / ", arity());
+}
+
+
void LCallKeyed::PrintDataTo(StringStream* stream) {
stream->Add("[rcx] #%d / ", arity());
}
@@ -1114,9 +1120,9 @@
return new LIsConstructCallAndBranch(TempRegister());
} else {
if (v->IsConstant()) {
- if (HConstant::cast(v)->handle()->IsTrue()) {
+ if (HConstant::cast(v)->ToBoolean()) {
return new LGoto(instr->FirstSuccessor()->block_id());
- } else if (HConstant::cast(v)->handle()->IsFalse()) {
+ } else {
return new LGoto(instr->SecondSuccessor()->block_id());
}
}
@@ -1211,6 +1217,14 @@
}
+LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
+ LOperand* function = UseFixed(instr->function(), rdi);
+ argument_count_ -= instr->argument_count();
+ LInvokeFunction* result = new LInvokeFunction(function);
+ return MarkAsCall(DefineFixed(result, rax), instr, CANNOT_DEOPTIMIZE_EAGERLY);
+}
+
+
LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
BuiltinFunctionId op = instr->op();
if (op == kMathLog || op == kMathSin || op == kMathCos) {
@@ -1613,11 +1627,8 @@
LOperand* value = UseRegister(instr->value());
bool needs_check = !instr->value()->type().IsSmi();
if (needs_check) {
- LOperand* xmm_temp =
- (instr->CanTruncateToInt32() &&
- Isolate::Current()->cpu_features()->IsSupported(SSE3))
- ? NULL
- : FixedTemp(xmm1);
+ LOperand* xmm_temp = instr->CanTruncateToInt32() ? NULL
+ : FixedTemp(xmm1);
LTaggedToI* res = new LTaggedToI(value, xmm_temp);
return AssignEnvironment(DefineSameAsFirst(res));
} else {
@@ -1718,21 +1729,36 @@
}
-LInstruction* LChunkBuilder::DoLoadGlobal(HLoadGlobal* instr) {
- LLoadGlobal* result = new LLoadGlobal;
+LInstruction* LChunkBuilder::DoLoadGlobalCell(HLoadGlobalCell* instr) {
+ LLoadGlobalCell* result = new LLoadGlobalCell;
return instr->check_hole_value()
? AssignEnvironment(DefineAsRegister(result))
: DefineAsRegister(result);
}
-LInstruction* LChunkBuilder::DoStoreGlobal(HStoreGlobal* instr) {
- LStoreGlobal* result = new LStoreGlobal(UseRegister(instr->value()),
- TempRegister());
+LInstruction* LChunkBuilder::DoLoadGlobalGeneric(HLoadGlobalGeneric* instr) {
+ LOperand* global_object = UseFixed(instr->global_object(), rax);
+ LLoadGlobalGeneric* result = new LLoadGlobalGeneric(global_object);
+ return MarkAsCall(DefineFixed(result, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoStoreGlobalCell(HStoreGlobalCell* instr) {
+ LStoreGlobalCell* result =
+ new LStoreGlobalCell(UseRegister(instr->value()), TempRegister());
return instr->check_hole_value() ? AssignEnvironment(result) : result;
}
+LInstruction* LChunkBuilder::DoStoreGlobalGeneric(HStoreGlobalGeneric* instr) {
+ LOperand* global_object = UseFixed(instr->global_object(), rdx);
+ LOperand* value = UseFixed(instr->value(), rax);
+ LStoreGlobalGeneric* result = new LStoreGlobalGeneric(global_object, value);
+ return MarkAsCall(result, instr);
+}
+
+
LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) {
LOperand* context = UseRegisterAtStart(instr->value());
return DefineAsRegister(new LLoadContextSlot(context));
@@ -1877,7 +1903,7 @@
array_type == kExternalFloatArray;
LOperand* val = val_is_temp_register
? UseTempRegister(instr->value())
- : UseRegister(instr->key());
+ : UseRegister(instr->value());
LOperand* key = UseRegister(instr->key());
return new LStoreKeyedSpecializedArrayElement(external_pointer,
@@ -1929,6 +1955,13 @@
}
+LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) {
+ LOperand* left = UseOrConstantAtStart(instr->left());
+ LOperand* right = UseOrConstantAtStart(instr->right());
+ return MarkAsCall(DefineFixed(new LStringAdd(left, right), rax), instr);
+}
+
+
LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
LOperand* string = UseRegister(instr->string());
LOperand* index = UseRegisterOrConstant(instr->index());
@@ -1972,7 +2005,8 @@
LInstruction* LChunkBuilder::DoDeleteProperty(HDeleteProperty* instr) {
LDeleteProperty* result =
- new LDeleteProperty(Use(instr->object()), UseOrConstant(instr->key()));
+ new LDeleteProperty(UseAtStart(instr->object()),
+ UseOrConstantAtStart(instr->key()));
return MarkAsCall(DefineFixed(result, rax), instr);
}
@@ -2058,7 +2092,6 @@
env->Push(value);
}
}
- ASSERT(env->length() == instr->environment_length());
// If there is an instruction pending deoptimization environment create a
// lazy bailout instruction to capture the environment.
diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h
index e94debf..74f4820 100644
--- a/src/x64/lithium-x64.h
+++ b/src/x64/lithium-x64.h
@@ -98,14 +98,15 @@
V(GlobalObject) \
V(GlobalReceiver) \
V(Goto) \
- V(HasInstanceType) \
- V(HasInstanceTypeAndBranch) \
V(HasCachedArrayIndex) \
V(HasCachedArrayIndexAndBranch) \
+ V(HasInstanceType) \
+ V(HasInstanceTypeAndBranch) \
V(InstanceOf) \
V(InstanceOfAndBranch) \
V(InstanceOfKnownGlobal) \
V(Integer32ToDouble) \
+ V(InvokeFunction) \
V(IsNull) \
V(IsNullAndBranch) \
V(IsObject) \
@@ -118,7 +119,8 @@
V(LoadContextSlot) \
V(LoadElements) \
V(LoadExternalArrayPointer) \
- V(LoadGlobal) \
+ V(LoadGlobalCell) \
+ V(LoadGlobalGeneric) \
V(LoadKeyedFastElement) \
V(LoadKeyedGeneric) \
V(LoadKeyedSpecializedArrayElement) \
@@ -144,12 +146,14 @@
V(SmiUntag) \
V(StackCheck) \
V(StoreContextSlot) \
- V(StoreGlobal) \
+ V(StoreGlobalCell) \
+ V(StoreGlobalGeneric) \
V(StoreKeyedFastElement) \
V(StoreKeyedGeneric) \
V(StoreKeyedSpecializedArrayElement) \
V(StoreNamedField) \
V(StoreNamedGeneric) \
+ V(StringAdd) \
V(StringCharCodeAt) \
V(StringCharFromCode) \
V(StringLength) \
@@ -1245,22 +1249,55 @@
};
-class LLoadGlobal: public LTemplateInstruction<1, 0, 0> {
+class LLoadGlobalCell: public LTemplateInstruction<1, 0, 0> {
public:
- DECLARE_CONCRETE_INSTRUCTION(LoadGlobal, "load-global")
- DECLARE_HYDROGEN_ACCESSOR(LoadGlobal)
+ DECLARE_CONCRETE_INSTRUCTION(LoadGlobalCell, "load-global-cell")
+ DECLARE_HYDROGEN_ACCESSOR(LoadGlobalCell)
};
-class LStoreGlobal: public LTemplateInstruction<0, 1, 1> {
+class LLoadGlobalGeneric: public LTemplateInstruction<1, 1, 0> {
public:
- explicit LStoreGlobal(LOperand* value, LOperand* temp) {
+ explicit LLoadGlobalGeneric(LOperand* global_object) {
+ inputs_[0] = global_object;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadGlobalGeneric, "load-global-generic")
+ DECLARE_HYDROGEN_ACCESSOR(LoadGlobalGeneric)
+
+ LOperand* global_object() { return inputs_[0]; }
+ Handle<Object> name() const { return hydrogen()->name(); }
+ bool for_typeof() const { return hydrogen()->for_typeof(); }
+};
+
+
+class LStoreGlobalCell: public LTemplateInstruction<0, 1, 1> {
+ public:
+ explicit LStoreGlobalCell(LOperand* value, LOperand* temp) {
inputs_[0] = value;
temps_[0] = temp;
}
- DECLARE_CONCRETE_INSTRUCTION(StoreGlobal, "store-global")
- DECLARE_HYDROGEN_ACCESSOR(StoreGlobal)
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalCell, "store-global-cell")
+ DECLARE_HYDROGEN_ACCESSOR(StoreGlobalCell)
+};
+
+
+class LStoreGlobalGeneric: public LTemplateInstruction<0, 2, 0> {
+ public:
+ explicit LStoreGlobalGeneric(LOperand* global_object,
+ LOperand* value) {
+ inputs_[0] = global_object;
+ inputs_[1] = value;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalGeneric, "store-global-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreGlobalGeneric)
+
+ LOperand* global_object() { return InputAt(0); }
+ Handle<Object> name() const { return hydrogen()->name(); }
+ LOperand* value() { return InputAt(1); }
+ bool strict_mode() { return hydrogen()->strict_mode(); }
};
@@ -1358,6 +1395,23 @@
};
+class LInvokeFunction: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LInvokeFunction(LOperand* function) {
+ inputs_[0] = function;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(InvokeFunction, "invoke-function")
+ DECLARE_HYDROGEN_ACCESSOR(InvokeFunction)
+
+ LOperand* function() { return inputs_[0]; }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
class LCallKeyed: public LTemplateInstruction<1, 1, 0> {
public:
explicit LCallKeyed(LOperand* key) {
@@ -1582,6 +1636,7 @@
LOperand* object() { return inputs_[0]; }
LOperand* value() { return inputs_[1]; }
Handle<Object> name() const { return hydrogen()->name(); }
+ bool strict_mode() { return hydrogen()->strict_mode(); }
};
@@ -1637,12 +1692,29 @@
}
DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store-keyed-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreKeyedGeneric)
virtual void PrintDataTo(StringStream* stream);
LOperand* object() { return inputs_[0]; }
LOperand* key() { return inputs_[1]; }
LOperand* value() { return inputs_[2]; }
+ bool strict_mode() { return hydrogen()->strict_mode(); }
+};
+
+
+class LStringAdd: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LStringAdd(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string-add")
+ DECLARE_HYDROGEN_ACCESSOR(StringAdd)
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
};
diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc
index 654814c..3394206 100644
--- a/src/x64/macro-assembler-x64.cc
+++ b/src/x64/macro-assembler-x64.cc
@@ -30,7 +30,7 @@
#if defined(V8_TARGET_ARCH_X64)
#include "bootstrapper.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "assembler-x64.h"
#include "macro-assembler-x64.h"
#include "serialize.h"
@@ -40,12 +40,15 @@
namespace v8 {
namespace internal {
-MacroAssembler::MacroAssembler(void* buffer, int size)
- : Assembler(buffer, size),
+MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size)
+ : Assembler(arg_isolate, buffer, size),
generating_stub_(false),
allow_stub_calls_(true),
- root_array_available_(true),
- code_object_(isolate()->heap()->undefined_value()) {
+ root_array_available_(true) {
+ if (isolate() != NULL) {
+ code_object_ = Handle<Object>(isolate()->heap()->undefined_value(),
+ isolate());
+ }
}
@@ -647,6 +650,7 @@
Label leave_exit_frame;
Label write_back;
+ Factory* factory = isolate()->factory();
ExternalReference next_address =
ExternalReference::handle_scope_next_address();
const int kNextOffset = 0;
@@ -694,7 +698,7 @@
// Check if the function scheduled an exception.
movq(rsi, scheduled_exception_address);
- Cmp(Operand(rsi, 0), FACTORY->the_hole_value());
+ Cmp(Operand(rsi, 0), factory->the_hole_value());
j(not_equal, &promote_scheduled_exception);
LeaveApiExitFrame();
@@ -709,7 +713,7 @@
bind(&empty_result);
// It was zero; the result is undefined.
- Move(rax, FACTORY->undefined_value());
+ Move(rax, factory->undefined_value());
jmp(&prologue);
// HandleScope limit has changed. Delete allocated extensions.
@@ -785,10 +789,10 @@
void MacroAssembler::Set(Register dst, int64_t x) {
if (x == 0) {
xorl(dst, dst);
- } else if (is_int32(x)) {
- movq(dst, Immediate(static_cast<int32_t>(x)));
} else if (is_uint32(x)) {
movl(dst, Immediate(static_cast<uint32_t>(x)));
+ } else if (is_int32(x)) {
+ movq(dst, Immediate(static_cast<int32_t>(x)));
} else {
movq(dst, x, RelocInfo::NONE);
}
@@ -798,7 +802,7 @@
if (is_int32(x)) {
movq(dst, Immediate(static_cast<int32_t>(x)));
} else {
- movq(kScratchRegister, x, RelocInfo::NONE);
+ Set(kScratchRegister, x);
movq(dst, kScratchRegister);
}
}
@@ -1244,12 +1248,17 @@
Register src2) {
// No overflow checking. Use only when it's known that
// overflowing is impossible.
- ASSERT(!dst.is(src2));
if (!dst.is(src1)) {
- movq(dst, src1);
+ if (emit_debug_code()) {
+ movq(kScratchRegister, src1);
+ addq(kScratchRegister, src2);
+ Check(no_overflow, "Smi addition overflow");
+ }
+ lea(dst, Operand(src1, src2, times_1, 0));
+ } else {
+ addq(dst, src2);
+ Assert(no_overflow, "Smi addition overflow");
}
- addq(dst, src2);
- Assert(no_overflow, "Smi addition overflow");
}
@@ -1317,6 +1326,7 @@
void MacroAssembler::SmiOr(Register dst, Register src1, Register src2) {
if (!dst.is(src1)) {
+ ASSERT(!src1.is(src2));
movq(dst, src1);
}
or_(dst, src2);
@@ -1337,6 +1347,7 @@
void MacroAssembler::SmiXor(Register dst, Register src1, Register src2) {
if (!dst.is(src1)) {
+ ASSERT(!src1.is(src2));
movq(dst, src1);
}
xor_(dst, src2);
@@ -1809,7 +1820,7 @@
// Set external caught exception to false.
ExternalReference external_caught(
Isolate::k_external_caught_exception_address, isolate());
- movq(rax, Immediate(false));
+ Set(rax, static_cast<int64_t>(false));
Store(external_caught, rax);
// Set pending exception and rax to out of memory exception.
@@ -1890,7 +1901,7 @@
Condition is_smi = CheckSmi(object);
j(is_smi, &ok);
Cmp(FieldOperand(object, HeapObject::kMapOffset),
- FACTORY->heap_number_map());
+ isolate()->factory()->heap_number_map());
Assert(equal, "Operand not a number");
bind(&ok);
}
@@ -1997,7 +2008,7 @@
void MacroAssembler::SetCounter(StatsCounter* counter, int value) {
if (FLAG_native_code_counters && counter->Enabled()) {
Operand counter_operand = ExternalOperand(ExternalReference(counter));
- movq(counter_operand, Immediate(value));
+ movl(counter_operand, Immediate(value));
}
}
@@ -2147,7 +2158,7 @@
push(kScratchRegister);
if (emit_debug_code()) {
movq(kScratchRegister,
- FACTORY->undefined_value(),
+ isolate()->factory()->undefined_value(),
RelocInfo::EMBEDDED_OBJECT);
cmpq(Operand(rsp, 0), kScratchRegister);
Check(not_equal, "code object not properly patched");
@@ -2199,7 +2210,6 @@
#endif
// Optionally save all XMM registers.
if (save_doubles) {
- CpuFeatures::Scope scope(SSE2);
int space = XMMRegister::kNumRegisters * kDoubleSize +
arg_stack_space * kPointerSize;
subq(rsp, Immediate(space));
@@ -2216,8 +2226,8 @@
const int kFrameAlignment = OS::ActivationFrameAlignment();
if (kFrameAlignment > 0) {
ASSERT(IsPowerOf2(kFrameAlignment));
- movq(kScratchRegister, Immediate(-kFrameAlignment));
- and_(rsp, kScratchRegister);
+ ASSERT(is_int8(kFrameAlignment));
+ and_(rsp, Immediate(-kFrameAlignment));
}
// Patch the saved entry sp.
@@ -2316,7 +2326,7 @@
// Check the context is a global context.
if (emit_debug_code()) {
Cmp(FieldOperand(scratch, HeapObject::kMapOffset),
- FACTORY->global_context_map());
+ isolate()->factory()->global_context_map());
Check(equal, "JSGlobalObject::global_context should be a global context.");
}
@@ -2818,7 +2828,7 @@
movq(map, FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
if (emit_debug_code()) {
Label ok, fail;
- CheckMap(map, FACTORY->meta_map(), &fail, false);
+ CheckMap(map, isolate()->factory()->meta_map(), &fail, false);
jmp(&ok);
bind(&fail);
Abort("Global functions must have initial map");
@@ -2851,9 +2861,6 @@
ASSERT(frame_alignment != 0);
ASSERT(num_arguments >= 0);
- // Reserve space for Isolate address which is always passed as last parameter
- num_arguments += 1;
-
// Make stack end at alignment and allocate space for arguments and old rsp.
movq(kScratchRegister, rsp);
ASSERT(IsPowerOf2(frame_alignment));
@@ -2873,26 +2880,6 @@
void MacroAssembler::CallCFunction(Register function, int num_arguments) {
- // Pass current isolate address as additional parameter.
- if (num_arguments < kRegisterPassedArguments) {
-#ifdef _WIN64
- // First four arguments are passed in registers on Windows.
- Register arg_to_reg[] = {rcx, rdx, r8, r9};
-#else
- // First six arguments are passed in registers on other platforms.
- Register arg_to_reg[] = {rdi, rsi, rdx, rcx, r8, r9};
-#endif
- Register reg = arg_to_reg[num_arguments];
- LoadAddress(reg, ExternalReference::isolate_address());
- } else {
- // Push Isolate pointer after all parameters.
- int argument_slots_on_stack =
- ArgumentStackSlotsForCFunctionCall(num_arguments);
- LoadAddress(kScratchRegister, ExternalReference::isolate_address());
- movq(Operand(rsp, argument_slots_on_stack * kPointerSize),
- kScratchRegister);
- }
-
// Check stack alignment.
if (emit_debug_code()) {
CheckStackAlignment();
@@ -2901,7 +2888,6 @@
call(function);
ASSERT(OS::ActivationFrameAlignment() != 0);
ASSERT(num_arguments >= 0);
- num_arguments += 1;
int argument_slots_on_stack =
ArgumentStackSlotsForCFunctionCall(num_arguments);
movq(rsp, Operand(rsp, argument_slots_on_stack * kPointerSize));
@@ -2909,7 +2895,9 @@
CodePatcher::CodePatcher(byte* address, int size)
- : address_(address), size_(size), masm_(address, size + Assembler::kGap) {
+ : address_(address),
+ size_(size),
+ masm_(Isolate::Current(), address, size + Assembler::kGap) {
// Create a new macro assembler pointing to the address of the code to patch.
// The size is adjusted with kGap on order for the assembler to generate size
// bytes of instructions without failing with buffer size constraints.
diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h
index 1ee0fe0..4c17720 100644
--- a/src/x64/macro-assembler-x64.h
+++ b/src/x64/macro-assembler-x64.h
@@ -74,7 +74,11 @@
// MacroAssembler implements a collection of frequently used macros.
class MacroAssembler: public Assembler {
public:
- MacroAssembler(void* buffer, int size);
+ // The isolate parameter can be NULL if the macro assembler should
+ // not use isolate-dependent functionality. In this case, it's the
+ // responsibility of the caller to never invoke such function on the
+ // macro assembler.
+ MacroAssembler(Isolate* isolate, void* buffer, int size);
// Prevent the use of the RootArray during the lifetime of this
// scope object.
@@ -319,6 +323,16 @@
Register src,
int power);
+ // Perform the logical or of two smi values and return a smi value.
+ // If either argument is not a smi, jump to on_not_smis and retain
+ // the original values of source registers. The destination register
+ // may be changed if it's not one of the source registers.
+ template <typename LabelType>
+ void SmiOrIfSmis(Register dst,
+ Register src1,
+ Register src2,
+ LabelType* on_not_smis);
+
// Simple comparison of smis. Both sides must be known smis to use these,
// otherwise use Cmp.
@@ -1029,7 +1043,10 @@
// may be bigger than 2^16 - 1. Requires a scratch register.
void Ret(int bytes_dropped, Register scratch);
- Handle<Object> CodeObject() { return code_object_; }
+ Handle<Object> CodeObject() {
+ ASSERT(!code_object_.is_null());
+ return code_object_;
+ }
// Copy length bytes from source to destination.
// Uses scratch register internally (if you have a low-eight register
@@ -1076,6 +1093,10 @@
void set_allow_stub_calls(bool value) { allow_stub_calls_ = value; }
bool allow_stub_calls() { return allow_stub_calls_; }
+ static int SafepointRegisterStackIndex(Register reg) {
+ return SafepointRegisterStackIndex(reg.code());
+ }
+
private:
// Order general registers are pushed by Pushad.
// rax, rcx, rdx, rbx, rsi, rdi, r8, r9, r11, r14, r15.
@@ -1779,6 +1800,24 @@
template <typename LabelType>
+void MacroAssembler::SmiOrIfSmis(Register dst, Register src1, Register src2,
+ LabelType* on_not_smis) {
+ if (dst.is(src1) || dst.is(src2)) {
+ ASSERT(!src1.is(kScratchRegister));
+ ASSERT(!src2.is(kScratchRegister));
+ movq(kScratchRegister, src1);
+ or_(kScratchRegister, src2);
+ JumpIfNotSmi(kScratchRegister, on_not_smis);
+ movq(dst, kScratchRegister);
+ } else {
+ movq(dst, src1);
+ or_(dst, src2);
+ JumpIfNotSmi(dst, on_not_smis);
+ }
+}
+
+
+template <typename LabelType>
void MacroAssembler::JumpIfNotString(Register object,
Register object_map,
LabelType* not_string) {
diff --git a/src/x64/regexp-macro-assembler-x64.cc b/src/x64/regexp-macro-assembler-x64.cc
index 269e7af..d4ccb0e 100644
--- a/src/x64/regexp-macro-assembler-x64.cc
+++ b/src/x64/regexp-macro-assembler-x64.cc
@@ -114,7 +114,7 @@
RegExpMacroAssemblerX64::RegExpMacroAssemblerX64(
Mode mode,
int registers_to_save)
- : masm_(NULL, kRegExpCodeSize),
+ : masm_(Isolate::Current(), NULL, kRegExpCodeSize),
no_root_array_scope_(&masm_),
code_relative_fixup_positions_(4),
mode_(mode),
@@ -402,13 +402,14 @@
#endif
__ push(backtrack_stackpointer());
- static const int num_arguments = 3;
+ static const int num_arguments = 4;
__ PrepareCallCFunction(num_arguments);
// Put arguments into parameter registers. Parameters are
// Address byte_offset1 - Address captured substring's start.
// Address byte_offset2 - Address of current character position.
// size_t byte_length - length of capture in bytes(!)
+ // Isolate* isolate
#ifdef _WIN64
// Compute and set byte_offset1 (start of capture).
__ lea(rcx, Operand(rsi, rdx, times_1, 0));
@@ -416,6 +417,8 @@
__ lea(rdx, Operand(rsi, rdi, times_1, 0));
// Set byte_length.
__ movq(r8, rbx);
+ // Isolate.
+ __ LoadAddress(r9, ExternalReference::isolate_address());
#else // AMD64 calling convention
// Compute byte_offset2 (current position = rsi+rdi).
__ lea(rax, Operand(rsi, rdi, times_1, 0));
@@ -425,6 +428,8 @@
__ movq(rsi, rax);
// Set byte_length.
__ movq(rdx, rbx);
+ // Isolate.
+ __ LoadAddress(rcx, ExternalReference::isolate_address());
#endif
ExternalReference compare =
ExternalReference::re_case_insensitive_compare_uc16(masm_.isolate());
@@ -757,7 +762,7 @@
__ j(above_equal, &stack_ok);
// Exit with OutOfMemory exception. There is not enough space on the stack
// for our working registers.
- __ movq(rax, Immediate(EXCEPTION));
+ __ Set(rax, EXCEPTION);
__ jmp(&exit_label_);
__ bind(&stack_limit_hit);
@@ -794,7 +799,7 @@
// Fill saved registers with initial value = start offset - 1
// Fill in stack push order, to avoid accessing across an unwritten
// page (a problem on Windows).
- __ movq(rcx, Immediate(kRegisterZero));
+ __ Set(rcx, kRegisterZero);
Label init_loop;
__ bind(&init_loop);
__ movq(Operand(rbp, rcx, times_1, 0), rax);
@@ -824,7 +829,7 @@
LoadCurrentCharacterUnchecked(-1, 1); // Load previous char.
__ jmp(&start_label_);
__ bind(&at_start);
- __ movq(current_character(), Immediate('\n'));
+ __ Set(current_character(), '\n');
__ jmp(&start_label_);
@@ -852,7 +857,7 @@
__ movl(Operand(rbx, i * kIntSize), rax);
}
}
- __ movq(rax, Immediate(SUCCESS));
+ __ Set(rax, SUCCESS);
}
// Exit and return rax
@@ -919,16 +924,18 @@
#endif
// Call GrowStack(backtrack_stackpointer())
- static const int num_arguments = 2;
+ static const int num_arguments = 3;
__ PrepareCallCFunction(num_arguments);
#ifdef _WIN64
- // Microsoft passes parameters in rcx, rdx.
+ // Microsoft passes parameters in rcx, rdx, r8.
// First argument, backtrack stackpointer, is already in rcx.
__ lea(rdx, Operand(rbp, kStackHighEnd)); // Second argument
+ __ LoadAddress(r8, ExternalReference::isolate_address());
#else
- // AMD64 ABI passes parameters in rdi, rsi.
+ // AMD64 ABI passes parameters in rdi, rsi, rdx.
__ movq(rdi, backtrack_stackpointer()); // First argument.
__ lea(rsi, Operand(rbp, kStackHighEnd)); // Second argument.
+ __ LoadAddress(rdx, ExternalReference::isolate_address());
#endif
ExternalReference grow_stack =
ExternalReference::re_grow_stack(masm_.isolate());
@@ -952,7 +959,7 @@
// If any of the code above needed to exit with an exception.
__ bind(&exit_with_exception);
// Exit with Result EXCEPTION(-1) to signal thrown exception.
- __ movq(rax, Immediate(EXCEPTION));
+ __ Set(rax, EXCEPTION);
__ jmp(&exit_label_);
}
diff --git a/src/x64/register-allocator-x64-inl.h b/src/x64/register-allocator-x64-inl.h
deleted file mode 100644
index 5df3d54..0000000
--- a/src/x64/register-allocator-x64-inl.h
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_X64_REGISTER_ALLOCATOR_X64_INL_H_
-#define V8_X64_REGISTER_ALLOCATOR_X64_INL_H_
-
-#include "v8.h"
-
-namespace v8 {
-namespace internal {
-
-// -------------------------------------------------------------------------
-// RegisterAllocator implementation.
-
-bool RegisterAllocator::IsReserved(Register reg) {
- return reg.is(rsp) || reg.is(rbp) || reg.is(rsi) ||
- reg.is(kScratchRegister) || reg.is(kRootRegister) ||
- reg.is(kSmiConstantRegister);
-}
-
-
-// The register allocator uses small integers to represent the
-// non-reserved assembler registers.
-int RegisterAllocator::ToNumber(Register reg) {
- ASSERT(reg.is_valid() && !IsReserved(reg));
- const int kNumbers[] = {
- 0, // rax
- 2, // rcx
- 3, // rdx
- 1, // rbx
- -1, // rsp Stack pointer.
- -1, // rbp Frame pointer.
- -1, // rsi Context.
- 4, // rdi
- 5, // r8
- 6, // r9
- -1, // r10 Scratch register.
- 8, // r11
- -1, // r12 Smi constant.
- -1, // r13 Roots array. This is callee saved.
- 7, // r14
- 9 // r15
- };
- return kNumbers[reg.code()];
-}
-
-
-Register RegisterAllocator::ToRegister(int num) {
- ASSERT(num >= 0 && num < kNumRegisters);
- const Register kRegisters[] =
- { rax, rbx, rcx, rdx, rdi, r8, r9, r14, r11, r15 };
- return kRegisters[num];
-}
-
-
-void RegisterAllocator::Initialize() {
- Reset();
- // The non-reserved rdi register is live on JS function entry.
- Use(rdi); // JS function.
-}
-} } // namespace v8::internal
-
-#endif // V8_X64_REGISTER_ALLOCATOR_X64_INL_H_
diff --git a/src/x64/register-allocator-x64.cc b/src/x64/register-allocator-x64.cc
deleted file mode 100644
index 65189f5..0000000
--- a/src/x64/register-allocator-x64.cc
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "v8.h"
-
-#if defined(V8_TARGET_ARCH_X64)
-
-#include "codegen-inl.h"
-#include "register-allocator-inl.h"
-#include "virtual-frame-inl.h"
-
-namespace v8 {
-namespace internal {
-
-// -------------------------------------------------------------------------
-// Result implementation.
-
-void Result::ToRegister() {
- ASSERT(is_valid());
- if (is_constant()) {
- CodeGenerator* code_generator =
- CodeGeneratorScope::Current(Isolate::Current());
- Result fresh = code_generator->allocator()->Allocate();
- ASSERT(fresh.is_valid());
- code_generator->masm()->Move(fresh.reg(), handle());
- // This result becomes a copy of the fresh one.
- fresh.set_type_info(type_info());
- *this = fresh;
- }
- ASSERT(is_register());
-}
-
-
-void Result::ToRegister(Register target) {
- ASSERT(is_valid());
- CodeGenerator* code_generator =
- CodeGeneratorScope::Current(Isolate::Current());
- if (!is_register() || !reg().is(target)) {
- Result fresh = code_generator->allocator()->Allocate(target);
- ASSERT(fresh.is_valid());
- if (is_register()) {
- code_generator->masm()->movq(fresh.reg(), reg());
- } else {
- ASSERT(is_constant());
- code_generator->masm()->Move(fresh.reg(), handle());
- }
- fresh.set_type_info(type_info());
- *this = fresh;
- } else if (is_register() && reg().is(target)) {
- ASSERT(code_generator->has_valid_frame());
- code_generator->frame()->Spill(target);
- ASSERT(code_generator->allocator()->count(target) == 1);
- }
- ASSERT(is_register());
- ASSERT(reg().is(target));
-}
-
-
-// -------------------------------------------------------------------------
-// RegisterAllocator implementation.
-
-Result RegisterAllocator::AllocateByteRegisterWithoutSpilling() {
- // This function is not used in 64-bit code.
- UNREACHABLE();
- return Result();
-}
-
-
-} } // namespace v8::internal
-
-#endif // V8_TARGET_ARCH_X64
diff --git a/src/x64/register-allocator-x64.h b/src/x64/register-allocator-x64.h
deleted file mode 100644
index a2884d9..0000000
--- a/src/x64/register-allocator-x64.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_X64_REGISTER_ALLOCATOR_X64_H_
-#define V8_X64_REGISTER_ALLOCATOR_X64_H_
-
-namespace v8 {
-namespace internal {
-
-class RegisterAllocatorConstants : public AllStatic {
- public:
- static const int kNumRegisters = 10;
- static const int kInvalidRegister = -1;
-};
-
-
-} } // namespace v8::internal
-
-#endif // V8_X64_REGISTER_ALLOCATOR_X64_H_
diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc
index 7494fe0..c19d29d 100644
--- a/src/x64/stub-cache-x64.cc
+++ b/src/x64/stub-cache-x64.cc
@@ -30,7 +30,7 @@
#if defined(V8_TARGET_ARCH_X64)
#include "ic-inl.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "stub-cache.h"
namespace v8 {
@@ -399,7 +399,7 @@
ExternalReference ref =
ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorOnly),
masm->isolate());
- __ movq(rax, Immediate(5));
+ __ Set(rax, 5);
__ LoadAddress(rbx, ref);
CEntryStub stub(1);
diff --git a/src/x64/virtual-frame-x64.cc b/src/x64/virtual-frame-x64.cc
deleted file mode 100644
index 10c327a..0000000
--- a/src/x64/virtual-frame-x64.cc
+++ /dev/null
@@ -1,1296 +0,0 @@
-// Copyright 2011 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "v8.h"
-
-#if defined(V8_TARGET_ARCH_X64)
-
-#include "codegen-inl.h"
-#include "register-allocator-inl.h"
-#include "scopes.h"
-#include "stub-cache.h"
-#include "virtual-frame-inl.h"
-
-namespace v8 {
-namespace internal {
-
-#define __ ACCESS_MASM(masm())
-
-void VirtualFrame::Enter() {
- // Registers live on entry to a JS frame:
- // rsp: stack pointer, points to return address from this function.
- // rbp: base pointer, points to previous JS, ArgumentsAdaptor, or
- // Trampoline frame.
- // rsi: context of this function call.
- // rdi: pointer to this function object.
- Comment cmnt(masm(), "[ Enter JS frame");
-
-#ifdef DEBUG
- if (FLAG_debug_code) {
- // Verify that rdi contains a JS function. The following code
- // relies on rax being available for use.
- Condition not_smi = NegateCondition(masm()->CheckSmi(rdi));
- __ Check(not_smi,
- "VirtualFrame::Enter - rdi is not a function (smi check).");
- __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rax);
- __ Check(equal,
- "VirtualFrame::Enter - rdi is not a function (map check).");
- }
-#endif
-
- EmitPush(rbp);
-
- __ movq(rbp, rsp);
-
- // Store the context in the frame. The context is kept in rsi and a
- // copy is stored in the frame. The external reference to rsi
- // remains.
- EmitPush(rsi);
-
- // Store the function in the frame. The frame owns the register
- // reference now (ie, it can keep it in rdi or spill it later).
- Push(rdi);
- SyncElementAt(element_count() - 1);
- cgen()->allocator()->Unuse(rdi);
-}
-
-
-void VirtualFrame::Exit() {
- Comment cmnt(masm(), "[ Exit JS frame");
- // Record the location of the JS exit code for patching when setting
- // break point.
- __ RecordJSReturn();
-
- // Avoid using the leave instruction here, because it is too
- // short. We need the return sequence to be a least the size of a
- // call instruction to support patching the exit code in the
- // debugger. See GenerateReturnSequence for the full return sequence.
- // TODO(X64): A patched call will be very long now. Make sure we
- // have enough room.
- __ movq(rsp, rbp);
- stack_pointer_ = frame_pointer();
- for (int i = element_count() - 1; i > stack_pointer_; i--) {
- FrameElement last = elements_.RemoveLast();
- if (last.is_register()) {
- Unuse(last.reg());
- }
- }
-
- EmitPop(rbp);
-}
-
-
-void VirtualFrame::AllocateStackSlots() {
- int count = local_count();
- if (count > 0) {
- Comment cmnt(masm(), "[ Allocate space for locals");
- // The locals are initialized to a constant (the undefined value), but
- // we sync them with the actual frame to allocate space for spilling
- // them later. First sync everything above the stack pointer so we can
- // use pushes to allocate and initialize the locals.
- SyncRange(stack_pointer_ + 1, element_count() - 1);
- Handle<Object> undefined = FACTORY->undefined_value();
- FrameElement initial_value =
- FrameElement::ConstantElement(undefined, FrameElement::SYNCED);
- if (count < kLocalVarBound) {
- // For fewer locals the unrolled loop is more compact.
-
- // Hope for one of the first eight registers, where the push operation
- // takes only one byte (kScratchRegister needs the REX.W bit).
- Result tmp = cgen()->allocator()->Allocate();
- ASSERT(tmp.is_valid());
- __ movq(tmp.reg(), undefined, RelocInfo::EMBEDDED_OBJECT);
- for (int i = 0; i < count; i++) {
- __ push(tmp.reg());
- }
- } else {
- // For more locals a loop in generated code is more compact.
- Label alloc_locals_loop;
- Result cnt = cgen()->allocator()->Allocate();
- ASSERT(cnt.is_valid());
- __ movq(kScratchRegister, undefined, RelocInfo::EMBEDDED_OBJECT);
-#ifdef DEBUG
- Label loop_size;
- __ bind(&loop_size);
-#endif
- if (is_uint8(count)) {
- // Loading imm8 is shorter than loading imm32.
- // Loading only partial byte register, and using decb below.
- __ movb(cnt.reg(), Immediate(count));
- } else {
- __ movl(cnt.reg(), Immediate(count));
- }
- __ bind(&alloc_locals_loop);
- __ push(kScratchRegister);
- if (is_uint8(count)) {
- __ decb(cnt.reg());
- } else {
- __ decl(cnt.reg());
- }
- __ j(not_zero, &alloc_locals_loop);
-#ifdef DEBUG
- CHECK(masm()->SizeOfCodeGeneratedSince(&loop_size) < kLocalVarBound);
-#endif
- }
- for (int i = 0; i < count; i++) {
- elements_.Add(initial_value);
- stack_pointer_++;
- }
- }
-}
-
-
-void VirtualFrame::SaveContextRegister() {
- ASSERT(elements_[context_index()].is_memory());
- __ movq(Operand(rbp, fp_relative(context_index())), rsi);
-}
-
-
-void VirtualFrame::RestoreContextRegister() {
- ASSERT(elements_[context_index()].is_memory());
- __ movq(rsi, Operand(rbp, fp_relative(context_index())));
-}
-
-
-void VirtualFrame::PushReceiverSlotAddress() {
- Result temp = cgen()->allocator()->Allocate();
- ASSERT(temp.is_valid());
- __ lea(temp.reg(), ParameterAt(-1));
- Push(&temp);
-}
-
-
-void VirtualFrame::EmitPop(Register reg) {
- ASSERT(stack_pointer_ == element_count() - 1);
- stack_pointer_--;
- elements_.RemoveLast();
- __ pop(reg);
-}
-
-
-void VirtualFrame::EmitPop(const Operand& operand) {
- ASSERT(stack_pointer_ == element_count() - 1);
- stack_pointer_--;
- elements_.RemoveLast();
- __ pop(operand);
-}
-
-
-void VirtualFrame::EmitPush(Register reg, TypeInfo info) {
- ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement(info));
- stack_pointer_++;
- __ push(reg);
-}
-
-
-void VirtualFrame::EmitPush(const Operand& operand, TypeInfo info) {
- ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement(info));
- stack_pointer_++;
- __ push(operand);
-}
-
-
-void VirtualFrame::EmitPush(Immediate immediate, TypeInfo info) {
- ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement(info));
- stack_pointer_++;
- __ push(immediate);
-}
-
-
-void VirtualFrame::EmitPush(Smi* smi_value) {
- ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement(TypeInfo::Smi()));
- stack_pointer_++;
- __ Push(smi_value);
-}
-
-
-void VirtualFrame::EmitPush(Handle<Object> value) {
- ASSERT(stack_pointer_ == element_count() - 1);
- TypeInfo info = TypeInfo::TypeFromValue(value);
- elements_.Add(FrameElement::MemoryElement(info));
- stack_pointer_++;
- __ Push(value);
-}
-
-
-void VirtualFrame::EmitPush(Heap::RootListIndex index, TypeInfo info) {
- ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement(info));
- stack_pointer_++;
- __ PushRoot(index);
-}
-
-
-void VirtualFrame::Push(Expression* expr) {
- ASSERT(expr->IsTrivial());
-
- Literal* lit = expr->AsLiteral();
- if (lit != NULL) {
- Push(lit->handle());
- return;
- }
-
- VariableProxy* proxy = expr->AsVariableProxy();
- if (proxy != NULL) {
- Slot* slot = proxy->var()->AsSlot();
- if (slot->type() == Slot::LOCAL) {
- PushLocalAt(slot->index());
- return;
- }
- if (slot->type() == Slot::PARAMETER) {
- PushParameterAt(slot->index());
- return;
- }
- }
- UNREACHABLE();
-}
-
-
-void VirtualFrame::Push(Handle<Object> value) {
- if (ConstantPoolOverflowed()) {
- Result temp = cgen()->allocator()->Allocate();
- ASSERT(temp.is_valid());
- if (value->IsSmi()) {
- __ Move(temp.reg(), Smi::cast(*value));
- } else {
- __ movq(temp.reg(), value, RelocInfo::EMBEDDED_OBJECT);
- }
- Push(&temp);
- } else {
- FrameElement element =
- FrameElement::ConstantElement(value, FrameElement::NOT_SYNCED);
- elements_.Add(element);
- }
-}
-
-
-void VirtualFrame::Drop(int count) {
- ASSERT(count >= 0);
- ASSERT(height() >= count);
- int num_virtual_elements = (element_count() - 1) - stack_pointer_;
-
- // Emit code to lower the stack pointer if necessary.
- if (num_virtual_elements < count) {
- int num_dropped = count - num_virtual_elements;
- stack_pointer_ -= num_dropped;
- __ addq(rsp, Immediate(num_dropped * kPointerSize));
- }
-
- // Discard elements from the virtual frame and free any registers.
- for (int i = 0; i < count; i++) {
- FrameElement dropped = elements_.RemoveLast();
- if (dropped.is_register()) {
- Unuse(dropped.reg());
- }
- }
-}
-
-
-int VirtualFrame::InvalidateFrameSlotAt(int index) {
- FrameElement original = elements_[index];
-
- // Is this element the backing store of any copies?
- int new_backing_index = kIllegalIndex;
- if (original.is_copied()) {
- // Verify it is copied, and find first copy.
- for (int i = index + 1; i < element_count(); i++) {
- if (elements_[i].is_copy() && elements_[i].index() == index) {
- new_backing_index = i;
- break;
- }
- }
- }
-
- if (new_backing_index == kIllegalIndex) {
- // No copies found, return kIllegalIndex.
- if (original.is_register()) {
- Unuse(original.reg());
- }
- elements_[index] = FrameElement::InvalidElement();
- return kIllegalIndex;
- }
-
- // This is the backing store of copies.
- Register backing_reg;
- if (original.is_memory()) {
- Result fresh = cgen()->allocator()->Allocate();
- ASSERT(fresh.is_valid());
- Use(fresh.reg(), new_backing_index);
- backing_reg = fresh.reg();
- __ movq(backing_reg, Operand(rbp, fp_relative(index)));
- } else {
- // The original was in a register.
- backing_reg = original.reg();
- set_register_location(backing_reg, new_backing_index);
- }
- // Invalidate the element at index.
- elements_[index] = FrameElement::InvalidElement();
- // Set the new backing element.
- if (elements_[new_backing_index].is_synced()) {
- elements_[new_backing_index] =
- FrameElement::RegisterElement(backing_reg,
- FrameElement::SYNCED,
- original.type_info());
- } else {
- elements_[new_backing_index] =
- FrameElement::RegisterElement(backing_reg,
- FrameElement::NOT_SYNCED,
- original.type_info());
- }
- // Update the other copies.
- for (int i = new_backing_index + 1; i < element_count(); i++) {
- if (elements_[i].is_copy() && elements_[i].index() == index) {
- elements_[i].set_index(new_backing_index);
- elements_[new_backing_index].set_copied();
- }
- }
- return new_backing_index;
-}
-
-
-void VirtualFrame::TakeFrameSlotAt(int index) {
- ASSERT(index >= 0);
- ASSERT(index <= element_count());
- FrameElement original = elements_[index];
- int new_backing_store_index = InvalidateFrameSlotAt(index);
- if (new_backing_store_index != kIllegalIndex) {
- elements_.Add(CopyElementAt(new_backing_store_index));
- return;
- }
-
- switch (original.type()) {
- case FrameElement::MEMORY: {
- // Emit code to load the original element's data into a register.
- // Push that register as a FrameElement on top of the frame.
- Result fresh = cgen()->allocator()->Allocate();
- ASSERT(fresh.is_valid());
- FrameElement new_element =
- FrameElement::RegisterElement(fresh.reg(),
- FrameElement::NOT_SYNCED,
- original.type_info());
- Use(fresh.reg(), element_count());
- elements_.Add(new_element);
- __ movq(fresh.reg(), Operand(rbp, fp_relative(index)));
- break;
- }
- case FrameElement::REGISTER:
- Use(original.reg(), element_count());
- // Fall through.
- case FrameElement::CONSTANT:
- case FrameElement::COPY:
- original.clear_sync();
- elements_.Add(original);
- break;
- case FrameElement::INVALID:
- UNREACHABLE();
- break;
- }
-}
-
-
-void VirtualFrame::StoreToFrameSlotAt(int index) {
- // Store the value on top of the frame to the virtual frame slot at
- // a given index. The value on top of the frame is left in place.
- // This is a duplicating operation, so it can create copies.
- ASSERT(index >= 0);
- ASSERT(index < element_count());
-
- int top_index = element_count() - 1;
- FrameElement top = elements_[top_index];
- FrameElement original = elements_[index];
- if (top.is_copy() && top.index() == index) return;
- ASSERT(top.is_valid());
-
- InvalidateFrameSlotAt(index);
-
- // InvalidateFrameSlotAt can potentially change any frame element, due
- // to spilling registers to allocate temporaries in order to preserve
- // the copy-on-write semantics of aliased elements. Reload top from
- // the frame.
- top = elements_[top_index];
-
- if (top.is_copy()) {
- // There are two cases based on the relative positions of the
- // stored-to slot and the backing slot of the top element.
- int backing_index = top.index();
- ASSERT(backing_index != index);
- if (backing_index < index) {
- // 1. The top element is a copy of a slot below the stored-to
- // slot. The stored-to slot becomes an unsynced copy of that
- // same backing slot.
- elements_[index] = CopyElementAt(backing_index);
- } else {
- // 2. The top element is a copy of a slot above the stored-to
- // slot. The stored-to slot becomes the new (unsynced) backing
- // slot and both the top element and the element at the former
- // backing slot become copies of it. The sync state of the top
- // and former backing elements is preserved.
- FrameElement backing_element = elements_[backing_index];
- ASSERT(backing_element.is_memory() || backing_element.is_register());
- if (backing_element.is_memory()) {
- // Because sets of copies are canonicalized to be backed by
- // their lowest frame element, and because memory frame
- // elements are backed by the corresponding stack address, we
- // have to move the actual value down in the stack.
- //
- // TODO(209): considering allocating the stored-to slot to the
- // temp register. Alternatively, allow copies to appear in
- // any order in the frame and lazily move the value down to
- // the slot.
- __ movq(kScratchRegister, Operand(rbp, fp_relative(backing_index)));
- __ movq(Operand(rbp, fp_relative(index)), kScratchRegister);
- } else {
- set_register_location(backing_element.reg(), index);
- if (backing_element.is_synced()) {
- // If the element is a register, we will not actually move
- // anything on the stack but only update the virtual frame
- // element.
- backing_element.clear_sync();
- }
- }
- elements_[index] = backing_element;
-
- // The old backing element becomes a copy of the new backing
- // element.
- FrameElement new_element = CopyElementAt(index);
- elements_[backing_index] = new_element;
- if (backing_element.is_synced()) {
- elements_[backing_index].set_sync();
- }
-
- // All the copies of the old backing element (including the top
- // element) become copies of the new backing element.
- for (int i = backing_index + 1; i < element_count(); i++) {
- if (elements_[i].is_copy() && elements_[i].index() == backing_index) {
- elements_[i].set_index(index);
- }
- }
- }
- return;
- }
-
- // Move the top element to the stored-to slot and replace it (the
- // top element) with a copy.
- elements_[index] = top;
- if (top.is_memory()) {
- // TODO(209): consider allocating the stored-to slot to the temp
- // register. Alternatively, allow copies to appear in any order
- // in the frame and lazily move the value down to the slot.
- FrameElement new_top = CopyElementAt(index);
- new_top.set_sync();
- elements_[top_index] = new_top;
-
- // The sync state of the former top element is correct (synced).
- // Emit code to move the value down in the frame.
- __ movq(kScratchRegister, Operand(rsp, 0));
- __ movq(Operand(rbp, fp_relative(index)), kScratchRegister);
- } else if (top.is_register()) {
- set_register_location(top.reg(), index);
- // The stored-to slot has the (unsynced) register reference and
- // the top element becomes a copy. The sync state of the top is
- // preserved.
- FrameElement new_top = CopyElementAt(index);
- if (top.is_synced()) {
- new_top.set_sync();
- elements_[index].clear_sync();
- }
- elements_[top_index] = new_top;
- } else {
- // The stored-to slot holds the same value as the top but
- // unsynced. (We do not have copies of constants yet.)
- ASSERT(top.is_constant());
- elements_[index].clear_sync();
- }
-}
-
-
-void VirtualFrame::MakeMergable() {
- for (int i = 0; i < element_count(); i++) {
- FrameElement element = elements_[i];
-
- // In all cases we have to reset the number type information
- // to unknown for a mergable frame because of incoming back edges.
- if (element.is_constant() || element.is_copy()) {
- if (element.is_synced()) {
- // Just spill.
- elements_[i] = FrameElement::MemoryElement(TypeInfo::Unknown());
- } else {
- // Allocate to a register.
- FrameElement backing_element; // Invalid if not a copy.
- if (element.is_copy()) {
- backing_element = elements_[element.index()];
- }
- Result fresh = cgen()->allocator()->Allocate();
- ASSERT(fresh.is_valid()); // A register was spilled if all were in use.
- elements_[i] =
- FrameElement::RegisterElement(fresh.reg(),
- FrameElement::NOT_SYNCED,
- TypeInfo::Unknown());
- Use(fresh.reg(), i);
-
- // Emit a move.
- if (element.is_constant()) {
- __ Move(fresh.reg(), element.handle());
- } else {
- ASSERT(element.is_copy());
- // Copies are only backed by register or memory locations.
- if (backing_element.is_register()) {
- // The backing store may have been spilled by allocating,
- // but that's OK. If it was, the value is right where we
- // want it.
- if (!fresh.reg().is(backing_element.reg())) {
- __ movq(fresh.reg(), backing_element.reg());
- }
- } else {
- ASSERT(backing_element.is_memory());
- __ movq(fresh.reg(), Operand(rbp, fp_relative(element.index())));
- }
- }
- }
- // No need to set the copied flag --- there are no copies.
- } else {
- // Clear the copy flag of non-constant, non-copy elements.
- // They cannot be copied because copies are not allowed.
- // The copy flag is not relied on before the end of this loop,
- // including when registers are spilled.
- elements_[i].clear_copied();
- elements_[i].set_type_info(TypeInfo::Unknown());
- }
- }
-}
-
-
-void VirtualFrame::MergeTo(VirtualFrame* expected) {
- Comment cmnt(masm(), "[ Merge frame");
- // We should always be merging the code generator's current frame to an
- // expected frame.
- ASSERT(cgen()->frame() == this);
-
- // Adjust the stack pointer upward (toward the top of the virtual
- // frame) if necessary.
- if (stack_pointer_ < expected->stack_pointer_) {
- int difference = expected->stack_pointer_ - stack_pointer_;
- stack_pointer_ = expected->stack_pointer_;
- __ subq(rsp, Immediate(difference * kPointerSize));
- }
-
- MergeMoveRegistersToMemory(expected);
- MergeMoveRegistersToRegisters(expected);
- MergeMoveMemoryToRegisters(expected);
-
- // Adjust the stack pointer downward if necessary.
- if (stack_pointer_ > expected->stack_pointer_) {
- int difference = stack_pointer_ - expected->stack_pointer_;
- stack_pointer_ = expected->stack_pointer_;
- __ addq(rsp, Immediate(difference * kPointerSize));
- }
-
- // At this point, the frames should be identical.
- ASSERT(Equals(expected));
-}
-
-
-void VirtualFrame::MergeMoveRegistersToMemory(VirtualFrame* expected) {
- ASSERT(stack_pointer_ >= expected->stack_pointer_);
-
- // Move registers, constants, and copies to memory. Perform moves
- // from the top downward in the frame in order to leave the backing
- // stores of copies in registers.
- for (int i = element_count() - 1; i >= 0; i--) {
- FrameElement target = expected->elements_[i];
- if (target.is_register()) continue; // Handle registers later.
- if (target.is_memory()) {
- FrameElement source = elements_[i];
- switch (source.type()) {
- case FrameElement::INVALID:
- // Not a legal merge move.
- UNREACHABLE();
- break;
-
- case FrameElement::MEMORY:
- // Already in place.
- break;
-
- case FrameElement::REGISTER:
- Unuse(source.reg());
- if (!source.is_synced()) {
- __ movq(Operand(rbp, fp_relative(i)), source.reg());
- }
- break;
-
- case FrameElement::CONSTANT:
- if (!source.is_synced()) {
- __ Move(Operand(rbp, fp_relative(i)), source.handle());
- }
- break;
-
- case FrameElement::COPY:
- if (!source.is_synced()) {
- int backing_index = source.index();
- FrameElement backing_element = elements_[backing_index];
- if (backing_element.is_memory()) {
- __ movq(kScratchRegister,
- Operand(rbp, fp_relative(backing_index)));
- __ movq(Operand(rbp, fp_relative(i)), kScratchRegister);
- } else {
- ASSERT(backing_element.is_register());
- __ movq(Operand(rbp, fp_relative(i)), backing_element.reg());
- }
- }
- break;
- }
- }
- elements_[i] = target;
- }
-}
-
-
-void VirtualFrame::MergeMoveRegistersToRegisters(VirtualFrame* expected) {
- // We have already done X-to-memory moves.
- ASSERT(stack_pointer_ >= expected->stack_pointer_);
-
- for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
- // Move the right value into register i if it is currently in a register.
- int index = expected->register_location(i);
- int use_index = register_location(i);
- // Skip if register i is unused in the target or else if source is
- // not a register (this is not a register-to-register move).
- if (index == kIllegalIndex || !elements_[index].is_register()) continue;
-
- Register target = RegisterAllocator::ToRegister(i);
- Register source = elements_[index].reg();
- if (index != use_index) {
- if (use_index == kIllegalIndex) { // Target is currently unused.
- // Copy contents of source from source to target.
- // Set frame element register to target.
- Use(target, index);
- Unuse(source);
- __ movq(target, source);
- } else {
- // Exchange contents of registers source and target.
- // Nothing except the register backing use_index has changed.
- elements_[use_index].set_reg(source);
- set_register_location(target, index);
- set_register_location(source, use_index);
- __ xchg(source, target);
- }
- }
-
- if (!elements_[index].is_synced() &&
- expected->elements_[index].is_synced()) {
- __ movq(Operand(rbp, fp_relative(index)), target);
- }
- elements_[index] = expected->elements_[index];
- }
-}
-
-
-void VirtualFrame::MergeMoveMemoryToRegisters(VirtualFrame* expected) {
- // Move memory, constants, and copies to registers. This is the
- // final step and since it is not done from the bottom up, but in
- // register code order, we have special code to ensure that the backing
- // elements of copies are in their correct locations when we
- // encounter the copies.
- for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
- int index = expected->register_location(i);
- if (index != kIllegalIndex) {
- FrameElement source = elements_[index];
- FrameElement target = expected->elements_[index];
- Register target_reg = RegisterAllocator::ToRegister(i);
- ASSERT(target.reg().is(target_reg));
- switch (source.type()) {
- case FrameElement::INVALID: // Fall through.
- UNREACHABLE();
- break;
- case FrameElement::REGISTER:
- ASSERT(source.Equals(target));
- // Go to next iteration. Skips Use(target_reg) and syncing
- // below. It is safe to skip syncing because a target
- // register frame element would only be synced if all source
- // elements were.
- continue;
- break;
- case FrameElement::MEMORY:
- ASSERT(index <= stack_pointer_);
- __ movq(target_reg, Operand(rbp, fp_relative(index)));
- break;
-
- case FrameElement::CONSTANT:
- __ Move(target_reg, source.handle());
- break;
-
- case FrameElement::COPY: {
- int backing_index = source.index();
- FrameElement backing = elements_[backing_index];
- ASSERT(backing.is_memory() || backing.is_register());
- if (backing.is_memory()) {
- ASSERT(backing_index <= stack_pointer_);
- // Code optimization if backing store should also move
- // to a register: move backing store to its register first.
- if (expected->elements_[backing_index].is_register()) {
- FrameElement new_backing = expected->elements_[backing_index];
- Register new_backing_reg = new_backing.reg();
- ASSERT(!is_used(new_backing_reg));
- elements_[backing_index] = new_backing;
- Use(new_backing_reg, backing_index);
- __ movq(new_backing_reg,
- Operand(rbp, fp_relative(backing_index)));
- __ movq(target_reg, new_backing_reg);
- } else {
- __ movq(target_reg, Operand(rbp, fp_relative(backing_index)));
- }
- } else {
- __ movq(target_reg, backing.reg());
- }
- }
- }
- // Ensure the proper sync state.
- if (target.is_synced() && !source.is_synced()) {
- __ movq(Operand(rbp, fp_relative(index)), target_reg);
- }
- Use(target_reg, index);
- elements_[index] = target;
- }
- }
-}
-
-
-Result VirtualFrame::Pop() {
- FrameElement element = elements_.RemoveLast();
- int index = element_count();
- ASSERT(element.is_valid());
-
- // Get number type information of the result.
- TypeInfo info;
- if (!element.is_copy()) {
- info = element.type_info();
- } else {
- info = elements_[element.index()].type_info();
- }
-
- bool pop_needed = (stack_pointer_ == index);
- if (pop_needed) {
- stack_pointer_--;
- if (element.is_memory()) {
- Result temp = cgen()->allocator()->Allocate();
- ASSERT(temp.is_valid());
- __ pop(temp.reg());
- temp.set_type_info(info);
- return temp;
- }
-
- __ addq(rsp, Immediate(kPointerSize));
- }
- ASSERT(!element.is_memory());
-
- // The top element is a register, constant, or a copy. Unuse
- // registers and follow copies to their backing store.
- if (element.is_register()) {
- Unuse(element.reg());
- } else if (element.is_copy()) {
- ASSERT(element.index() < index);
- index = element.index();
- element = elements_[index];
- }
- ASSERT(!element.is_copy());
-
- // The element is memory, a register, or a constant.
- if (element.is_memory()) {
- // Memory elements could only be the backing store of a copy.
- // Allocate the original to a register.
- ASSERT(index <= stack_pointer_);
- Result temp = cgen()->allocator()->Allocate();
- ASSERT(temp.is_valid());
- Use(temp.reg(), index);
- FrameElement new_element =
- FrameElement::RegisterElement(temp.reg(),
- FrameElement::SYNCED,
- element.type_info());
- // Preserve the copy flag on the element.
- if (element.is_copied()) new_element.set_copied();
- elements_[index] = new_element;
- __ movq(temp.reg(), Operand(rbp, fp_relative(index)));
- return Result(temp.reg(), info);
- } else if (element.is_register()) {
- return Result(element.reg(), info);
- } else {
- ASSERT(element.is_constant());
- return Result(element.handle());
- }
-}
-
-
-Result VirtualFrame::RawCallStub(CodeStub* stub) {
- ASSERT(cgen()->HasValidEntryRegisters());
- __ CallStub(stub);
- Result result = cgen()->allocator()->Allocate(rax);
- ASSERT(result.is_valid());
- return result;
-}
-
-
-Result VirtualFrame::CallStub(CodeStub* stub, Result* arg) {
- PrepareForCall(0, 0);
- arg->ToRegister(rax);
- arg->Unuse();
- return RawCallStub(stub);
-}
-
-
-Result VirtualFrame::CallStub(CodeStub* stub, Result* arg0, Result* arg1) {
- PrepareForCall(0, 0);
-
- if (arg0->is_register() && arg0->reg().is(rax)) {
- if (arg1->is_register() && arg1->reg().is(rdx)) {
- // Wrong registers.
- __ xchg(rax, rdx);
- } else {
- // Register rdx is free for arg0, which frees rax for arg1.
- arg0->ToRegister(rdx);
- arg1->ToRegister(rax);
- }
- } else {
- // Register rax is free for arg1, which guarantees rdx is free for
- // arg0.
- arg1->ToRegister(rax);
- arg0->ToRegister(rdx);
- }
-
- arg0->Unuse();
- arg1->Unuse();
- return RawCallStub(stub);
-}
-
-
-Result VirtualFrame::CallJSFunction(int arg_count) {
- Result function = Pop();
-
- // InvokeFunction requires function in rdi. Move it in there.
- function.ToRegister(rdi);
- function.Unuse();
-
- // +1 for receiver.
- PrepareForCall(arg_count + 1, arg_count + 1);
- ASSERT(cgen()->HasValidEntryRegisters());
- ParameterCount count(arg_count);
- __ InvokeFunction(rdi, count, CALL_FUNCTION);
- RestoreContextRegister();
- Result result = cgen()->allocator()->Allocate(rax);
- ASSERT(result.is_valid());
- return result;
-}
-
-
-void VirtualFrame::SyncElementBelowStackPointer(int index) {
- // Emit code to write elements below the stack pointer to their
- // (already allocated) stack address.
- ASSERT(index <= stack_pointer_);
- FrameElement element = elements_[index];
- ASSERT(!element.is_synced());
- switch (element.type()) {
- case FrameElement::INVALID:
- break;
-
- case FrameElement::MEMORY:
- // This function should not be called with synced elements.
- // (memory elements are always synced).
- UNREACHABLE();
- break;
-
- case FrameElement::REGISTER:
- __ movq(Operand(rbp, fp_relative(index)), element.reg());
- break;
-
- case FrameElement::CONSTANT:
- __ Move(Operand(rbp, fp_relative(index)), element.handle());
- break;
-
- case FrameElement::COPY: {
- int backing_index = element.index();
- FrameElement backing_element = elements_[backing_index];
- if (backing_element.is_memory()) {
- __ movq(kScratchRegister, Operand(rbp, fp_relative(backing_index)));
- __ movq(Operand(rbp, fp_relative(index)), kScratchRegister);
- } else {
- ASSERT(backing_element.is_register());
- __ movq(Operand(rbp, fp_relative(index)), backing_element.reg());
- }
- break;
- }
- }
- elements_[index].set_sync();
-}
-
-
-void VirtualFrame::SyncElementByPushing(int index) {
- // Sync an element of the frame that is just above the stack pointer
- // by pushing it.
- ASSERT(index == stack_pointer_ + 1);
- stack_pointer_++;
- FrameElement element = elements_[index];
-
- switch (element.type()) {
- case FrameElement::INVALID:
- __ Push(Smi::FromInt(0));
- break;
-
- case FrameElement::MEMORY:
- // No memory elements exist above the stack pointer.
- UNREACHABLE();
- break;
-
- case FrameElement::REGISTER:
- __ push(element.reg());
- break;
-
- case FrameElement::CONSTANT:
- __ Move(kScratchRegister, element.handle());
- __ push(kScratchRegister);
- break;
-
- case FrameElement::COPY: {
- int backing_index = element.index();
- FrameElement backing = elements_[backing_index];
- ASSERT(backing.is_memory() || backing.is_register());
- if (backing.is_memory()) {
- __ push(Operand(rbp, fp_relative(backing_index)));
- } else {
- __ push(backing.reg());
- }
- break;
- }
- }
- elements_[index].set_sync();
-}
-
-
-// Clear the dirty bits for the range of elements in
-// [min(stack_pointer_ + 1,begin), end].
-void VirtualFrame::SyncRange(int begin, int end) {
- ASSERT(begin >= 0);
- ASSERT(end < element_count());
- // Sync elements below the range if they have not been materialized
- // on the stack.
- int start = Min(begin, stack_pointer_ + 1);
- int end_or_stack_pointer = Min(stack_pointer_, end);
- // Emit normal push instructions for elements above stack pointer
- // and use mov instructions if we are below stack pointer.
- int i = start;
-
- while (i <= end_or_stack_pointer) {
- if (!elements_[i].is_synced()) SyncElementBelowStackPointer(i);
- i++;
- }
- while (i <= end) {
- SyncElementByPushing(i);
- i++;
- }
-}
-
-
-//------------------------------------------------------------------------------
-// Virtual frame stub and IC calling functions.
-
-Result VirtualFrame::CallRuntime(const Runtime::Function* f, int arg_count) {
- PrepareForCall(arg_count, arg_count);
- ASSERT(cgen()->HasValidEntryRegisters());
- __ CallRuntime(f, arg_count);
- Result result = cgen()->allocator()->Allocate(rax);
- ASSERT(result.is_valid());
- return result;
-}
-
-
-Result VirtualFrame::CallRuntime(Runtime::FunctionId id, int arg_count) {
- PrepareForCall(arg_count, arg_count);
- ASSERT(cgen()->HasValidEntryRegisters());
- __ CallRuntime(id, arg_count);
- Result result = cgen()->allocator()->Allocate(rax);
- ASSERT(result.is_valid());
- return result;
-}
-
-
-#ifdef ENABLE_DEBUGGER_SUPPORT
-void VirtualFrame::DebugBreak() {
- PrepareForCall(0, 0);
- ASSERT(cgen()->HasValidEntryRegisters());
- __ DebugBreak();
- Result result = cgen()->allocator()->Allocate(rax);
- ASSERT(result.is_valid());
-}
-#endif
-
-
-Result VirtualFrame::InvokeBuiltin(Builtins::JavaScript id,
- InvokeFlag flag,
- int arg_count) {
- PrepareForCall(arg_count, arg_count);
- ASSERT(cgen()->HasValidEntryRegisters());
- __ InvokeBuiltin(id, flag);
- Result result = cgen()->allocator()->Allocate(rax);
- ASSERT(result.is_valid());
- return result;
-}
-
-
-Result VirtualFrame::RawCallCodeObject(Handle<Code> code,
- RelocInfo::Mode rmode) {
- ASSERT(cgen()->HasValidEntryRegisters());
- __ Call(code, rmode);
- Result result = cgen()->allocator()->Allocate(rax);
- ASSERT(result.is_valid());
- return result;
-}
-
-
-// This function assumes that the only results that could be in a_reg or b_reg
-// are a and b. Other results can be live, but must not be in a_reg or b_reg.
-void VirtualFrame::MoveResultsToRegisters(Result* a,
- Result* b,
- Register a_reg,
- Register b_reg) {
- ASSERT(!a_reg.is(b_reg));
- // Assert that cgen()->allocator()->count(a_reg) is accounted for by a and b.
- ASSERT(cgen()->allocator()->count(a_reg) <= 2);
- ASSERT(cgen()->allocator()->count(a_reg) != 2 || a->reg().is(a_reg));
- ASSERT(cgen()->allocator()->count(a_reg) != 2 || b->reg().is(a_reg));
- ASSERT(cgen()->allocator()->count(a_reg) != 1 ||
- (a->is_register() && a->reg().is(a_reg)) ||
- (b->is_register() && b->reg().is(a_reg)));
- // Assert that cgen()->allocator()->count(b_reg) is accounted for by a and b.
- ASSERT(cgen()->allocator()->count(b_reg) <= 2);
- ASSERT(cgen()->allocator()->count(b_reg) != 2 || a->reg().is(b_reg));
- ASSERT(cgen()->allocator()->count(b_reg) != 2 || b->reg().is(b_reg));
- ASSERT(cgen()->allocator()->count(b_reg) != 1 ||
- (a->is_register() && a->reg().is(b_reg)) ||
- (b->is_register() && b->reg().is(b_reg)));
-
- if (a->is_register() && a->reg().is(a_reg)) {
- b->ToRegister(b_reg);
- } else if (!cgen()->allocator()->is_used(a_reg)) {
- a->ToRegister(a_reg);
- b->ToRegister(b_reg);
- } else if (cgen()->allocator()->is_used(b_reg)) {
- // a must be in b_reg, b in a_reg.
- __ xchg(a_reg, b_reg);
- // Results a and b will be invalidated, so it is ok if they are switched.
- } else {
- b->ToRegister(b_reg);
- a->ToRegister(a_reg);
- }
- a->Unuse();
- b->Unuse();
-}
-
-
-Result VirtualFrame::CallLoadIC(RelocInfo::Mode mode) {
- // Name and receiver are on the top of the frame. Both are dropped.
- // The IC expects name in rcx and receiver in rax.
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- Builtins::kLoadIC_Initialize));
- Result name = Pop();
- Result receiver = Pop();
- PrepareForCall(0, 0);
- MoveResultsToRegisters(&name, &receiver, rcx, rax);
-
- return RawCallCodeObject(ic, mode);
-}
-
-
-Result VirtualFrame::CallKeyedLoadIC(RelocInfo::Mode mode) {
- // Key and receiver are on top of the frame. Put them in rax and rdx.
- Result key = Pop();
- Result receiver = Pop();
- PrepareForCall(0, 0);
- MoveResultsToRegisters(&key, &receiver, rax, rdx);
-
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- Builtins::kKeyedLoadIC_Initialize));
- return RawCallCodeObject(ic, mode);
-}
-
-
-Result VirtualFrame::CallStoreIC(Handle<String> name,
- bool is_contextual,
- StrictModeFlag strict_mode) {
- // Value and (if not contextual) receiver are on top of the frame.
- // The IC expects name in rcx, value in rax, and receiver in rdx.
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- (strict_mode == kStrictMode) ? Builtins::kStoreIC_Initialize_Strict
- : Builtins::kStoreIC_Initialize));
- Result value = Pop();
- RelocInfo::Mode mode;
- if (is_contextual) {
- PrepareForCall(0, 0);
- value.ToRegister(rax);
- __ movq(rdx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
- value.Unuse();
- mode = RelocInfo::CODE_TARGET_CONTEXT;
- } else {
- Result receiver = Pop();
- PrepareForCall(0, 0);
- MoveResultsToRegisters(&value, &receiver, rax, rdx);
- mode = RelocInfo::CODE_TARGET;
- }
- __ Move(rcx, name);
- return RawCallCodeObject(ic, mode);
-}
-
-
-Result VirtualFrame::CallKeyedStoreIC(StrictModeFlag strict_mode) {
- // Value, key, and receiver are on the top of the frame. The IC
- // expects value in rax, key in rcx, and receiver in rdx.
- Result value = Pop();
- Result key = Pop();
- Result receiver = Pop();
- PrepareForCall(0, 0);
- if (!cgen()->allocator()->is_used(rax) ||
- (value.is_register() && value.reg().is(rax))) {
- if (!cgen()->allocator()->is_used(rax)) {
- value.ToRegister(rax);
- }
- MoveResultsToRegisters(&key, &receiver, rcx, rdx);
- value.Unuse();
- } else if (!cgen()->allocator()->is_used(rcx) ||
- (key.is_register() && key.reg().is(rcx))) {
- if (!cgen()->allocator()->is_used(rcx)) {
- key.ToRegister(rcx);
- }
- MoveResultsToRegisters(&value, &receiver, rax, rdx);
- key.Unuse();
- } else if (!cgen()->allocator()->is_used(rdx) ||
- (receiver.is_register() && receiver.reg().is(rdx))) {
- if (!cgen()->allocator()->is_used(rdx)) {
- receiver.ToRegister(rdx);
- }
- MoveResultsToRegisters(&key, &value, rcx, rax);
- receiver.Unuse();
- } else {
- // All three registers are used, and no value is in the correct place.
- // We have one of the two circular permutations of rax, rcx, rdx.
- ASSERT(value.is_register());
- if (value.reg().is(rcx)) {
- __ xchg(rax, rdx);
- __ xchg(rax, rcx);
- } else {
- __ xchg(rax, rcx);
- __ xchg(rax, rdx);
- }
- value.Unuse();
- key.Unuse();
- receiver.Unuse();
- }
-
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- (strict_mode == kStrictMode) ? Builtins::kKeyedStoreIC_Initialize_Strict
- : Builtins::kKeyedStoreIC_Initialize));
- return RawCallCodeObject(ic, RelocInfo::CODE_TARGET);
-}
-
-
-Result VirtualFrame::CallCallIC(RelocInfo::Mode mode,
- int arg_count,
- int loop_nesting) {
- // Function name, arguments, and receiver are found on top of the frame
- // and dropped by the call. The IC expects the name in rcx and the rest
- // on the stack, and drops them all.
- InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP;
- Handle<Code> ic =
- ISOLATE->stub_cache()->ComputeCallInitialize(arg_count, in_loop);
- Result name = Pop();
- // Spill args, receiver, and function. The call will drop args and
- // receiver.
- PrepareForCall(arg_count + 1, arg_count + 1);
- name.ToRegister(rcx);
- name.Unuse();
- return RawCallCodeObject(ic, mode);
-}
-
-
-Result VirtualFrame::CallKeyedCallIC(RelocInfo::Mode mode,
- int arg_count,
- int loop_nesting) {
- // Function name, arguments, and receiver are found on top of the frame
- // and dropped by the call. The IC expects the name in rcx and the rest
- // on the stack, and drops them all.
- InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP;
- Handle<Code> ic =
- ISOLATE->stub_cache()->ComputeKeyedCallInitialize(arg_count, in_loop);
- Result name = Pop();
- // Spill args, receiver, and function. The call will drop args and
- // receiver.
- PrepareForCall(arg_count + 1, arg_count + 1);
- name.ToRegister(rcx);
- name.Unuse();
- return RawCallCodeObject(ic, mode);
-}
-
-
-Result VirtualFrame::CallConstructor(int arg_count) {
- // Arguments, receiver, and function are on top of the frame. The
- // IC expects arg count in rax, function in rdi, and the arguments
- // and receiver on the stack.
- Handle<Code> ic(Isolate::Current()->builtins()->builtin(
- Builtins::kJSConstructCall));
- // Duplicate the function before preparing the frame.
- PushElementAt(arg_count);
- Result function = Pop();
- PrepareForCall(arg_count + 1, arg_count + 1); // Spill function and args.
- function.ToRegister(rdi);
-
- // Constructors are called with the number of arguments in register
- // rax for now. Another option would be to have separate construct
- // call trampolines per different arguments counts encountered.
- Result num_args = cgen()->allocator()->Allocate(rax);
- ASSERT(num_args.is_valid());
- __ Set(num_args.reg(), arg_count);
-
- function.Unuse();
- num_args.Unuse();
- return RawCallCodeObject(ic, RelocInfo::CONSTRUCT_CALL);
-}
-
-
-void VirtualFrame::PushTryHandler(HandlerType type) {
- ASSERT(cgen()->HasValidEntryRegisters());
- // Grow the expression stack by handler size less one (the return
- // address is already pushed by a call instruction).
- Adjust(kHandlerSize - 1);
- __ PushTryHandler(IN_JAVASCRIPT, type);
-}
-
-
-#undef __
-
-} } // namespace v8::internal
-
-#endif // V8_TARGET_ARCH_X64
diff --git a/src/x64/virtual-frame-x64.h b/src/x64/virtual-frame-x64.h
deleted file mode 100644
index aac9864..0000000
--- a/src/x64/virtual-frame-x64.h
+++ /dev/null
@@ -1,597 +0,0 @@
-// Copyright 2011 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_X64_VIRTUAL_FRAME_X64_H_
-#define V8_X64_VIRTUAL_FRAME_X64_H_
-
-#include "type-info.h"
-#include "register-allocator.h"
-#include "scopes.h"
-#include "codegen.h"
-
-namespace v8 {
-namespace internal {
-
-// -------------------------------------------------------------------------
-// Virtual frames
-//
-// The virtual frame is an abstraction of the physical stack frame. It
-// encapsulates the parameters, frame-allocated locals, and the expression
-// stack. It supports push/pop operations on the expression stack, as well
-// as random access to the expression stack elements, locals, and
-// parameters.
-
-class VirtualFrame : public ZoneObject {
- public:
- // A utility class to introduce a scope where the virtual frame is
- // expected to remain spilled. The constructor spills the code
- // generator's current frame, but no attempt is made to require it
- // to stay spilled. It is intended as documentation while the code
- // generator is being transformed.
- class SpilledScope BASE_EMBEDDED {
- public:
- SpilledScope() : previous_state_(cgen()->in_spilled_code()) {
- ASSERT(cgen()->has_valid_frame());
- cgen()->frame()->SpillAll();
- cgen()->set_in_spilled_code(true);
- }
-
- ~SpilledScope() {
- cgen()->set_in_spilled_code(previous_state_);
- }
-
- private:
- bool previous_state_;
-
- CodeGenerator* cgen() {
- return CodeGeneratorScope::Current(Isolate::Current());
- }
- };
-
- // An illegal index into the virtual frame.
- static const int kIllegalIndex = -1;
-
- // Construct an initial virtual frame on entry to a JS function.
- inline VirtualFrame();
-
- // Construct a virtual frame as a clone of an existing one.
- explicit inline VirtualFrame(VirtualFrame* original);
-
- CodeGenerator* cgen() {
- return CodeGeneratorScope::Current(Isolate::Current());
- }
-
- MacroAssembler* masm() { return cgen()->masm(); }
-
- // Create a duplicate of an existing valid frame element.
- FrameElement CopyElementAt(int index,
- TypeInfo info = TypeInfo::Uninitialized());
-
- // The number of elements on the virtual frame.
- int element_count() { return elements_.length(); }
-
- // The height of the virtual expression stack.
- int height() {
- return element_count() - expression_base_index();
- }
-
- int register_location(int num) {
- ASSERT(num >= 0 && num < RegisterAllocator::kNumRegisters);
- return register_locations_[num];
- }
-
- inline int register_location(Register reg);
-
- inline void set_register_location(Register reg, int index);
-
- bool is_used(int num) {
- ASSERT(num >= 0 && num < RegisterAllocator::kNumRegisters);
- return register_locations_[num] != kIllegalIndex;
- }
-
- inline bool is_used(Register reg);
-
- // Add extra in-memory elements to the top of the frame to match an actual
- // frame (eg, the frame after an exception handler is pushed). No code is
- // emitted.
- void Adjust(int count);
-
- // Forget count elements from the top of the frame all in-memory
- // (including synced) and adjust the stack pointer downward, to
- // match an external frame effect (examples include a call removing
- // its arguments, and exiting a try/catch removing an exception
- // handler). No code will be emitted.
- void Forget(int count) {
- ASSERT(count >= 0);
- ASSERT(stack_pointer_ == element_count() - 1);
- stack_pointer_ -= count;
- ForgetElements(count);
- }
-
- // Forget count elements from the top of the frame without adjusting
- // the stack pointer downward. This is used, for example, before
- // merging frames at break, continue, and return targets.
- void ForgetElements(int count);
-
- // Spill all values from the frame to memory.
- inline void SpillAll();
-
- // Spill all occurrences of a specific register from the frame.
- void Spill(Register reg) {
- if (is_used(reg)) SpillElementAt(register_location(reg));
- }
-
- // Spill all occurrences of an arbitrary register if possible. Return the
- // register spilled or no_reg if it was not possible to free any register
- // (ie, they all have frame-external references).
- Register SpillAnyRegister();
-
- // Spill the top element of the frame to memory.
- void SpillTop() { SpillElementAt(element_count() - 1); }
-
- // Sync the range of elements in [begin, end] with memory.
- void SyncRange(int begin, int end);
-
- // Make this frame so that an arbitrary frame of the same height can
- // be merged to it. Copies and constants are removed from the frame.
- void MakeMergable();
-
- // Prepare this virtual frame for merging to an expected frame by
- // performing some state changes that do not require generating
- // code. It is guaranteed that no code will be generated.
- void PrepareMergeTo(VirtualFrame* expected);
-
- // Make this virtual frame have a state identical to an expected virtual
- // frame. As a side effect, code may be emitted to make this frame match
- // the expected one.
- void MergeTo(VirtualFrame* expected);
-
- // Detach a frame from its code generator, perhaps temporarily. This
- // tells the register allocator that it is free to use frame-internal
- // registers. Used when the code generator's frame is switched from this
- // one to NULL by an unconditional jump.
- void DetachFromCodeGenerator() {
- RegisterAllocator* cgen_allocator = cgen()->allocator();
- for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
- if (is_used(i)) cgen_allocator->Unuse(i);
- }
- }
-
- // (Re)attach a frame to its code generator. This informs the register
- // allocator that the frame-internal register references are active again.
- // Used when a code generator's frame is switched from NULL to this one by
- // binding a label.
- void AttachToCodeGenerator() {
- RegisterAllocator* cgen_allocator = cgen()->allocator();
- for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
- if (is_used(i)) cgen_allocator->Use(i);
- }
- }
-
- // Emit code for the physical JS entry and exit frame sequences. After
- // calling Enter, the virtual frame is ready for use; and after calling
- // Exit it should not be used. Note that Enter does not allocate space in
- // the physical frame for storing frame-allocated locals.
- void Enter();
- void Exit();
-
- // Prepare for returning from the frame by spilling locals. This
- // avoids generating unnecessary merge code when jumping to the
- // shared return site. Emits code for spills.
- inline void PrepareForReturn();
-
- // Number of local variables after when we use a loop for allocating.
- static const int kLocalVarBound = 14;
-
- // Allocate and initialize the frame-allocated locals.
- void AllocateStackSlots();
-
- // An element of the expression stack as an assembly operand.
- Operand ElementAt(int index) const {
- return Operand(rsp, index * kPointerSize);
- }
-
- // Random-access store to a frame-top relative frame element. The result
- // becomes owned by the frame and is invalidated.
- void SetElementAt(int index, Result* value);
-
- // Set a frame element to a constant. The index is frame-top relative.
- inline void SetElementAt(int index, Handle<Object> value);
-
- void PushElementAt(int index) {
- PushFrameSlotAt(element_count() - index - 1);
- }
-
- void StoreToElementAt(int index) {
- StoreToFrameSlotAt(element_count() - index - 1);
- }
-
- // A frame-allocated local as an assembly operand.
- Operand LocalAt(int index) {
- ASSERT(0 <= index);
- ASSERT(index < local_count());
- return Operand(rbp, kLocal0Offset - index * kPointerSize);
- }
-
- // Push a copy of the value of a local frame slot on top of the frame.
- void PushLocalAt(int index) {
- PushFrameSlotAt(local0_index() + index);
- }
-
- // Push the value of a local frame slot on top of the frame and invalidate
- // the local slot. The slot should be written to before trying to read
- // from it again.
- void TakeLocalAt(int index) {
- TakeFrameSlotAt(local0_index() + index);
- }
-
- // Store the top value on the virtual frame into a local frame slot. The
- // value is left in place on top of the frame.
- void StoreToLocalAt(int index) {
- StoreToFrameSlotAt(local0_index() + index);
- }
-
- // Push the address of the receiver slot on the frame.
- void PushReceiverSlotAddress();
-
- // Push the function on top of the frame.
- void PushFunction() { PushFrameSlotAt(function_index()); }
-
- // Save the value of the esi register to the context frame slot.
- void SaveContextRegister();
-
- // Restore the esi register from the value of the context frame
- // slot.
- void RestoreContextRegister();
-
- // A parameter as an assembly operand.
- Operand ParameterAt(int index) {
- ASSERT(-1 <= index); // -1 is the receiver.
- ASSERT(index < parameter_count());
- return Operand(rbp, (1 + parameter_count() - index) * kPointerSize);
- }
-
- // Push a copy of the value of a parameter frame slot on top of the frame.
- void PushParameterAt(int index) {
- PushFrameSlotAt(param0_index() + index);
- }
-
- // Push the value of a paramter frame slot on top of the frame and
- // invalidate the parameter slot. The slot should be written to before
- // trying to read from it again.
- void TakeParameterAt(int index) {
- TakeFrameSlotAt(param0_index() + index);
- }
-
- // Store the top value on the virtual frame into a parameter frame slot.
- // The value is left in place on top of the frame.
- void StoreToParameterAt(int index) {
- StoreToFrameSlotAt(param0_index() + index);
- }
-
- // The receiver frame slot.
- Operand Receiver() { return ParameterAt(-1); }
-
- // Push a try-catch or try-finally handler on top of the virtual frame.
- void PushTryHandler(HandlerType type);
-
- // Call stub given the number of arguments it expects on (and
- // removes from) the stack.
- inline Result CallStub(CodeStub* stub, int arg_count);
-
- // Call stub that takes a single argument passed in eax. The
- // argument is given as a result which does not have to be eax or
- // even a register. The argument is consumed by the call.
- Result CallStub(CodeStub* stub, Result* arg);
-
- // Call stub that takes a pair of arguments passed in edx (arg0, rdx) and
- // eax (arg1, rax). The arguments are given as results which do not have
- // to be in the proper registers or even in registers. The
- // arguments are consumed by the call.
- Result CallStub(CodeStub* stub, Result* arg0, Result* arg1);
-
- // Call JS function from top of the stack with arguments
- // taken from the stack.
- Result CallJSFunction(int arg_count);
-
- // Call runtime given the number of arguments expected on (and
- // removed from) the stack.
- Result CallRuntime(const Runtime::Function* f, int arg_count);
- Result CallRuntime(Runtime::FunctionId id, int arg_count);
-
-#ifdef ENABLE_DEBUGGER_SUPPORT
- void DebugBreak();
-#endif
-
- // Invoke builtin given the number of arguments it expects on (and
- // removes from) the stack.
- Result InvokeBuiltin(Builtins::JavaScript id,
- InvokeFlag flag,
- int arg_count);
-
- // Call load IC. Name and receiver are found on top of the frame.
- // Both are dropped.
- Result CallLoadIC(RelocInfo::Mode mode);
-
- // Call keyed load IC. Key and receiver are found on top of the
- // frame. Both are dropped.
- Result CallKeyedLoadIC(RelocInfo::Mode mode);
-
- // Call store IC. If the load is contextual, value is found on top of the
- // frame. If not, value and receiver are on the frame. Both are dropped.
- Result CallStoreIC(Handle<String> name, bool is_contextual,
- StrictModeFlag strict_mode);
-
- // Call keyed store IC. Value, key, and receiver are found on top
- Result CallKeyedStoreIC(StrictModeFlag strict_mode);
-
- // Call call IC. Function name, arguments, and receiver are found on top
- // of the frame and dropped by the call.
- // The argument count does not include the receiver.
- Result CallCallIC(RelocInfo::Mode mode, int arg_count, int loop_nesting);
-
- // Call keyed call IC. Same calling convention as CallCallIC.
- Result CallKeyedCallIC(RelocInfo::Mode mode, int arg_count, int loop_nesting);
-
- // Allocate and call JS function as constructor. Arguments,
- // receiver (global object), and function are found on top of the
- // frame. Function is not dropped. The argument count does not
- // include the receiver.
- Result CallConstructor(int arg_count);
-
- // Drop a number of elements from the top of the expression stack. May
- // emit code to affect the physical frame. Does not clobber any registers
- // excepting possibly the stack pointer.
- void Drop(int count);
-
- // Drop one element.
- void Drop() { Drop(1); }
-
- // Duplicate the top element of the frame.
- void Dup() { PushFrameSlotAt(element_count() - 1); }
-
- // Duplicate the n'th element from the top of the frame.
- // Dup(1) is equivalent to Dup().
- void Dup(int n) {
- ASSERT(n > 0);
- PushFrameSlotAt(element_count() - n);
- }
-
- // Pop an element from the top of the expression stack. Returns a
- // Result, which may be a constant or a register.
- Result Pop();
-
- // Pop and save an element from the top of the expression stack and
- // emit a corresponding pop instruction.
- void EmitPop(Register reg);
- void EmitPop(const Operand& operand);
-
- // Push an element on top of the expression stack and emit a
- // corresponding push instruction.
- void EmitPush(Register reg,
- TypeInfo info = TypeInfo::Unknown());
- void EmitPush(const Operand& operand,
- TypeInfo info = TypeInfo::Unknown());
- void EmitPush(Heap::RootListIndex index,
- TypeInfo info = TypeInfo::Unknown());
- void EmitPush(Immediate immediate,
- TypeInfo info = TypeInfo::Unknown());
- void EmitPush(Smi* value);
- // Uses kScratchRegister, emits appropriate relocation info.
- void EmitPush(Handle<Object> value);
-
- inline bool ConstantPoolOverflowed();
-
- // Push an element on the virtual frame.
- void Push(Handle<Object> value);
- inline void Push(Register reg, TypeInfo info = TypeInfo::Unknown());
- inline void Push(Smi* value);
-
- // Pushing a result invalidates it (its contents become owned by the
- // frame).
- void Push(Result* result) {
- if (result->is_register()) {
- Push(result->reg(), result->type_info());
- } else {
- ASSERT(result->is_constant());
- Push(result->handle());
- }
- result->Unuse();
- }
-
- // Pushing an expression expects that the expression is trivial (according
- // to Expression::IsTrivial).
- void Push(Expression* expr);
-
- // Nip removes zero or more elements from immediately below the top
- // of the frame, leaving the previous top-of-frame value on top of
- // the frame. Nip(k) is equivalent to x = Pop(), Drop(k), Push(x).
- inline void Nip(int num_dropped);
-
- inline void SetTypeForLocalAt(int index, TypeInfo info);
- inline void SetTypeForParamAt(int index, TypeInfo info);
-
- private:
- static const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset;
- static const int kFunctionOffset = JavaScriptFrameConstants::kFunctionOffset;
- static const int kContextOffset = StandardFrameConstants::kContextOffset;
-
- static const int kHandlerSize = StackHandlerConstants::kSize / kPointerSize;
- static const int kPreallocatedElements = 5 + 8; // 8 expression stack slots.
-
- ZoneList<FrameElement> elements_;
-
- // The index of the element that is at the processor's stack pointer
- // (the esp register).
- int stack_pointer_;
-
- // The index of the register frame element using each register, or
- // kIllegalIndex if a register is not on the frame.
- int register_locations_[RegisterAllocator::kNumRegisters];
-
- // The number of frame-allocated locals and parameters respectively.
- inline int parameter_count();
- inline int local_count();
-
- // The index of the element that is at the processor's frame pointer
- // (the ebp register). The parameters, receiver, and return address
- // are below the frame pointer.
- int frame_pointer() { return parameter_count() + 2; }
-
- // The index of the first parameter. The receiver lies below the first
- // parameter.
- int param0_index() { return 1; }
-
- // The index of the context slot in the frame. It is immediately
- // above the frame pointer.
- int context_index() { return frame_pointer() + 1; }
-
- // The index of the function slot in the frame. It is above the frame
- // pointer and the context slot.
- int function_index() { return frame_pointer() + 2; }
-
- // The index of the first local. Between the frame pointer and the
- // locals lie the context and the function.
- int local0_index() { return frame_pointer() + 3; }
-
- // The index of the base of the expression stack.
- int expression_base_index() { return local0_index() + local_count(); }
-
- // Convert a frame index into a frame pointer relative offset into the
- // actual stack.
- int fp_relative(int index) {
- ASSERT(index < element_count());
- ASSERT(frame_pointer() < element_count()); // FP is on the frame.
- return (frame_pointer() - index) * kPointerSize;
- }
-
- // Record an occurrence of a register in the virtual frame. This has the
- // effect of incrementing the register's external reference count and
- // of updating the index of the register's location in the frame.
- void Use(Register reg, int index) {
- ASSERT(!is_used(reg));
- set_register_location(reg, index);
- cgen()->allocator()->Use(reg);
- }
-
- // Record that a register reference has been dropped from the frame. This
- // decrements the register's external reference count and invalidates the
- // index of the register's location in the frame.
- void Unuse(Register reg) {
- ASSERT(is_used(reg));
- set_register_location(reg, kIllegalIndex);
- cgen()->allocator()->Unuse(reg);
- }
-
- // Spill the element at a particular index---write it to memory if
- // necessary, free any associated register, and forget its value if
- // constant.
- void SpillElementAt(int index);
-
- // Sync the element at a particular index. If it is a register or
- // constant that disagrees with the value on the stack, write it to memory.
- // Keep the element type as register or constant, and clear the dirty bit.
- void SyncElementAt(int index);
-
- // Sync a single unsynced element that lies beneath or at the stack pointer.
- void SyncElementBelowStackPointer(int index);
-
- // Sync a single unsynced element that lies just above the stack pointer.
- void SyncElementByPushing(int index);
-
- // Push a copy of a frame slot (typically a local or parameter) on top of
- // the frame.
- inline void PushFrameSlotAt(int index);
-
- // Push a the value of a frame slot (typically a local or parameter) on
- // top of the frame and invalidate the slot.
- void TakeFrameSlotAt(int index);
-
- // Store the value on top of the frame to a frame slot (typically a local
- // or parameter).
- void StoreToFrameSlotAt(int index);
-
- // Spill all elements in registers. Spill the top spilled_args elements
- // on the frame. Sync all other frame elements.
- // Then drop dropped_args elements from the virtual frame, to match
- // the effect of an upcoming call that will drop them from the stack.
- void PrepareForCall(int spilled_args, int dropped_args);
-
- // Move frame elements currently in registers or constants, that
- // should be in memory in the expected frame, to memory.
- void MergeMoveRegistersToMemory(VirtualFrame* expected);
-
- // Make the register-to-register moves necessary to
- // merge this frame with the expected frame.
- // Register to memory moves must already have been made,
- // and memory to register moves must follow this call.
- // This is because some new memory-to-register moves are
- // created in order to break cycles of register moves.
- // Used in the implementation of MergeTo().
- void MergeMoveRegistersToRegisters(VirtualFrame* expected);
-
- // Make the memory-to-register and constant-to-register moves
- // needed to make this frame equal the expected frame.
- // Called after all register-to-memory and register-to-register
- // moves have been made. After this function returns, the frames
- // should be equal.
- void MergeMoveMemoryToRegisters(VirtualFrame* expected);
-
- // Invalidates a frame slot (puts an invalid frame element in it).
- // Copies on the frame are correctly handled, and if this slot was
- // the backing store of copies, the index of the new backing store
- // is returned. Otherwise, returns kIllegalIndex.
- // Register counts are correctly updated.
- int InvalidateFrameSlotAt(int index);
-
- // This function assumes that a and b are the only results that could be in
- // the registers a_reg or b_reg. Other results can be live, but must not
- // be in the registers a_reg or b_reg. The results a and b are invalidated.
- void MoveResultsToRegisters(Result* a,
- Result* b,
- Register a_reg,
- Register b_reg);
-
- // Call a code stub that has already been prepared for calling (via
- // PrepareForCall).
- Result RawCallStub(CodeStub* stub);
-
- // Calls a code object which has already been prepared for calling
- // (via PrepareForCall).
- Result RawCallCodeObject(Handle<Code> code, RelocInfo::Mode rmode);
-
- inline bool Equals(VirtualFrame* other);
-
- // Classes that need raw access to the elements_ array.
- friend class FrameRegisterState;
- friend class JumpTarget;
-};
-
-
-} } // namespace v8::internal
-
-#endif // V8_X64_VIRTUAL_FRAME_X64_H_
diff --git a/src/zone-inl.h b/src/zone-inl.h
index 516fc4a..17e83dc 100644
--- a/src/zone-inl.h
+++ b/src/zone-inl.h
@@ -97,6 +97,10 @@
return ZONE->New(static_cast<int>(size));
}
+void* ZoneObject::operator new(size_t size, Zone* zone) {
+ return zone->New(static_cast<int>(size));
+}
+
inline void* ZoneListAllocationPolicy::New(int size) {
return ZONE->New(size);
diff --git a/src/zone.h b/src/zone.h
index 13b55c4..9efe4f5 100644
--- a/src/zone.h
+++ b/src/zone.h
@@ -133,6 +133,7 @@
public:
// Allocate a new ZoneObject of 'size' bytes in the Zone.
inline void* operator new(size_t size);
+ inline void* operator new(size_t size, Zone* zone);
// Ideally, the delete operator should be private instead of
// public, but unfortunately the compiler sometimes synthesizes
diff --git a/test/cctest/cctest.status b/test/cctest/cctest.status
index 986fb8d..3bb9998 100644
--- a/test/cctest/cctest.status
+++ b/test/cctest/cctest.status
@@ -57,6 +57,12 @@
test-debug/DebuggerAgentProtocolOverflowHeader: SKIP
test-sockets/Socket: SKIP
+# BUG(1075): Unresolved crashes.
+cctest/test-serialize/Deserialize: PASS || FAIL
+cctest/test-serialize/DeserializeFromSecondSerializationAndRunScript2: PASS || FAIL
+cctest/test-serialize/DeserializeAndRunScript2: PASS || FAIL
+cctest/test-serialize/DeserializeFromSecondSerialization: PASS || FAIL
+
##############################################################################
[ $arch == arm && $crankshaft ]
diff --git a/test/cctest/test-accessors.cc b/test/cctest/test-accessors.cc
index b74f166..028f82f 100644
--- a/test/cctest/test-accessors.cc
+++ b/test/cctest/test-accessors.cc
@@ -397,7 +397,7 @@
for (int i = 0; !iter.done(); i++) {
i::StackFrame* frame = iter.frame();
CHECK(i != 0 || (frame->type() == i::StackFrame::EXIT));
- i::Code* code = frame->LookupCode(i::Isolate::Current());
+ i::Code* code = frame->LookupCode();
CHECK(code->IsCode());
i::Address pc = frame->pc();
CHECK(code->contains(pc));
diff --git a/test/cctest/test-alloc.cc b/test/cctest/test-alloc.cc
index 3d8157d..0ccf4b8 100644
--- a/test/cctest/test-alloc.cc
+++ b/test/cctest/test-alloc.cc
@@ -200,6 +200,7 @@
size_t allocated = 0;
void* base = Isolate::Current()->code_range()->
AllocateRawMemory(requested, &allocated);
+ CHECK(base != NULL);
blocks.Add(Block(base, static_cast<int>(allocated)));
current_allocated += static_cast<int>(allocated);
total_allocated += static_cast<int>(allocated);
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index 33d505e..d7621d1 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -50,18 +50,26 @@
}
using ::v8::AccessorInfo;
+using ::v8::Arguments;
using ::v8::Context;
using ::v8::Extension;
using ::v8::Function;
+using ::v8::FunctionTemplate;
+using ::v8::Handle;
using ::v8::HandleScope;
using ::v8::Local;
+using ::v8::Message;
+using ::v8::MessageCallback;
using ::v8::Object;
using ::v8::ObjectTemplate;
using ::v8::Persistent;
using ::v8::Script;
+using ::v8::StackTrace;
using ::v8::String;
-using ::v8::Value;
+using ::v8::TryCatch;
+using ::v8::Undefined;
using ::v8::V8;
+using ::v8::Value;
namespace i = ::i;
@@ -2655,6 +2663,21 @@
}
+TEST(APIStackOverflowAndVerboseTryCatch) {
+ message_received = false;
+ v8::HandleScope scope;
+ v8::V8::AddMessageListener(receive_message);
+ LocalContext context;
+ v8::TryCatch try_catch;
+ try_catch.SetVerbose(true);
+ Local<Value> result = CompileRun("function foo() { foo(); } foo();");
+ CHECK(try_catch.HasCaught());
+ CHECK(result.IsEmpty());
+ CHECK(message_received);
+ v8::V8::RemoveMessageListeners(receive_message);
+}
+
+
THREADED_TEST(ExternalScriptException) {
v8::HandleScope scope;
Local<ObjectTemplate> templ = ObjectTemplate::New();
@@ -8574,6 +8597,132 @@
}
+static Handle<Value> ThrowingGetter(Local<String> name,
+ const AccessorInfo& info) {
+ ApiTestFuzzer::Fuzz();
+ ThrowException(Handle<Value>());
+ return Undefined();
+}
+
+
+THREADED_TEST(VariousGetPropertiesAndThrowingCallbacks) {
+ HandleScope scope;
+ LocalContext context;
+
+ Local<FunctionTemplate> templ = FunctionTemplate::New();
+ Local<ObjectTemplate> instance_templ = templ->InstanceTemplate();
+ instance_templ->SetAccessor(v8_str("f"), ThrowingGetter);
+
+ Local<Object> instance = templ->GetFunction()->NewInstance();
+
+ Local<Object> another = Object::New();
+ another->SetPrototype(instance);
+
+ Local<Object> with_js_getter = CompileRun(
+ "o = {};\n"
+ "o.__defineGetter__('f', function() { throw undefined; });\n"
+ "o\n").As<Object>();
+ CHECK(!with_js_getter.IsEmpty());
+
+ TryCatch try_catch;
+
+ Local<Value> result = instance->GetRealNamedProperty(v8_str("f"));
+ CHECK(try_catch.HasCaught());
+ try_catch.Reset();
+ CHECK(result.IsEmpty());
+
+ result = another->GetRealNamedProperty(v8_str("f"));
+ CHECK(try_catch.HasCaught());
+ try_catch.Reset();
+ CHECK(result.IsEmpty());
+
+ result = another->GetRealNamedPropertyInPrototypeChain(v8_str("f"));
+ CHECK(try_catch.HasCaught());
+ try_catch.Reset();
+ CHECK(result.IsEmpty());
+
+ result = another->Get(v8_str("f"));
+ CHECK(try_catch.HasCaught());
+ try_catch.Reset();
+ CHECK(result.IsEmpty());
+
+ result = with_js_getter->GetRealNamedProperty(v8_str("f"));
+ CHECK(try_catch.HasCaught());
+ try_catch.Reset();
+ CHECK(result.IsEmpty());
+
+ result = with_js_getter->Get(v8_str("f"));
+ CHECK(try_catch.HasCaught());
+ try_catch.Reset();
+ CHECK(result.IsEmpty());
+}
+
+
+static Handle<Value> ThrowingCallbackWithTryCatch(const Arguments& args) {
+ TryCatch try_catch;
+ // Verboseness is important: it triggers message delivery which can call into
+ // external code.
+ try_catch.SetVerbose(true);
+ CompileRun("throw 'from JS';");
+ CHECK(try_catch.HasCaught());
+ CHECK(!i::Isolate::Current()->has_pending_exception());
+ CHECK(!i::Isolate::Current()->has_scheduled_exception());
+ return Undefined();
+}
+
+
+static int call_depth;
+
+
+static void WithTryCatch(Handle<Message> message, Handle<Value> data) {
+ TryCatch try_catch;
+}
+
+
+static void ThrowFromJS(Handle<Message> message, Handle<Value> data) {
+ if (--call_depth) CompileRun("throw 'ThrowInJS';");
+}
+
+
+static void ThrowViaApi(Handle<Message> message, Handle<Value> data) {
+ if (--call_depth) ThrowException(v8_str("ThrowViaApi"));
+}
+
+
+static void WebKitLike(Handle<Message> message, Handle<Value> data) {
+ Handle<String> errorMessageString = message->Get();
+ CHECK(!errorMessageString.IsEmpty());
+ message->GetStackTrace();
+ message->GetScriptResourceName();
+}
+
+THREADED_TEST(ExceptionsDoNotPropagatePastTryCatch) {
+ HandleScope scope;
+ LocalContext context;
+
+ Local<Function> func =
+ FunctionTemplate::New(ThrowingCallbackWithTryCatch)->GetFunction();
+ context->Global()->Set(v8_str("func"), func);
+
+ MessageCallback callbacks[] =
+ { NULL, WebKitLike, ThrowViaApi, ThrowFromJS, WithTryCatch };
+ for (unsigned i = 0; i < sizeof(callbacks)/sizeof(callbacks[0]); i++) {
+ MessageCallback callback = callbacks[i];
+ if (callback != NULL) {
+ V8::AddMessageListener(callback);
+ }
+ call_depth = 5;
+ ExpectFalse(
+ "var thrown = false;\n"
+ "try { func(); } catch(e) { thrown = true; }\n"
+ "thrown\n");
+ if (callback != NULL) {
+ V8::RemoveMessageListeners(callback);
+ }
+ }
+}
+
+
static v8::Handle<Value> ParentGetter(Local<String> name,
const AccessorInfo& info) {
ApiTestFuzzer::Fuzz();
@@ -11495,7 +11644,7 @@
array->set(i, static_cast<ElementType>(i));
}
result = CompileRun("function ee_load_test_func(sum) {"
- " for (var i=0;i<40;++i)"
+ " for (var i = 0; i < 40; ++i)"
" sum += ext_array[i];"
" return sum;"
"}"
@@ -11508,7 +11657,7 @@
// Test crankshaft external array stores
result = CompileRun("function ee_store_test_func(sum) {"
- " for (var i=0;i<40;++i)"
+ " for (var i = 0; i < 40; ++i)"
" sum += ext_array[i] = i;"
" return sum;"
"}"
@@ -11519,6 +11668,39 @@
"sum;");
CHECK_EQ(7800000, result->Int32Value());
+ for (int i = 0; i < kElementCount; i++) {
+ array->set(i, static_cast<ElementType>(i));
+ }
+ // Test complex assignments
+ result = CompileRun("function ee_op_test_complex_func(sum) {"
+ " for (var i = 0; i < 40; ++i) {"
+ " sum += (ext_array[i] += 1);"
+ " sum += (ext_array[i] -= 1);"
+ " } "
+ " return sum;"
+ "}"
+ "sum=0;"
+ "for (var i=0;i<10000;++i) {"
+ " sum=ee_op_test_complex_func(sum);"
+ "}"
+ "sum;");
+ CHECK_EQ(16000000, result->Int32Value());
+
+ // Test count operations
+ result = CompileRun("function ee_op_test_count_func(sum) {"
+ " for (var i = 0; i < 40; ++i) {"
+ " sum += (++ext_array[i]);"
+ " sum += (--ext_array[i]);"
+ " } "
+ " return sum;"
+ "}"
+ "sum=0;"
+ "for (var i=0;i<10000;++i) {"
+ " sum=ee_op_test_count_func(sum);"
+ "}"
+ "sum;");
+ CHECK_EQ(16000000, result->Int32Value());
+
result = CompileRun("ext_array[3] = 33;"
"delete ext_array[3];"
"ext_array[3];");
@@ -12169,28 +12351,14 @@
static double DoubleFromBits(uint64_t value) {
double target;
-#ifdef BIG_ENDIAN_FLOATING_POINT
- const int kIntSize = 4;
- // Somebody swapped the lower and higher half of doubles.
- memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
- memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
-#else
memcpy(&target, &value, sizeof(target));
-#endif
return target;
}
static uint64_t DoubleToBits(double value) {
uint64_t target;
-#ifdef BIG_ENDIAN_FLOATING_POINT
- const int kIntSize = 4;
- // Somebody swapped the lower and higher half of doubles.
- memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
- memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
-#else
memcpy(&target, &value, sizeof(target));
-#endif
return target;
}
@@ -13629,3 +13797,102 @@
context->DetachGlobal();
define_property->Call(proxy, 0, NULL);
}
+
+
+static void InstallContextId(v8::Handle<Context> context, int id) {
+ Context::Scope scope(context);
+ CompileRun("Object.prototype").As<Object>()->
+ Set(v8_str("context_id"), v8::Integer::New(id));
+}
+
+
+static void CheckContextId(v8::Handle<Object> object, int expected) {
+ CHECK_EQ(expected, object->Get(v8_str("context_id"))->Int32Value());
+}
+
+
+THREADED_TEST(CreationContext) {
+ HandleScope handle_scope;
+ Persistent<Context> context1 = Context::New();
+ InstallContextId(context1, 1);
+ Persistent<Context> context2 = Context::New();
+ InstallContextId(context2, 2);
+ Persistent<Context> context3 = Context::New();
+ InstallContextId(context3, 3);
+
+ Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New();
+
+ Local<Object> object1;
+ Local<Function> func1;
+ {
+ Context::Scope scope(context1);
+ object1 = Object::New();
+ func1 = tmpl->GetFunction();
+ }
+
+ Local<Object> object2;
+ Local<Function> func2;
+ {
+ Context::Scope scope(context2);
+ object2 = Object::New();
+ func2 = tmpl->GetFunction();
+ }
+
+ Local<Object> instance1;
+ Local<Object> instance2;
+
+ {
+ Context::Scope scope(context3);
+ instance1 = func1->NewInstance();
+ instance2 = func2->NewInstance();
+ }
+
+ CHECK(object1->CreationContext() == context1);
+ CheckContextId(object1, 1);
+ CHECK(func1->CreationContext() == context1);
+ CheckContextId(func1, 1);
+ CHECK(instance1->CreationContext() == context1);
+ CheckContextId(instance1, 1);
+ CHECK(object2->CreationContext() == context2);
+ CheckContextId(object2, 2);
+ CHECK(func2->CreationContext() == context2);
+ CheckContextId(func2, 2);
+ CHECK(instance2->CreationContext() == context2);
+ CheckContextId(instance2, 2);
+
+ {
+ Context::Scope scope(context1);
+ CHECK(object1->CreationContext() == context1);
+ CheckContextId(object1, 1);
+ CHECK(func1->CreationContext() == context1);
+ CheckContextId(func1, 1);
+ CHECK(instance1->CreationContext() == context1);
+ CheckContextId(instance1, 1);
+ CHECK(object2->CreationContext() == context2);
+ CheckContextId(object2, 2);
+ CHECK(func2->CreationContext() == context2);
+ CheckContextId(func2, 2);
+ CHECK(instance2->CreationContext() == context2);
+ CheckContextId(instance2, 2);
+ }
+
+ {
+ Context::Scope scope(context2);
+ CHECK(object1->CreationContext() == context1);
+ CheckContextId(object1, 1);
+ CHECK(func1->CreationContext() == context1);
+ CheckContextId(func1, 1);
+ CHECK(instance1->CreationContext() == context1);
+ CheckContextId(instance1, 1);
+ CHECK(object2->CreationContext() == context2);
+ CheckContextId(object2, 2);
+ CHECK(func2->CreationContext() == context2);
+ CheckContextId(func2, 2);
+ CHECK(instance2->CreationContext() == context2);
+ CheckContextId(instance2, 2);
+ }
+
+ context1.Dispose();
+ context2.Dispose();
+ context3.Dispose();
+}
diff --git a/test/cctest/test-assembler-arm.cc b/test/cctest/test-assembler-arm.cc
index a91886e..89153c7 100644
--- a/test/cctest/test-assembler-arm.cc
+++ b/test/cctest/test-assembler-arm.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -39,7 +39,8 @@
// Define these function prototypes to match JSEntryFunction in execution.cc.
typedef Object* (*F1)(int x, int p1, int p2, int p3, int p4);
typedef Object* (*F2)(int x, int y, int p2, int p3, int p4);
-typedef Object* (*F3)(void* p, int p1, int p2, int p3, int p4);
+typedef Object* (*F3)(void* p0, int p1, int p2, int p3, int p4);
+typedef Object* (*F4)(void* p0, void* p1, int p2, int p3, int p4);
static v8::Persistent<v8::Context> env;
@@ -58,7 +59,7 @@
InitializeVM();
v8::HandleScope scope;
- Assembler assm(NULL, 0);
+ Assembler assm(Isolate::Current(), NULL, 0);
__ add(r0, r0, Operand(r1));
__ mov(pc, Operand(lr));
@@ -84,7 +85,7 @@
InitializeVM();
v8::HandleScope scope;
- Assembler assm(NULL, 0);
+ Assembler assm(Isolate::Current(), NULL, 0);
Label L, C;
__ mov(r1, Operand(r0));
@@ -121,7 +122,7 @@
InitializeVM();
v8::HandleScope scope;
- Assembler assm(NULL, 0);
+ Assembler assm(Isolate::Current(), NULL, 0);
Label L, C;
__ mov(r1, Operand(r0));
@@ -174,7 +175,7 @@
} T;
T t;
- Assembler assm(NULL, 0);
+ Assembler assm(Isolate::Current(), NULL, 0);
Label L, C;
__ mov(ip, Operand(sp));
@@ -241,11 +242,11 @@
// Create a function that accepts &t, and loads, manipulates, and stores
// the doubles and floats.
- Assembler assm(NULL, 0);
+ Assembler assm(Isolate::Current(), NULL, 0);
Label L, C;
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
__ mov(ip, Operand(sp));
@@ -357,9 +358,9 @@
InitializeVM();
v8::HandleScope scope;
- Assembler assm(NULL, 0);
+ Assembler assm(Isolate::Current(), NULL, 0);
- if (Isolate::Current()->cpu_features()->IsSupported(ARMv7)) {
+ if (CpuFeatures::IsSupported(ARMv7)) {
CpuFeatures::Scope scope(ARMv7);
// On entry, r0 = 0xAAAAAAAA = 0b10..10101010.
__ ubfx(r0, r0, 1, 12); // 0b00..010101010101 = 0x555
@@ -393,9 +394,9 @@
InitializeVM();
v8::HandleScope scope;
- Assembler assm(NULL, 0);
+ Assembler assm(Isolate::Current(), NULL, 0);
- if (Isolate::Current()->cpu_features()->IsSupported(ARMv7)) {
+ if (CpuFeatures::IsSupported(ARMv7)) {
CpuFeatures::Scope scope(ARMv7);
__ usat(r1, 8, Operand(r0)); // Sat 0xFFFF to 0-255 = 0xFF.
__ usat(r2, 12, Operand(r0, ASR, 9)); // Sat (0xFFFF>>9) to 0-4095 = 0x7F.
@@ -436,9 +437,9 @@
InitializeVM();
v8::HandleScope scope;
- Assembler assm(NULL, 0);
+ Assembler assm(Isolate::Current(), NULL, 0);
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
Label wrong_exception;
@@ -608,4 +609,340 @@
TestRoundingMode(u32_f64, RN, (kMaxUInt + 1.0), kMaxUInt, true);
}
+TEST(8) {
+ // Test VFP multi load/store with ia_w.
+ InitializeVM();
+ v8::HandleScope scope;
+
+ typedef struct {
+ double a;
+ double b;
+ double c;
+ double d;
+ double e;
+ double f;
+ double g;
+ double h;
+ } D;
+ D d;
+
+ typedef struct {
+ float a;
+ float b;
+ float c;
+ float d;
+ float e;
+ float f;
+ float g;
+ float h;
+ } F;
+ F f;
+
+ // Create a function that uses vldm/vstm to move some double and
+ // single precision values around in memory.
+ Assembler assm(Isolate::Current(), NULL, 0);
+
+ if (CpuFeatures::IsSupported(VFP3)) {
+ CpuFeatures::Scope scope(VFP3);
+
+ __ mov(ip, Operand(sp));
+ __ stm(db_w, sp, r4.bit() | fp.bit() | lr.bit());
+ __ sub(fp, ip, Operand(4));
+
+ __ add(r4, r0, Operand(OFFSET_OF(D, a)));
+ __ vldm(ia_w, r4, d0, d3);
+ __ vldm(ia_w, r4, d4, d7);
+
+ __ add(r4, r0, Operand(OFFSET_OF(D, a)));
+ __ vstm(ia_w, r4, d6, d7);
+ __ vstm(ia_w, r4, d0, d5);
+
+ __ add(r4, r1, Operand(OFFSET_OF(F, a)));
+ __ vldm(ia_w, r4, s0, s3);
+ __ vldm(ia_w, r4, s4, s7);
+
+ __ add(r4, r1, Operand(OFFSET_OF(F, a)));
+ __ vstm(ia_w, r4, s6, s7);
+ __ vstm(ia_w, r4, s0, s5);
+
+ __ ldm(ia_w, sp, r4.bit() | fp.bit() | pc.bit());
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Object* code = HEAP->CreateCode(
+ desc,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(HEAP->undefined_value()))->ToObjectChecked();
+ CHECK(code->IsCode());
+#ifdef DEBUG
+ Code::cast(code)->Print();
+#endif
+ F4 fn = FUNCTION_CAST<F4>(Code::cast(code)->entry());
+ d.a = 1.1;
+ d.b = 2.2;
+ d.c = 3.3;
+ d.d = 4.4;
+ d.e = 5.5;
+ d.f = 6.6;
+ d.g = 7.7;
+ d.h = 8.8;
+
+ f.a = 1.0;
+ f.b = 2.0;
+ f.c = 3.0;
+ f.d = 4.0;
+ f.e = 5.0;
+ f.f = 6.0;
+ f.g = 7.0;
+ f.h = 8.0;
+
+ Object* dummy = CALL_GENERATED_CODE(fn, &d, &f, 0, 0, 0);
+ USE(dummy);
+
+ CHECK_EQ(7.7, d.a);
+ CHECK_EQ(8.8, d.b);
+ CHECK_EQ(1.1, d.c);
+ CHECK_EQ(2.2, d.d);
+ CHECK_EQ(3.3, d.e);
+ CHECK_EQ(4.4, d.f);
+ CHECK_EQ(5.5, d.g);
+ CHECK_EQ(6.6, d.h);
+
+ CHECK_EQ(7.0, f.a);
+ CHECK_EQ(8.0, f.b);
+ CHECK_EQ(1.0, f.c);
+ CHECK_EQ(2.0, f.d);
+ CHECK_EQ(3.0, f.e);
+ CHECK_EQ(4.0, f.f);
+ CHECK_EQ(5.0, f.g);
+ CHECK_EQ(6.0, f.h);
+ }
+}
+
+
+TEST(9) {
+ // Test VFP multi load/store with ia.
+ InitializeVM();
+ v8::HandleScope scope;
+
+ typedef struct {
+ double a;
+ double b;
+ double c;
+ double d;
+ double e;
+ double f;
+ double g;
+ double h;
+ } D;
+ D d;
+
+ typedef struct {
+ float a;
+ float b;
+ float c;
+ float d;
+ float e;
+ float f;
+ float g;
+ float h;
+ } F;
+ F f;
+
+ // Create a function that uses vldm/vstm to move some double and
+ // single precision values around in memory.
+ Assembler assm(Isolate::Current(), NULL, 0);
+
+ if (CpuFeatures::IsSupported(VFP3)) {
+ CpuFeatures::Scope scope(VFP3);
+
+ __ mov(ip, Operand(sp));
+ __ stm(db_w, sp, r4.bit() | fp.bit() | lr.bit());
+ __ sub(fp, ip, Operand(4));
+
+ __ add(r4, r0, Operand(OFFSET_OF(D, a)));
+ __ vldm(ia, r4, d0, d3);
+ __ add(r4, r4, Operand(4 * 8));
+ __ vldm(ia, r4, d4, d7);
+
+ __ add(r4, r0, Operand(OFFSET_OF(D, a)));
+ __ vstm(ia, r4, d6, d7);
+ __ add(r4, r4, Operand(2 * 8));
+ __ vstm(ia, r4, d0, d5);
+
+ __ add(r4, r1, Operand(OFFSET_OF(F, a)));
+ __ vldm(ia, r4, s0, s3);
+ __ add(r4, r4, Operand(4 * 4));
+ __ vldm(ia, r4, s4, s7);
+
+ __ add(r4, r1, Operand(OFFSET_OF(F, a)));
+ __ vstm(ia, r4, s6, s7);
+ __ add(r4, r4, Operand(2 * 4));
+ __ vstm(ia, r4, s0, s5);
+
+ __ ldm(ia_w, sp, r4.bit() | fp.bit() | pc.bit());
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Object* code = HEAP->CreateCode(
+ desc,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(HEAP->undefined_value()))->ToObjectChecked();
+ CHECK(code->IsCode());
+#ifdef DEBUG
+ Code::cast(code)->Print();
+#endif
+ F4 fn = FUNCTION_CAST<F4>(Code::cast(code)->entry());
+ d.a = 1.1;
+ d.b = 2.2;
+ d.c = 3.3;
+ d.d = 4.4;
+ d.e = 5.5;
+ d.f = 6.6;
+ d.g = 7.7;
+ d.h = 8.8;
+
+ f.a = 1.0;
+ f.b = 2.0;
+ f.c = 3.0;
+ f.d = 4.0;
+ f.e = 5.0;
+ f.f = 6.0;
+ f.g = 7.0;
+ f.h = 8.0;
+
+ Object* dummy = CALL_GENERATED_CODE(fn, &d, &f, 0, 0, 0);
+ USE(dummy);
+
+ CHECK_EQ(7.7, d.a);
+ CHECK_EQ(8.8, d.b);
+ CHECK_EQ(1.1, d.c);
+ CHECK_EQ(2.2, d.d);
+ CHECK_EQ(3.3, d.e);
+ CHECK_EQ(4.4, d.f);
+ CHECK_EQ(5.5, d.g);
+ CHECK_EQ(6.6, d.h);
+
+ CHECK_EQ(7.0, f.a);
+ CHECK_EQ(8.0, f.b);
+ CHECK_EQ(1.0, f.c);
+ CHECK_EQ(2.0, f.d);
+ CHECK_EQ(3.0, f.e);
+ CHECK_EQ(4.0, f.f);
+ CHECK_EQ(5.0, f.g);
+ CHECK_EQ(6.0, f.h);
+ }
+}
+
+
+TEST(10) {
+ // Test VFP multi load/store with db_w.
+ InitializeVM();
+ v8::HandleScope scope;
+
+ typedef struct {
+ double a;
+ double b;
+ double c;
+ double d;
+ double e;
+ double f;
+ double g;
+ double h;
+ } D;
+ D d;
+
+ typedef struct {
+ float a;
+ float b;
+ float c;
+ float d;
+ float e;
+ float f;
+ float g;
+ float h;
+ } F;
+ F f;
+
+ // Create a function that uses vldm/vstm to move some double and
+ // single precision values around in memory.
+ Assembler assm(Isolate::Current(), NULL, 0);
+
+ if (CpuFeatures::IsSupported(VFP3)) {
+ CpuFeatures::Scope scope(VFP3);
+
+ __ mov(ip, Operand(sp));
+ __ stm(db_w, sp, r4.bit() | fp.bit() | lr.bit());
+ __ sub(fp, ip, Operand(4));
+
+ __ add(r4, r0, Operand(OFFSET_OF(D, h) + 8));
+ __ vldm(db_w, r4, d4, d7);
+ __ vldm(db_w, r4, d0, d3);
+
+ __ add(r4, r0, Operand(OFFSET_OF(D, h) + 8));
+ __ vstm(db_w, r4, d0, d5);
+ __ vstm(db_w, r4, d6, d7);
+
+ __ add(r4, r1, Operand(OFFSET_OF(F, h) + 4));
+ __ vldm(db_w, r4, s4, s7);
+ __ vldm(db_w, r4, s0, s3);
+
+ __ add(r4, r1, Operand(OFFSET_OF(F, h) + 4));
+ __ vstm(db_w, r4, s0, s5);
+ __ vstm(db_w, r4, s6, s7);
+
+ __ ldm(ia_w, sp, r4.bit() | fp.bit() | pc.bit());
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Object* code = HEAP->CreateCode(
+ desc,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(HEAP->undefined_value()))->ToObjectChecked();
+ CHECK(code->IsCode());
+#ifdef DEBUG
+ Code::cast(code)->Print();
+#endif
+ F4 fn = FUNCTION_CAST<F4>(Code::cast(code)->entry());
+ d.a = 1.1;
+ d.b = 2.2;
+ d.c = 3.3;
+ d.d = 4.4;
+ d.e = 5.5;
+ d.f = 6.6;
+ d.g = 7.7;
+ d.h = 8.8;
+
+ f.a = 1.0;
+ f.b = 2.0;
+ f.c = 3.0;
+ f.d = 4.0;
+ f.e = 5.0;
+ f.f = 6.0;
+ f.g = 7.0;
+ f.h = 8.0;
+
+ Object* dummy = CALL_GENERATED_CODE(fn, &d, &f, 0, 0, 0);
+ USE(dummy);
+
+ CHECK_EQ(7.7, d.a);
+ CHECK_EQ(8.8, d.b);
+ CHECK_EQ(1.1, d.c);
+ CHECK_EQ(2.2, d.d);
+ CHECK_EQ(3.3, d.e);
+ CHECK_EQ(4.4, d.f);
+ CHECK_EQ(5.5, d.g);
+ CHECK_EQ(6.6, d.h);
+
+ CHECK_EQ(7.0, f.a);
+ CHECK_EQ(8.0, f.b);
+ CHECK_EQ(1.0, f.c);
+ CHECK_EQ(2.0, f.d);
+ CHECK_EQ(3.0, f.e);
+ CHECK_EQ(4.0, f.f);
+ CHECK_EQ(5.0, f.g);
+ CHECK_EQ(6.0, f.h);
+ }
+}
+
#undef __
diff --git a/test/cctest/test-assembler-ia32.cc b/test/cctest/test-assembler-ia32.cc
index 694bd57..576739b 100644
--- a/test/cctest/test-assembler-ia32.cc
+++ b/test/cctest/test-assembler-ia32.cc
@@ -61,7 +61,7 @@
v8::HandleScope scope;
v8::internal::byte buffer[256];
- Assembler assm(buffer, sizeof buffer);
+ Assembler assm(Isolate::Current(), buffer, sizeof buffer);
__ mov(eax, Operand(esp, 4));
__ add(eax, Operand(esp, 8));
@@ -89,7 +89,7 @@
v8::HandleScope scope;
v8::internal::byte buffer[256];
- Assembler assm(buffer, sizeof buffer);
+ Assembler assm(Isolate::Current(), buffer, sizeof buffer);
Label L, C;
__ mov(edx, Operand(esp, 4));
@@ -127,7 +127,7 @@
v8::HandleScope scope;
v8::internal::byte buffer[256];
- Assembler assm(buffer, sizeof buffer);
+ Assembler assm(Isolate::Current(), buffer, sizeof buffer);
Label L, C;
__ mov(edx, Operand(esp, 4));
@@ -167,15 +167,15 @@
typedef int (*F3)(float x);
TEST(AssemblerIa323) {
- if (!Isolate::Current()->cpu_features()->IsSupported(SSE2)) return;
-
InitializeVM();
+ if (!CpuFeatures::IsSupported(SSE2)) return;
+
v8::HandleScope scope;
v8::internal::byte buffer[256];
- Assembler assm(buffer, sizeof buffer);
+ Assembler assm(Isolate::Current(), buffer, sizeof buffer);
- CHECK(Isolate::Current()->cpu_features()->IsSupported(SSE2));
+ CHECK(CpuFeatures::IsSupported(SSE2));
{ CpuFeatures::Scope fscope(SSE2);
__ cvttss2si(eax, Operand(esp, 4));
__ ret(0);
@@ -202,15 +202,15 @@
typedef int (*F4)(double x);
TEST(AssemblerIa324) {
- if (!Isolate::Current()->cpu_features()->IsSupported(SSE2)) return;
-
InitializeVM();
+ if (!CpuFeatures::IsSupported(SSE2)) return;
+
v8::HandleScope scope;
v8::internal::byte buffer[256];
- Assembler assm(buffer, sizeof buffer);
+ Assembler assm(Isolate::Current(), buffer, sizeof buffer);
- CHECK(Isolate::Current()->cpu_features()->IsSupported(SSE2));
+ CHECK(CpuFeatures::IsSupported(SSE2));
CpuFeatures::Scope fscope(SSE2);
__ cvttsd2si(eax, Operand(esp, 4));
__ ret(0);
@@ -239,7 +239,7 @@
v8::HandleScope scope;
v8::internal::byte buffer[256];
- Assembler assm(buffer, sizeof buffer);
+ Assembler assm(Isolate::Current(), buffer, sizeof buffer);
__ mov(eax, Operand(reinterpret_cast<intptr_t>(&baz), RelocInfo::NONE));
__ ret(0);
@@ -259,14 +259,14 @@
typedef double (*F5)(double x, double y);
TEST(AssemblerIa326) {
- if (!Isolate::Current()->cpu_features()->IsSupported(SSE2)) return;
-
InitializeVM();
+ if (!CpuFeatures::IsSupported(SSE2)) return;
+
v8::HandleScope scope;
- CHECK(Isolate::Current()->cpu_features()->IsSupported(SSE2));
+ CHECK(CpuFeatures::IsSupported(SSE2));
CpuFeatures::Scope fscope(SSE2);
v8::internal::byte buffer[256];
- Assembler assm(buffer, sizeof buffer);
+ Assembler assm(Isolate::Current(), buffer, sizeof buffer);
__ movdbl(xmm0, Operand(esp, 1 * kPointerSize));
__ movdbl(xmm1, Operand(esp, 3 * kPointerSize));
@@ -305,14 +305,14 @@
typedef double (*F6)(int x);
TEST(AssemblerIa328) {
- if (!Isolate::Current()->cpu_features()->IsSupported(SSE2)) return;
-
InitializeVM();
+ if (!CpuFeatures::IsSupported(SSE2)) return;
+
v8::HandleScope scope;
- CHECK(Isolate::Current()->cpu_features()->IsSupported(SSE2));
+ CHECK(CpuFeatures::IsSupported(SSE2));
CpuFeatures::Scope fscope(SSE2);
v8::internal::byte buffer[256];
- Assembler assm(buffer, sizeof buffer);
+ Assembler assm(Isolate::Current(), buffer, sizeof buffer);
__ mov(eax, Operand(esp, 4));
__ cvtsi2sd(xmm0, Operand(eax));
// Copy xmm0 to st(0) using eight bytes of stack.
@@ -345,7 +345,7 @@
InitializeVM();
v8::HandleScope scope;
v8::internal::byte buffer[256];
- MacroAssembler assm(buffer, sizeof buffer);
+ MacroAssembler assm(Isolate::Current(), buffer, sizeof buffer);
enum { kEqual = 0, kGreater = 1, kLess = 2, kNaN = 3, kUndefined = 4 };
Label equal_l, less_l, greater_l, nan_l;
__ fld_d(Operand(esp, 3 * kPointerSize));
diff --git a/test/cctest/test-assembler-x64.cc b/test/cctest/test-assembler-x64.cc
index 7e2115a..ea70f54 100644
--- a/test/cctest/test-assembler-x64.cc
+++ b/test/cctest/test-assembler-x64.cc
@@ -35,30 +35,30 @@
#include "serialize.h"
#include "cctest.h"
-using v8::internal::byte;
-using v8::internal::OS;
using v8::internal::Assembler;
-using v8::internal::Operand;
-using v8::internal::Immediate;
-using v8::internal::Label;
-using v8::internal::rax;
-using v8::internal::rsi;
-using v8::internal::rdi;
-using v8::internal::rcx;
-using v8::internal::rdx;
-using v8::internal::rbp;
-using v8::internal::rsp;
-using v8::internal::r8;
-using v8::internal::r9;
-using v8::internal::r13;
-using v8::internal::r15;
-using v8::internal::times_1;
-
-using v8::internal::FUNCTION_CAST;
using v8::internal::CodeDesc;
+using v8::internal::FUNCTION_CAST;
+using v8::internal::Immediate;
+using v8::internal::Isolate;
+using v8::internal::Label;
+using v8::internal::OS;
+using v8::internal::Operand;
+using v8::internal::byte;
+using v8::internal::greater;
using v8::internal::less_equal;
using v8::internal::not_equal;
-using v8::internal::greater;
+using v8::internal::r13;
+using v8::internal::r15;
+using v8::internal::r8;
+using v8::internal::r9;
+using v8::internal::rax;
+using v8::internal::rbp;
+using v8::internal::rcx;
+using v8::internal::rdi;
+using v8::internal::rdx;
+using v8::internal::rsi;
+using v8::internal::rsp;
+using v8::internal::times_1;
// Test the x64 assembler by compiling some simple functions into
// a buffer and executing them. These tests do not initialize the
@@ -93,7 +93,7 @@
&actual_size,
true));
CHECK(buffer);
- Assembler assm(buffer, static_cast<int>(actual_size));
+ Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
// Assemble a simple function that copies argument 2 and returns it.
__ movq(rax, arg2);
@@ -115,7 +115,7 @@
&actual_size,
true));
CHECK(buffer);
- Assembler assm(buffer, static_cast<int>(actual_size));
+ Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
// Assemble a simple function that copies argument 2 and returns it.
// We compile without stack frame pointers, so the gdb debugger shows
@@ -147,7 +147,7 @@
&actual_size,
true));
CHECK(buffer);
- Assembler assm(buffer, static_cast<int>(actual_size));
+ Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
// Assemble a simple function that adds arguments returning the sum.
__ movq(rax, arg2);
@@ -169,7 +169,7 @@
&actual_size,
true));
CHECK(buffer);
- Assembler assm(buffer, static_cast<int>(actual_size));
+ Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
// Assemble a simple function that multiplies arguments returning the high
// word.
@@ -197,7 +197,7 @@
&actual_size,
true));
CHECK(buffer);
- Assembler assm(buffer, static_cast<int>(actual_size));
+ Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
// Assemble a simple function that copies argument 2 and returns it.
__ push(rbp);
@@ -231,7 +231,7 @@
&actual_size,
true));
CHECK(buffer);
- Assembler assm(buffer, static_cast<int>(actual_size));
+ Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
// Assemble a simple function that copies argument 1 and returns it.
__ push(rbp);
@@ -260,7 +260,7 @@
&actual_size,
true));
CHECK(buffer);
- Assembler assm(buffer, static_cast<int>(actual_size));
+ Assembler assm(Isolate::Current(), buffer, static_cast<int>(actual_size));
// Assemble two loops using rax as counter, and verify the ending counts.
Label Fail;
__ movq(rax, Immediate(-3));
diff --git a/test/cctest/test-conversions.cc b/test/cctest/test-conversions.cc
index 1b5cc2d..7c29746 100644
--- a/test/cctest/test-conversions.cc
+++ b/test/cctest/test-conversions.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
#include <stdlib.h>
@@ -11,139 +11,151 @@
TEST(Hex) {
- CHECK_EQ(0.0, StringToDouble("0x0", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(0.0, StringToDouble("0X0", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(1.0, StringToDouble("0x1", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(16.0, StringToDouble("0x10", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(255.0, StringToDouble("0xff", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(175.0, StringToDouble("0xAF", ALLOW_HEX | ALLOW_OCTALS));
+ UnicodeCache uc;
+ CHECK_EQ(0.0, StringToDouble(&uc, "0x0", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(0.0, StringToDouble(&uc, "0X0", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(1.0, StringToDouble(&uc, "0x1", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(16.0, StringToDouble(&uc, "0x10", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(255.0, StringToDouble(&uc, "0xff", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(175.0, StringToDouble(&uc, "0xAF", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(0.0, StringToDouble("0x0", ALLOW_HEX));
- CHECK_EQ(0.0, StringToDouble("0X0", ALLOW_HEX));
- CHECK_EQ(1.0, StringToDouble("0x1", ALLOW_HEX));
- CHECK_EQ(16.0, StringToDouble("0x10", ALLOW_HEX));
- CHECK_EQ(255.0, StringToDouble("0xff", ALLOW_HEX));
- CHECK_EQ(175.0, StringToDouble("0xAF", ALLOW_HEX));
+ CHECK_EQ(0.0, StringToDouble(&uc, "0x0", ALLOW_HEX));
+ CHECK_EQ(0.0, StringToDouble(&uc, "0X0", ALLOW_HEX));
+ CHECK_EQ(1.0, StringToDouble(&uc, "0x1", ALLOW_HEX));
+ CHECK_EQ(16.0, StringToDouble(&uc, "0x10", ALLOW_HEX));
+ CHECK_EQ(255.0, StringToDouble(&uc, "0xff", ALLOW_HEX));
+ CHECK_EQ(175.0, StringToDouble(&uc, "0xAF", ALLOW_HEX));
}
TEST(Octal) {
- CHECK_EQ(0.0, StringToDouble("0", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(0.0, StringToDouble("00", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(1.0, StringToDouble("01", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(7.0, StringToDouble("07", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(8.0, StringToDouble("010", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(63.0, StringToDouble("077", ALLOW_HEX | ALLOW_OCTALS));
+ UnicodeCache uc;
+ CHECK_EQ(0.0, StringToDouble(&uc, "0", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(0.0, StringToDouble(&uc, "00", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(1.0, StringToDouble(&uc, "01", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(7.0, StringToDouble(&uc, "07", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(8.0, StringToDouble(&uc, "010", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(63.0, StringToDouble(&uc, "077", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(0.0, StringToDouble("0", ALLOW_HEX));
- CHECK_EQ(0.0, StringToDouble("00", ALLOW_HEX));
- CHECK_EQ(1.0, StringToDouble("01", ALLOW_HEX));
- CHECK_EQ(7.0, StringToDouble("07", ALLOW_HEX));
- CHECK_EQ(10.0, StringToDouble("010", ALLOW_HEX));
- CHECK_EQ(77.0, StringToDouble("077", ALLOW_HEX));
+ CHECK_EQ(0.0, StringToDouble(&uc, "0", ALLOW_HEX));
+ CHECK_EQ(0.0, StringToDouble(&uc, "00", ALLOW_HEX));
+ CHECK_EQ(1.0, StringToDouble(&uc, "01", ALLOW_HEX));
+ CHECK_EQ(7.0, StringToDouble(&uc, "07", ALLOW_HEX));
+ CHECK_EQ(10.0, StringToDouble(&uc, "010", ALLOW_HEX));
+ CHECK_EQ(77.0, StringToDouble(&uc, "077", ALLOW_HEX));
const double x = 010000000000; // Power of 2, no rounding errors.
- CHECK_EQ(x * x * x * x * x, StringToDouble("01" "0000000000" "0000000000"
+ CHECK_EQ(x * x * x * x * x, StringToDouble(&uc, "01" "0000000000" "0000000000"
"0000000000" "0000000000" "0000000000", ALLOW_OCTALS));
}
TEST(MalformedOctal) {
- CHECK_EQ(8.0, StringToDouble("08", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(81.0, StringToDouble("081", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(78.0, StringToDouble("078", ALLOW_HEX | ALLOW_OCTALS));
+ UnicodeCache uc;
+ CHECK_EQ(8.0, StringToDouble(&uc, "08", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(81.0, StringToDouble(&uc, "081", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(78.0, StringToDouble(&uc, "078", ALLOW_HEX | ALLOW_OCTALS));
- CHECK(isnan(StringToDouble("07.7", ALLOW_HEX | ALLOW_OCTALS)));
- CHECK(isnan(StringToDouble("07.8", ALLOW_HEX | ALLOW_OCTALS)));
- CHECK(isnan(StringToDouble("07e8", ALLOW_HEX | ALLOW_OCTALS)));
- CHECK(isnan(StringToDouble("07e7", ALLOW_HEX | ALLOW_OCTALS)));
+ CHECK(isnan(StringToDouble(&uc, "07.7", ALLOW_HEX | ALLOW_OCTALS)));
+ CHECK(isnan(StringToDouble(&uc, "07.8", ALLOW_HEX | ALLOW_OCTALS)));
+ CHECK(isnan(StringToDouble(&uc, "07e8", ALLOW_HEX | ALLOW_OCTALS)));
+ CHECK(isnan(StringToDouble(&uc, "07e7", ALLOW_HEX | ALLOW_OCTALS)));
- CHECK_EQ(8.7, StringToDouble("08.7", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(8e7, StringToDouble("08e7", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(8.7, StringToDouble(&uc, "08.7", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(8e7, StringToDouble(&uc, "08e7", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(0.001, StringToDouble("0.001", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(0.713, StringToDouble("0.713", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(0.001, StringToDouble(&uc, "0.001", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(0.713, StringToDouble(&uc, "0.713", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(8.0, StringToDouble("08", ALLOW_HEX));
- CHECK_EQ(81.0, StringToDouble("081", ALLOW_HEX));
- CHECK_EQ(78.0, StringToDouble("078", ALLOW_HEX));
+ CHECK_EQ(8.0, StringToDouble(&uc, "08", ALLOW_HEX));
+ CHECK_EQ(81.0, StringToDouble(&uc, "081", ALLOW_HEX));
+ CHECK_EQ(78.0, StringToDouble(&uc, "078", ALLOW_HEX));
- CHECK_EQ(7.7, StringToDouble("07.7", ALLOW_HEX));
- CHECK_EQ(7.8, StringToDouble("07.8", ALLOW_HEX));
- CHECK_EQ(7e8, StringToDouble("07e8", ALLOW_HEX));
- CHECK_EQ(7e7, StringToDouble("07e7", ALLOW_HEX));
+ CHECK_EQ(7.7, StringToDouble(&uc, "07.7", ALLOW_HEX));
+ CHECK_EQ(7.8, StringToDouble(&uc, "07.8", ALLOW_HEX));
+ CHECK_EQ(7e8, StringToDouble(&uc, "07e8", ALLOW_HEX));
+ CHECK_EQ(7e7, StringToDouble(&uc, "07e7", ALLOW_HEX));
- CHECK_EQ(8.7, StringToDouble("08.7", ALLOW_HEX));
- CHECK_EQ(8e7, StringToDouble("08e7", ALLOW_HEX));
+ CHECK_EQ(8.7, StringToDouble(&uc, "08.7", ALLOW_HEX));
+ CHECK_EQ(8e7, StringToDouble(&uc, "08e7", ALLOW_HEX));
- CHECK_EQ(0.001, StringToDouble("0.001", ALLOW_HEX));
- CHECK_EQ(0.713, StringToDouble("0.713", ALLOW_HEX));
+ CHECK_EQ(0.001, StringToDouble(&uc, "0.001", ALLOW_HEX));
+ CHECK_EQ(0.713, StringToDouble(&uc, "0.713", ALLOW_HEX));
}
TEST(TrailingJunk) {
- CHECK_EQ(8.0, StringToDouble("8q", ALLOW_TRAILING_JUNK));
- CHECK_EQ(63.0, StringToDouble("077qqq", ALLOW_OCTALS | ALLOW_TRAILING_JUNK));
- CHECK_EQ(10.0, StringToDouble("10e", ALLOW_OCTALS | ALLOW_TRAILING_JUNK));
- CHECK_EQ(10.0, StringToDouble("10e-", ALLOW_OCTALS | ALLOW_TRAILING_JUNK));
+ UnicodeCache uc;
+ CHECK_EQ(8.0, StringToDouble(&uc, "8q", ALLOW_TRAILING_JUNK));
+ CHECK_EQ(63.0,
+ StringToDouble(&uc, "077qqq", ALLOW_OCTALS | ALLOW_TRAILING_JUNK));
+ CHECK_EQ(10.0,
+ StringToDouble(&uc, "10e", ALLOW_OCTALS | ALLOW_TRAILING_JUNK));
+ CHECK_EQ(10.0,
+ StringToDouble(&uc, "10e-", ALLOW_OCTALS | ALLOW_TRAILING_JUNK));
}
TEST(NonStrDecimalLiteral) {
- CHECK(isnan(StringToDouble(" ", NO_FLAGS, OS::nan_value())));
- CHECK(isnan(StringToDouble("", NO_FLAGS, OS::nan_value())));
- CHECK(isnan(StringToDouble(" ", NO_FLAGS, OS::nan_value())));
- CHECK_EQ(0.0, StringToDouble("", NO_FLAGS));
- CHECK_EQ(0.0, StringToDouble(" ", NO_FLAGS));
+ UnicodeCache uc;
+ CHECK(isnan(StringToDouble(&uc, " ", NO_FLAGS, OS::nan_value())));
+ CHECK(isnan(StringToDouble(&uc, "", NO_FLAGS, OS::nan_value())));
+ CHECK(isnan(StringToDouble(&uc, " ", NO_FLAGS, OS::nan_value())));
+ CHECK_EQ(0.0, StringToDouble(&uc, "", NO_FLAGS));
+ CHECK_EQ(0.0, StringToDouble(&uc, " ", NO_FLAGS));
}
TEST(IntegerStrLiteral) {
- CHECK_EQ(0.0, StringToDouble("0.0", NO_FLAGS));
- CHECK_EQ(0.0, StringToDouble("0", NO_FLAGS));
- CHECK_EQ(0.0, StringToDouble("00", NO_FLAGS));
- CHECK_EQ(0.0, StringToDouble("000", NO_FLAGS));
- CHECK_EQ(1.0, StringToDouble("1", NO_FLAGS));
- CHECK_EQ(-1.0, StringToDouble("-1", NO_FLAGS));
- CHECK_EQ(-1.0, StringToDouble(" -1 ", NO_FLAGS));
- CHECK_EQ(1.0, StringToDouble(" +1 ", NO_FLAGS));
- CHECK(isnan(StringToDouble(" - 1 ", NO_FLAGS)));
- CHECK(isnan(StringToDouble(" + 1 ", NO_FLAGS)));
+ UnicodeCache uc;
+ CHECK_EQ(0.0, StringToDouble(&uc, "0.0", NO_FLAGS));
+ CHECK_EQ(0.0, StringToDouble(&uc, "0", NO_FLAGS));
+ CHECK_EQ(0.0, StringToDouble(&uc, "00", NO_FLAGS));
+ CHECK_EQ(0.0, StringToDouble(&uc, "000", NO_FLAGS));
+ CHECK_EQ(1.0, StringToDouble(&uc, "1", NO_FLAGS));
+ CHECK_EQ(-1.0, StringToDouble(&uc, "-1", NO_FLAGS));
+ CHECK_EQ(-1.0, StringToDouble(&uc, " -1 ", NO_FLAGS));
+ CHECK_EQ(1.0, StringToDouble(&uc, " +1 ", NO_FLAGS));
+ CHECK(isnan(StringToDouble(&uc, " - 1 ", NO_FLAGS)));
+ CHECK(isnan(StringToDouble(&uc, " + 1 ", NO_FLAGS)));
- CHECK_EQ(0.0, StringToDouble("0e0", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(0.0, StringToDouble("0e1", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(0.0, StringToDouble("0e-1", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(0.0, StringToDouble("0e-100000", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(0.0, StringToDouble("0e+100000", ALLOW_HEX | ALLOW_OCTALS));
- CHECK_EQ(0.0, StringToDouble("0.", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(0.0, StringToDouble(&uc, "0e0", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(0.0, StringToDouble(&uc, "0e1", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(0.0, StringToDouble(&uc, "0e-1", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(0.0, StringToDouble(&uc, "0e-100000", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(0.0, StringToDouble(&uc, "0e+100000", ALLOW_HEX | ALLOW_OCTALS));
+ CHECK_EQ(0.0, StringToDouble(&uc, "0.", ALLOW_HEX | ALLOW_OCTALS));
}
TEST(LongNumberStr) {
- CHECK_EQ(1e10, StringToDouble("1" "0000000000", NO_FLAGS));
- CHECK_EQ(1e20, StringToDouble("1" "0000000000" "0000000000", NO_FLAGS));
+ UnicodeCache uc;
+ CHECK_EQ(1e10, StringToDouble(&uc, "1" "0000000000", NO_FLAGS));
+ CHECK_EQ(1e20, StringToDouble(&uc, "1" "0000000000" "0000000000", NO_FLAGS));
- CHECK_EQ(1e60, StringToDouble("1" "0000000000" "0000000000" "0000000000"
+ CHECK_EQ(1e60, StringToDouble(&uc, "1" "0000000000" "0000000000" "0000000000"
"0000000000" "0000000000" "0000000000", NO_FLAGS));
- CHECK_EQ(1e-2, StringToDouble("." "0" "1", NO_FLAGS));
- CHECK_EQ(1e-11, StringToDouble("." "0000000000" "1", NO_FLAGS));
- CHECK_EQ(1e-21, StringToDouble("." "0000000000" "0000000000" "1", NO_FLAGS));
+ CHECK_EQ(1e-2, StringToDouble(&uc, "." "0" "1", NO_FLAGS));
+ CHECK_EQ(1e-11, StringToDouble(&uc, "." "0000000000" "1", NO_FLAGS));
+ CHECK_EQ(1e-21, StringToDouble(&uc, "." "0000000000" "0000000000" "1",
+ NO_FLAGS));
- CHECK_EQ(1e-61, StringToDouble("." "0000000000" "0000000000" "0000000000"
+ CHECK_EQ(1e-61, StringToDouble(&uc, "." "0000000000" "0000000000" "0000000000"
"0000000000" "0000000000" "0000000000" "1", NO_FLAGS));
// x = 24414062505131248.0 and y = 24414062505131252.0 are representable in
// double. Check chat z = (x + y) / 2 is rounded to x...
CHECK_EQ(24414062505131248.0,
- StringToDouble("24414062505131250.0", NO_FLAGS));
+ StringToDouble(&uc, "24414062505131250.0", NO_FLAGS));
// ... and z = (x + y) / 2 + delta is rounded to y.
CHECK_EQ(24414062505131252.0,
- StringToDouble("24414062505131250.000000001", NO_FLAGS));
+ StringToDouble(&uc, "24414062505131250.000000001", NO_FLAGS));
}
TEST(MaximumSignificantDigits) {
+ UnicodeCache uc;
char num[] =
"4.4501477170144020250819966727949918635852426585926051135169509"
"122872622312493126406953054127118942431783801370080830523154578"
@@ -159,15 +171,16 @@
"847003580761626016356864581135848683152156368691976240370422601"
"6998291015625000000000000000000000000000000000e-308";
- CHECK_EQ(4.4501477170144017780491e-308, StringToDouble(num, NO_FLAGS));
+ CHECK_EQ(4.4501477170144017780491e-308, StringToDouble(&uc, num, NO_FLAGS));
// Changes the result of strtod (at least in glibc implementation).
num[sizeof(num) - 8] = '1';
- CHECK_EQ(4.4501477170144022721148e-308, StringToDouble(num, NO_FLAGS));
+ CHECK_EQ(4.4501477170144022721148e-308, StringToDouble(&uc, num, NO_FLAGS));
}
TEST(MinimumExponent) {
+ UnicodeCache uc;
// Same test but with different point-position.
char num[] =
"445014771701440202508199667279499186358524265859260511351695091"
@@ -184,29 +197,31 @@
"470035807616260163568645811358486831521563686919762403704226016"
"998291015625000000000000000000000000000000000e-1108";
- CHECK_EQ(4.4501477170144017780491e-308, StringToDouble(num, NO_FLAGS));
+ CHECK_EQ(4.4501477170144017780491e-308, StringToDouble(&uc, num, NO_FLAGS));
// Changes the result of strtod (at least in glibc implementation).
num[sizeof(num) - 8] = '1';
- CHECK_EQ(4.4501477170144022721148e-308, StringToDouble(num, NO_FLAGS));
+ CHECK_EQ(4.4501477170144022721148e-308, StringToDouble(&uc, num, NO_FLAGS));
}
TEST(MaximumExponent) {
+ UnicodeCache uc;
char num[] = "0.16e309";
- CHECK_EQ(1.59999999999999997765e+308, StringToDouble(num, NO_FLAGS));
+ CHECK_EQ(1.59999999999999997765e+308, StringToDouble(&uc, num, NO_FLAGS));
}
TEST(ExponentNumberStr) {
- CHECK_EQ(1e1, StringToDouble("1e1", NO_FLAGS));
- CHECK_EQ(1e1, StringToDouble("1e+1", NO_FLAGS));
- CHECK_EQ(1e-1, StringToDouble("1e-1", NO_FLAGS));
- CHECK_EQ(1e100, StringToDouble("1e+100", NO_FLAGS));
- CHECK_EQ(1e-100, StringToDouble("1e-100", NO_FLAGS));
- CHECK_EQ(1e-106, StringToDouble(".000001e-100", NO_FLAGS));
+ UnicodeCache uc;
+ CHECK_EQ(1e1, StringToDouble(&uc, "1e1", NO_FLAGS));
+ CHECK_EQ(1e1, StringToDouble(&uc, "1e+1", NO_FLAGS));
+ CHECK_EQ(1e-1, StringToDouble(&uc, "1e-1", NO_FLAGS));
+ CHECK_EQ(1e100, StringToDouble(&uc, "1e+100", NO_FLAGS));
+ CHECK_EQ(1e-100, StringToDouble(&uc, "1e-100", NO_FLAGS));
+ CHECK_EQ(1e-106, StringToDouble(&uc, ".000001e-100", NO_FLAGS));
}
class OneBit1: public BitField<uint32_t, 0, 1> {};
diff --git a/test/cctest/test-disasm-arm.cc b/test/cctest/test-disasm-arm.cc
index 3221614..65a2cf3 100644
--- a/test/cctest/test-disasm-arm.cc
+++ b/test/cctest/test-disasm-arm.cc
@@ -1,4 +1,4 @@
-// Copyright 2007-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -72,11 +72,11 @@
// Setup V8 to a state where we can at least run the assembler and
// disassembler. Declare the variables and allocate the data structures used
// in the rest of the macros.
-#define SETUP() \
- InitializeVM(); \
- v8::HandleScope scope; \
+#define SETUP() \
+ InitializeVM(); \
+ v8::HandleScope scope; \
byte *buffer = reinterpret_cast<byte*>(malloc(4*1024)); \
- Assembler assm(buffer, 4*1024); \
+ Assembler assm(Isolate::Current(), buffer, 4*1024); \
bool failure = false;
@@ -270,7 +270,7 @@
"13a06000 movne r6, #0");
// mov -> movw.
- if (Isolate::Current()->cpu_features()->IsSupported(ARMv7)) {
+ if (CpuFeatures::IsSupported(ARMv7)) {
COMPARE(mov(r5, Operand(0x01234), LeaveCC, ne),
"13015234 movwne r5, #4660");
// We only disassemble one instruction so the eor instruction is not here.
@@ -360,7 +360,7 @@
TEST(Type3) {
SETUP();
- if (Isolate::Current()->cpu_features()->IsSupported(ARMv7)) {
+ if (CpuFeatures::IsSupported(ARMv7)) {
COMPARE(ubfx(r0, r1, 5, 10),
"e7e902d1 ubfx r0, r1, #5, #10");
COMPARE(ubfx(r1, r0, 5, 10),
@@ -415,7 +415,7 @@
TEST(Vfp) {
SETUP();
- if (Isolate::Current()->cpu_features()->IsSupported(VFP3)) {
+ if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
COMPARE(vmov(d0, d1),
"eeb00b41 vmov.f64 d0, d1");
@@ -522,6 +522,23 @@
"aef1aa10 vmrsge r10, FPSCR");
COMPARE(vmrs(pc),
"eef1fa10 vmrs APSR, FPSCR");
+
+ COMPARE(vstm(ia, r0, d1, d3),
+ "ec801b06 vstmia r0, {d1-d3}");
+ COMPARE(vldm(ia, r1, d2, d5),
+ "ec912b08 vldmia r1, {d2-d5}");
+ COMPARE(vstm(ia, r2, d0, d15),
+ "ec820b20 vstmia r2, {d0-d15}");
+ COMPARE(vldm(ia, r3, d0, d15),
+ "ec930b20 vldmia r3, {d0-d15}");
+ COMPARE(vstm(ia, r4, s1, s3),
+ "ecc40a03 vstmia r4, {s1-s3}");
+ COMPARE(vldm(ia, r5, s2, s5),
+ "ec951a04 vldmia r5, {s2-s5}");
+ COMPARE(vstm(ia, r6, s0, s31),
+ "ec860a20 vstmia r6, {s0-s31}");
+ COMPARE(vldm(ia, r7, s0, s31),
+ "ec970a20 vldmia r7, {s0-s31}");
}
VERIFY_RUN();
diff --git a/test/cctest/test-disasm-ia32.cc b/test/cctest/test-disasm-ia32.cc
index 26da5c9..cb735c7 100644
--- a/test/cctest/test-disasm-ia32.cc
+++ b/test/cctest/test-disasm-ia32.cc
@@ -58,7 +58,7 @@
InitializeVM();
v8::HandleScope scope;
v8::internal::byte buffer[2048];
- Assembler assm(buffer, sizeof buffer);
+ Assembler assm(Isolate::Current(), buffer, sizeof buffer);
DummyStaticFunction(NULL); // just bloody use it (DELETE; debugging)
// Short immediate instructions
@@ -107,12 +107,12 @@
__ xor_(edx, 3);
__ nop();
{
- CHECK(Isolate::Current()->cpu_features()->IsSupported(CPUID));
+ CHECK(CpuFeatures::IsSupported(CPUID));
CpuFeatures::Scope fscope(CPUID);
__ cpuid();
}
{
- CHECK(Isolate::Current()->cpu_features()->IsSupported(RDTSC));
+ CHECK(CpuFeatures::IsSupported(RDTSC));
CpuFeatures::Scope fscope(RDTSC);
__ rdtsc();
}
@@ -375,7 +375,7 @@
__ fwait();
__ nop();
{
- if (Isolate::Current()->cpu_features()->IsSupported(SSE2)) {
+ if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope fscope(SSE2);
__ cvttss2si(edx, Operand(ebx, ecx, times_4, 10000));
__ cvtsi2sd(xmm1, Operand(ebx, ecx, times_4, 10000));
@@ -397,7 +397,7 @@
// cmov.
{
- if (Isolate::Current()->cpu_features()->IsSupported(CMOV)) {
+ if (CpuFeatures::IsSupported(CMOV)) {
CpuFeatures::Scope use_cmov(CMOV);
__ cmov(overflow, eax, Operand(eax, 0));
__ cmov(no_overflow, eax, Operand(eax, 1));
@@ -420,7 +420,7 @@
// andpd, cmpltsd, movaps, psllq, psrlq, por.
{
- if (Isolate::Current()->cpu_features()->IsSupported(SSE2)) {
+ if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope fscope(SSE2);
__ andpd(xmm0, xmm1);
__ andpd(xmm1, xmm2);
@@ -449,7 +449,7 @@
}
{
- if (Isolate::Current()->cpu_features()->IsSupported(SSE4_1)) {
+ if (CpuFeatures::IsSupported(SSE4_1)) {
CpuFeatures::Scope scope(SSE4_1);
__ pextrd(Operand(eax), xmm0, 1);
__ pinsrd(xmm1, Operand(eax), 0);
diff --git a/test/cctest/test-heap-profiler.cc b/test/cctest/test-heap-profiler.cc
index 141b42f..bd08d4c 100644
--- a/test/cctest/test-heap-profiler.cc
+++ b/test/cctest/test-heap-profiler.cc
@@ -574,10 +574,10 @@
// Find references to code.
const v8::HeapGraphNode* compiled_code =
- GetProperty(compiled, v8::HeapGraphEdge::kInternal, "code");
+ GetProperty(compiled, v8::HeapGraphEdge::kInternal, "shared");
CHECK_NE(NULL, compiled_code);
const v8::HeapGraphNode* lazy_code =
- GetProperty(lazy, v8::HeapGraphEdge::kInternal, "code");
+ GetProperty(lazy, v8::HeapGraphEdge::kInternal, "shared");
CHECK_NE(NULL, lazy_code);
// Verify that non-compiled code doesn't contain references to "x"
@@ -1257,9 +1257,9 @@
ccc, v8::HeapGraphNode::kString, "CCC");
CHECK_NE(NULL, n_CCC);
- CHECK_EQ(aaa, GetProperty(n_AAA, v8::HeapGraphEdge::kInternal, "Native"));
- CHECK_EQ(aaa, GetProperty(n_BBB, v8::HeapGraphEdge::kInternal, "Native"));
- CHECK_EQ(ccc, GetProperty(n_CCC, v8::HeapGraphEdge::kInternal, "Native"));
+ CHECK_EQ(aaa, GetProperty(n_AAA, v8::HeapGraphEdge::kInternal, "native"));
+ CHECK_EQ(aaa, GetProperty(n_BBB, v8::HeapGraphEdge::kInternal, "native"));
+ CHECK_EQ(ccc, GetProperty(n_CCC, v8::HeapGraphEdge::kInternal, "native"));
}
diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc
index 86860ce..09aa613 100644
--- a/test/cctest/test-heap.cc
+++ b/test/cctest/test-heap.cc
@@ -70,7 +70,7 @@
// Test FindCodeObject
#define __ assm.
- Assembler assm(NULL, 0);
+ Assembler assm(Isolate::Current(), NULL, 0);
__ nop(); // supported on all architectures
diff --git a/test/cctest/test-log-stack-tracer.cc b/test/cctest/test-log-stack-tracer.cc
index df0806f..b967c73 100644
--- a/test/cctest/test-log-stack-tracer.cc
+++ b/test/cctest/test-log-stack-tracer.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -39,7 +39,6 @@
#include "isolate.h"
#include "cctest.h"
#include "disassembler.h"
-#include "register-allocator-inl.h"
#include "vm-state-inl.h"
using v8::Function;
@@ -280,6 +279,9 @@
// StackTracer uses Isolate::c_entry_fp as a starting point for stack
// walking.
TEST(CFromJSStackTrace) {
+ // BUG(1303) Inlining of JSFuncDoTrace() in JSTrace below breaks this test.
+ i::FLAG_use_inlining = false;
+
TickSample sample;
InitTraceEnv(&sample);
diff --git a/test/cctest/test-macro-assembler-x64.cc b/test/cctest/test-macro-assembler-x64.cc
index c7c67b0..59eeed9 100755
--- a/test/cctest/test-macro-assembler-x64.cc
+++ b/test/cctest/test-macro-assembler-x64.cc
@@ -35,48 +35,49 @@
#include "serialize.h"
#include "cctest.h"
-using v8::internal::byte;
-using v8::internal::OS;
using v8::internal::Assembler;
+using v8::internal::CodeDesc;
using v8::internal::Condition;
-using v8::internal::MacroAssembler;
+using v8::internal::FUNCTION_CAST;
using v8::internal::HandleScope;
-using v8::internal::Operand;
using v8::internal::Immediate;
-using v8::internal::SmiIndex;
+using v8::internal::Isolate;
using v8::internal::Label;
+using v8::internal::MacroAssembler;
+using v8::internal::OS;
+using v8::internal::Operand;
using v8::internal::RelocInfo;
-using v8::internal::rax;
-using v8::internal::rbx;
-using v8::internal::rsi;
-using v8::internal::rdi;
-using v8::internal::rcx;
-using v8::internal::rdx;
-using v8::internal::rbp;
-using v8::internal::rsp;
-using v8::internal::r8;
-using v8::internal::r9;
+using v8::internal::Smi;
+using v8::internal::SmiIndex;
+using v8::internal::byte;
+using v8::internal::carry;
+using v8::internal::greater;
+using v8::internal::greater_equal;
+using v8::internal::kIntSize;
+using v8::internal::kPointerSize;
+using v8::internal::kSmiTagMask;
+using v8::internal::kSmiValueSize;
+using v8::internal::less_equal;
+using v8::internal::negative;
+using v8::internal::not_carry;
+using v8::internal::not_equal;
+using v8::internal::not_zero;
+using v8::internal::positive;
using v8::internal::r11;
using v8::internal::r13;
using v8::internal::r14;
using v8::internal::r15;
+using v8::internal::r8;
+using v8::internal::r9;
+using v8::internal::rax;
+using v8::internal::rbp;
+using v8::internal::rbx;
+using v8::internal::rcx;
+using v8::internal::rdi;
+using v8::internal::rdx;
+using v8::internal::rsi;
+using v8::internal::rsp;
using v8::internal::times_pointer_size;
-using v8::internal::FUNCTION_CAST;
-using v8::internal::CodeDesc;
-using v8::internal::less_equal;
-using v8::internal::not_equal;
-using v8::internal::not_zero;
-using v8::internal::greater;
-using v8::internal::greater_equal;
-using v8::internal::carry;
-using v8::internal::not_carry;
-using v8::internal::negative;
-using v8::internal::positive;
-using v8::internal::Smi;
-using v8::internal::kSmiTagMask;
-using v8::internal::kSmiValueSize;
-using v8::internal::kPointerSize;
-using v8::internal::kIntSize;
// Test the x64 assembler by compiling some simple functions into
// a buffer and executing them. These tests do not initialize the
@@ -157,7 +158,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler; // Create a pointer for the __ macro.
masm->set_allow_stub_calls(false);
EntryCode(masm);
@@ -245,7 +248,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
@@ -295,7 +300,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
@@ -423,7 +430,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
@@ -467,7 +476,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
@@ -715,7 +726,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
@@ -803,7 +816,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
@@ -993,7 +1008,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
@@ -1083,7 +1100,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
@@ -1189,7 +1208,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
@@ -1299,7 +1320,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
@@ -1395,7 +1418,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
@@ -1464,7 +1489,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false); // Avoid inline checks.
@@ -1543,7 +1570,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
@@ -1624,7 +1653,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
@@ -1707,7 +1738,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
@@ -1774,7 +1807,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
@@ -1870,7 +1905,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
@@ -1976,7 +2013,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
@@ -2045,7 +2084,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
@@ -2109,7 +2150,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
@@ -2152,7 +2195,9 @@
true));
CHECK(buffer);
HandleScope handles;
- MacroAssembler assembler(buffer, static_cast<int>(actual_size));
+ MacroAssembler assembler(Isolate::Current(),
+ buffer,
+ static_cast<int>(actual_size));
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
diff --git a/test/cctest/test-mark-compact.cc b/test/cctest/test-mark-compact.cc
index 6d1b5ce..dcb51a0 100644
--- a/test/cctest/test-mark-compact.cc
+++ b/test/cctest/test-mark-compact.cc
@@ -299,8 +299,8 @@
}
TEST(ObjectGroups) {
- GlobalHandles* global_handles = Isolate::Current()->global_handles();
InitializeVM();
+ GlobalHandles* global_handles = Isolate::Current()->global_handles();
NumberOfWeakCalls = 0;
v8::HandleScope handle_scope;
@@ -308,9 +308,9 @@
Handle<Object> g1s1 =
global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked());
Handle<Object> g1s2 =
- global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked());
+ global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked());
Handle<Object> g1c1 =
- global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked());
+ global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked());
global_handles->MakeWeak(g1s1.location(),
reinterpret_cast<void*>(1234),
&WeakPointerCallback);
@@ -349,11 +349,11 @@
Object** g2_objects[] = { g2s1.location(), g2s2.location() };
Object** g2_children[] = { g2c1.location() };
global_handles->AddObjectGroup(g1_objects, 2, NULL);
- global_handles->AddImplicitReferences(HeapObject::cast(*g1s1),
- g1_children, 1);
+ global_handles->AddImplicitReferences(
+ Handle<HeapObject>::cast(g1s1).location(), g1_children, 1);
global_handles->AddObjectGroup(g2_objects, 2, NULL);
- global_handles->AddImplicitReferences(HeapObject::cast(*g2s2),
- g2_children, 1);
+ global_handles->AddImplicitReferences(
+ Handle<HeapObject>::cast(g2s2).location(), g2_children, 1);
}
// Do a full GC
HEAP->CollectGarbage(OLD_POINTER_SPACE);
@@ -377,11 +377,11 @@
Object** g2_objects[] = { g2s1.location(), g2s2.location() };
Object** g2_children[] = { g2c1.location() };
global_handles->AddObjectGroup(g1_objects, 2, NULL);
- global_handles->AddImplicitReferences(HeapObject::cast(*g1s1),
- g1_children, 1);
+ global_handles->AddImplicitReferences(
+ Handle<HeapObject>::cast(g1s1).location(), g1_children, 1);
global_handles->AddObjectGroup(g2_objects, 2, NULL);
- global_handles->AddImplicitReferences(HeapObject::cast(*g2s2),
- g2_children, 1);
+ global_handles->AddImplicitReferences(
+ Handle<HeapObject>::cast(g2s2).location(), g2_children, 1);
}
HEAP->CollectGarbage(OLD_POINTER_SPACE);
@@ -400,3 +400,45 @@
HEAP->CollectGarbage(OLD_POINTER_SPACE);
CHECK_EQ(7, NumberOfWeakCalls);
}
+
+
+class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
+ public:
+ TestRetainedObjectInfo() : has_been_disposed_(false) {}
+
+ bool has_been_disposed() { return has_been_disposed_; }
+
+ virtual void Dispose() {
+ ASSERT(!has_been_disposed_);
+ has_been_disposed_ = true;
+ }
+
+ virtual bool IsEquivalent(v8::RetainedObjectInfo* other) {
+ return other == this;
+ }
+
+ virtual intptr_t GetHash() { return 0; }
+
+ virtual const char* GetLabel() { return "whatever"; }
+
+ private:
+ bool has_been_disposed_;
+};
+
+
+TEST(EmptyObjectGroups) {
+ InitializeVM();
+ GlobalHandles* global_handles = Isolate::Current()->global_handles();
+
+ v8::HandleScope handle_scope;
+
+ Handle<Object> object =
+ global_handles->Create(HEAP->AllocateFixedArray(1)->ToObjectChecked());
+
+ TestRetainedObjectInfo info;
+ global_handles->AddObjectGroup(NULL, 0, &info);
+ ASSERT(info.has_been_disposed());
+
+ global_handles->AddImplicitReferences(
+ Handle<HeapObject>::cast(object).location(), NULL, 0);
+}
diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc
index 98a5870..39856b6 100755
--- a/test/cctest/test-parsing.cc
+++ b/test/cctest/test-parsing.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2009 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -258,14 +258,14 @@
NULL
};
- uintptr_t stack_limit = ISOLATE->stack_guard()->real_climit();
+ uintptr_t stack_limit = i::Isolate::Current()->stack_guard()->real_climit();
for (int i = 0; programs[i]; i++) {
const char* program = programs[i];
i::Utf8ToUC16CharacterStream stream(
reinterpret_cast<const i::byte*>(program),
static_cast<unsigned>(strlen(program)));
i::CompleteParserRecorder log;
- i::V8JavaScriptScanner scanner(ISOLATE->scanner_constants());
+ i::V8JavaScriptScanner scanner(i::Isolate::Current()->unicode_cache());
scanner.Initialize(&stream);
v8::preparser::PreParser::PreParseResult result =
@@ -282,7 +282,7 @@
TEST(RegressChromium62639) {
int marker;
- ISOLATE->stack_guard()->SetStackLimit(
+ i::Isolate::Current()->stack_guard()->SetStackLimit(
reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
const char* program = "var x = 'something';\n"
@@ -307,7 +307,7 @@
// the block could be lazily compiled, and an extra, unexpected,
// entry was added to the data.
int marker;
- ISOLATE->stack_guard()->SetStackLimit(
+ i::Isolate::Current()->stack_guard()->SetStackLimit(
reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
const char* program =
@@ -343,7 +343,7 @@
TEST(PreParseOverflow) {
int marker;
- ISOLATE->stack_guard()->SetStackLimit(
+ i::Isolate::Current()->stack_guard()->SetStackLimit(
reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
size_t kProgramSize = 1024 * 1024;
@@ -352,13 +352,13 @@
memset(*program, '(', kProgramSize);
program[kProgramSize] = '\0';
- uintptr_t stack_limit = ISOLATE->stack_guard()->real_climit();
+ uintptr_t stack_limit = i::Isolate::Current()->stack_guard()->real_climit();
i::Utf8ToUC16CharacterStream stream(
reinterpret_cast<const i::byte*>(*program),
static_cast<unsigned>(kProgramSize));
i::CompleteParserRecorder log;
- i::V8JavaScriptScanner scanner(ISOLATE->scanner_constants());
+ i::V8JavaScriptScanner scanner(i::Isolate::Current()->unicode_cache());
scanner.Initialize(&stream);
@@ -576,7 +576,7 @@
i::Token::Value* expected_tokens,
int skip_pos = 0, // Zero means not skipping.
int skip_to = 0) {
- i::V8JavaScriptScanner scanner(ISOLATE->scanner_constants());
+ i::V8JavaScriptScanner scanner(i::Isolate::Current()->unicode_cache());
scanner.Initialize(stream);
int i = 0;
@@ -655,7 +655,7 @@
i::Utf8ToUC16CharacterStream stream(
reinterpret_cast<const i::byte*>(re_source),
static_cast<unsigned>(strlen(re_source)));
- i::V8JavaScriptScanner scanner(ISOLATE->scanner_constants());
+ i::V8JavaScriptScanner scanner(i::Isolate::Current()->unicode_cache());
scanner.Initialize(&stream);
i::Token::Value start = scanner.peek();
diff --git a/test/cctest/test-regexp.cc b/test/cctest/test-regexp.cc
index e7026d4..fa3c6ea 100644
--- a/test/cctest/test-regexp.cc
+++ b/test/cctest/test-regexp.cc
@@ -488,12 +488,14 @@
static RegExpNode* Compile(const char* input, bool multiline, bool is_ascii) {
V8::Initialize(NULL);
- FlatStringReader reader(Isolate::Current(), CStrVector(input));
+ Isolate* isolate = Isolate::Current();
+ FlatStringReader reader(isolate, CStrVector(input));
RegExpCompileData compile_data;
if (!v8::internal::RegExpParser::ParseRegExp(&reader, multiline,
&compile_data))
return NULL;
- Handle<String> pattern = FACTORY->NewStringFromUtf8(CStrVector(input));
+ Handle<String> pattern = isolate->factory()->
+ NewStringFromUtf8(CStrVector(input));
RegExpEngine::Compile(&compile_data, false, multiline, pattern, is_ascii);
return compile_data.node;
}
@@ -715,17 +717,18 @@
TEST(MacroAssemblerNativeSuccess) {
v8::V8::Initialize();
ContextInitializer initializer;
+ Factory* factory = Isolate::Current()->factory();
ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4);
m.Succeed();
- Handle<String> source = FACTORY->NewStringFromAscii(CStrVector(""));
+ Handle<String> source = factory->NewStringFromAscii(CStrVector(""));
Handle<Object> code_object = m.GetCode(source);
Handle<Code> code = Handle<Code>::cast(code_object);
int captures[4] = {42, 37, 87, 117};
- Handle<String> input = FACTORY->NewStringFromAscii(CStrVector("foofoo"));
+ Handle<String> input = factory->NewStringFromAscii(CStrVector("foofoo"));
Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
const byte* start_adr =
reinterpret_cast<const byte*>(seq_input->GetCharsAddress());
@@ -749,6 +752,7 @@
TEST(MacroAssemblerNativeSimple) {
v8::V8::Initialize();
ContextInitializer initializer;
+ Factory* factory = Isolate::Current()->factory();
ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4);
@@ -764,12 +768,12 @@
m.Bind(&fail);
m.Fail();
- Handle<String> source = FACTORY->NewStringFromAscii(CStrVector("^foo"));
+ Handle<String> source = factory->NewStringFromAscii(CStrVector("^foo"));
Handle<Object> code_object = m.GetCode(source);
Handle<Code> code = Handle<Code>::cast(code_object);
int captures[4] = {42, 37, 87, 117};
- Handle<String> input = FACTORY->NewStringFromAscii(CStrVector("foofoo"));
+ Handle<String> input = factory->NewStringFromAscii(CStrVector("foofoo"));
Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
Address start_adr = seq_input->GetCharsAddress();
@@ -787,7 +791,7 @@
CHECK_EQ(-1, captures[2]);
CHECK_EQ(-1, captures[3]);
- input = FACTORY->NewStringFromAscii(CStrVector("barbarbar"));
+ input = factory->NewStringFromAscii(CStrVector("barbarbar"));
seq_input = Handle<SeqAsciiString>::cast(input);
start_adr = seq_input->GetCharsAddress();
@@ -805,6 +809,7 @@
TEST(MacroAssemblerNativeSimpleUC16) {
v8::V8::Initialize();
ContextInitializer initializer;
+ Factory* factory = Isolate::Current()->factory();
ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::UC16, 4);
@@ -820,14 +825,14 @@
m.Bind(&fail);
m.Fail();
- Handle<String> source = FACTORY->NewStringFromAscii(CStrVector("^foo"));
+ Handle<String> source = factory->NewStringFromAscii(CStrVector("^foo"));
Handle<Object> code_object = m.GetCode(source);
Handle<Code> code = Handle<Code>::cast(code_object);
int captures[4] = {42, 37, 87, 117};
const uc16 input_data[6] = {'f', 'o', 'o', 'f', 'o', '\xa0'};
Handle<String> input =
- FACTORY->NewStringFromTwoByte(Vector<const uc16>(input_data, 6));
+ factory->NewStringFromTwoByte(Vector<const uc16>(input_data, 6));
Handle<SeqTwoByteString> seq_input = Handle<SeqTwoByteString>::cast(input);
Address start_adr = seq_input->GetCharsAddress();
@@ -846,7 +851,7 @@
CHECK_EQ(-1, captures[3]);
const uc16 input_data2[9] = {'b', 'a', 'r', 'b', 'a', 'r', 'b', 'a', '\xa0'};
- input = FACTORY->NewStringFromTwoByte(Vector<const uc16>(input_data2, 9));
+ input = factory->NewStringFromTwoByte(Vector<const uc16>(input_data2, 9));
seq_input = Handle<SeqTwoByteString>::cast(input);
start_adr = seq_input->GetCharsAddress();
@@ -864,6 +869,7 @@
TEST(MacroAssemblerNativeBacktrack) {
v8::V8::Initialize();
ContextInitializer initializer;
+ Factory* factory = Isolate::Current()->factory();
ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 0);
@@ -878,11 +884,11 @@
m.Bind(&backtrack);
m.Fail();
- Handle<String> source = FACTORY->NewStringFromAscii(CStrVector(".........."));
+ Handle<String> source = factory->NewStringFromAscii(CStrVector(".........."));
Handle<Object> code_object = m.GetCode(source);
Handle<Code> code = Handle<Code>::cast(code_object);
- Handle<String> input = FACTORY->NewStringFromAscii(CStrVector("foofoo"));
+ Handle<String> input = factory->NewStringFromAscii(CStrVector("foofoo"));
Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
Address start_adr = seq_input->GetCharsAddress();
@@ -901,6 +907,7 @@
TEST(MacroAssemblerNativeBackReferenceASCII) {
v8::V8::Initialize();
ContextInitializer initializer;
+ Factory* factory = Isolate::Current()->factory();
ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4);
@@ -919,11 +926,11 @@
m.Bind(&missing_match);
m.Fail();
- Handle<String> source = FACTORY->NewStringFromAscii(CStrVector("^(..)..\1"));
+ Handle<String> source = factory->NewStringFromAscii(CStrVector("^(..)..\1"));
Handle<Object> code_object = m.GetCode(source);
Handle<Code> code = Handle<Code>::cast(code_object);
- Handle<String> input = FACTORY->NewStringFromAscii(CStrVector("fooofo"));
+ Handle<String> input = factory->NewStringFromAscii(CStrVector("fooofo"));
Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
Address start_adr = seq_input->GetCharsAddress();
@@ -947,6 +954,7 @@
TEST(MacroAssemblerNativeBackReferenceUC16) {
v8::V8::Initialize();
ContextInitializer initializer;
+ Factory* factory = Isolate::Current()->factory();
ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::UC16, 4);
@@ -965,13 +973,13 @@
m.Bind(&missing_match);
m.Fail();
- Handle<String> source = FACTORY->NewStringFromAscii(CStrVector("^(..)..\1"));
+ Handle<String> source = factory->NewStringFromAscii(CStrVector("^(..)..\1"));
Handle<Object> code_object = m.GetCode(source);
Handle<Code> code = Handle<Code>::cast(code_object);
const uc16 input_data[6] = {'f', 0x2028, 'o', 'o', 'f', 0x2028};
Handle<String> input =
- FACTORY->NewStringFromTwoByte(Vector<const uc16>(input_data, 6));
+ factory->NewStringFromTwoByte(Vector<const uc16>(input_data, 6));
Handle<SeqTwoByteString> seq_input = Handle<SeqTwoByteString>::cast(input);
Address start_adr = seq_input->GetCharsAddress();
@@ -996,6 +1004,7 @@
TEST(MacroAssemblernativeAtStart) {
v8::V8::Initialize();
ContextInitializer initializer;
+ Factory* factory = Isolate::Current()->factory();
ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 0);
@@ -1020,11 +1029,11 @@
m.CheckNotCharacter('b', &fail);
m.Succeed();
- Handle<String> source = FACTORY->NewStringFromAscii(CStrVector("(^f|ob)"));
+ Handle<String> source = factory->NewStringFromAscii(CStrVector("(^f|ob)"));
Handle<Object> code_object = m.GetCode(source);
Handle<Code> code = Handle<Code>::cast(code_object);
- Handle<String> input = FACTORY->NewStringFromAscii(CStrVector("foobar"));
+ Handle<String> input = factory->NewStringFromAscii(CStrVector("foobar"));
Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
Address start_adr = seq_input->GetCharsAddress();
@@ -1052,6 +1061,7 @@
TEST(MacroAssemblerNativeBackRefNoCase) {
v8::V8::Initialize();
ContextInitializer initializer;
+ Factory* factory = Isolate::Current()->factory();
ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 4);
@@ -1078,12 +1088,12 @@
m.Succeed();
Handle<String> source =
- FACTORY->NewStringFromAscii(CStrVector("^(abc)\1\1(?!\1)...(?!\1)"));
+ factory->NewStringFromAscii(CStrVector("^(abc)\1\1(?!\1)...(?!\1)"));
Handle<Object> code_object = m.GetCode(source);
Handle<Code> code = Handle<Code>::cast(code_object);
Handle<String> input =
- FACTORY->NewStringFromAscii(CStrVector("aBcAbCABCxYzab"));
+ factory->NewStringFromAscii(CStrVector("aBcAbCABCxYzab"));
Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
Address start_adr = seq_input->GetCharsAddress();
@@ -1108,6 +1118,7 @@
TEST(MacroAssemblerNativeRegisters) {
v8::V8::Initialize();
ContextInitializer initializer;
+ Factory* factory = Isolate::Current()->factory();
ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 6);
@@ -1176,13 +1187,13 @@
m.Fail();
Handle<String> source =
- FACTORY->NewStringFromAscii(CStrVector("<loop test>"));
+ factory->NewStringFromAscii(CStrVector("<loop test>"));
Handle<Object> code_object = m.GetCode(source);
Handle<Code> code = Handle<Code>::cast(code_object);
// String long enough for test (content doesn't matter).
Handle<String> input =
- FACTORY->NewStringFromAscii(CStrVector("foofoofoofoofoo"));
+ factory->NewStringFromAscii(CStrVector("foofoofoofoofoo"));
Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
Address start_adr = seq_input->GetCharsAddress();
@@ -1208,6 +1219,8 @@
TEST(MacroAssemblerStackOverflow) {
v8::V8::Initialize();
ContextInitializer initializer;
+ Isolate* isolate = Isolate::Current();
+ Factory* factory = isolate->factory();
ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 0);
@@ -1217,13 +1230,13 @@
m.GoTo(&loop);
Handle<String> source =
- FACTORY->NewStringFromAscii(CStrVector("<stack overflow test>"));
+ factory->NewStringFromAscii(CStrVector("<stack overflow test>"));
Handle<Object> code_object = m.GetCode(source);
Handle<Code> code = Handle<Code>::cast(code_object);
// String long enough for test (content doesn't matter).
Handle<String> input =
- FACTORY->NewStringFromAscii(CStrVector("dummy"));
+ factory->NewStringFromAscii(CStrVector("dummy"));
Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
Address start_adr = seq_input->GetCharsAddress();
@@ -1236,14 +1249,16 @@
NULL);
CHECK_EQ(NativeRegExpMacroAssembler::EXCEPTION, result);
- CHECK(Isolate::Current()->has_pending_exception());
- Isolate::Current()->clear_pending_exception();
+ CHECK(isolate->has_pending_exception());
+ isolate->clear_pending_exception();
}
TEST(MacroAssemblerNativeLotsOfRegisters) {
v8::V8::Initialize();
ContextInitializer initializer;
+ Isolate* isolate = Isolate::Current();
+ Factory* factory = isolate->factory();
ArchRegExpMacroAssembler m(NativeRegExpMacroAssembler::ASCII, 2);
@@ -1261,13 +1276,13 @@
m.Succeed();
Handle<String> source =
- FACTORY->NewStringFromAscii(CStrVector("<huge register space test>"));
+ factory->NewStringFromAscii(CStrVector("<huge register space test>"));
Handle<Object> code_object = m.GetCode(source);
Handle<Code> code = Handle<Code>::cast(code_object);
// String long enough for test (content doesn't matter).
Handle<String> input =
- FACTORY->NewStringFromAscii(CStrVector("sample text"));
+ factory->NewStringFromAscii(CStrVector("sample text"));
Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
Address start_adr = seq_input->GetCharsAddress();
@@ -1284,7 +1299,7 @@
CHECK_EQ(0, captures[0]);
CHECK_EQ(42, captures[1]);
- Isolate::Current()->clear_pending_exception();
+ isolate->clear_pending_exception();
}
#else // V8_INTERPRETED_REGEXP
@@ -1327,17 +1342,19 @@
m.PopRegister(0);
m.Fail();
- v8::HandleScope scope;
+ Isolate* isolate = Isolate::Current();
+ Factory* factory = isolate->factory();
+ HandleScope scope(isolate);
- Handle<String> source = FACTORY->NewStringFromAscii(CStrVector("^f(o)o"));
+ Handle<String> source = factory->NewStringFromAscii(CStrVector("^f(o)o"));
Handle<ByteArray> array = Handle<ByteArray>::cast(m.GetCode(source));
int captures[5];
const uc16 str1[] = {'f', 'o', 'o', 'b', 'a', 'r'};
Handle<String> f1_16 =
- FACTORY->NewStringFromTwoByte(Vector<const uc16>(str1, 6));
+ factory->NewStringFromTwoByte(Vector<const uc16>(str1, 6));
- CHECK(IrregexpInterpreter::Match(array, f1_16, captures, 0));
+ CHECK(IrregexpInterpreter::Match(isolate, array, f1_16, captures, 0));
CHECK_EQ(0, captures[0]);
CHECK_EQ(3, captures[1]);
CHECK_EQ(1, captures[2]);
@@ -1346,9 +1363,9 @@
const uc16 str2[] = {'b', 'a', 'r', 'f', 'o', 'o'};
Handle<String> f2_16 =
- FACTORY->NewStringFromTwoByte(Vector<const uc16>(str2, 6));
+ factory->NewStringFromTwoByte(Vector<const uc16>(str2, 6));
- CHECK(!IrregexpInterpreter::Match(array, f2_16, captures, 0));
+ CHECK(!IrregexpInterpreter::Match(isolate, array, f2_16, captures, 0));
CHECK_EQ(42, captures[0]);
}
diff --git a/test/cctest/test-threads.cc b/test/cctest/test-threads.cc
index 37f0205..c6d5cb0 100644
--- a/test/cctest/test-threads.cc
+++ b/test/cctest/test-threads.cc
@@ -28,6 +28,7 @@
#include "v8.h"
#include "platform.h"
+#include "isolate.h"
#include "cctest.h"
@@ -136,3 +137,55 @@
CHECK_EQ(DONE, turn);
}
+
+class ThreadIdValidationThread : public v8::internal::Thread {
+ public:
+ ThreadIdValidationThread(i::Thread* thread_to_start,
+ i::List<i::ThreadId>* refs,
+ unsigned int thread_no,
+ i::Semaphore* semaphore)
+ : Thread(NULL, "ThreadRefValidationThread"),
+ refs_(refs), thread_no_(thread_no), thread_to_start_(thread_to_start),
+ semaphore_(semaphore) {
+ }
+
+ void Run() {
+ i::ThreadId thread_id = i::ThreadId::Current();
+ for (int i = 0; i < thread_no_; i++) {
+ CHECK(!(*refs_)[i].Equals(thread_id));
+ }
+ CHECK(thread_id.IsValid());
+ (*refs_)[thread_no_] = thread_id;
+ if (thread_to_start_ != NULL) {
+ thread_to_start_->Start();
+ }
+ semaphore_->Signal();
+ }
+ private:
+ i::List<i::ThreadId>* refs_;
+ int thread_no_;
+ i::Thread* thread_to_start_;
+ i::Semaphore* semaphore_;
+};
+
+TEST(ThreadIdValidation) {
+ const int kNThreads = 100;
+ i::List<ThreadIdValidationThread*> threads(kNThreads);
+ i::List<i::ThreadId> refs(kNThreads);
+ i::Semaphore* semaphore = i::OS::CreateSemaphore(0);
+ ThreadIdValidationThread* prev = NULL;
+ for (int i = kNThreads - 1; i >= 0; i--) {
+ ThreadIdValidationThread* newThread =
+ new ThreadIdValidationThread(prev, &refs, i, semaphore);
+ threads.Add(newThread);
+ prev = newThread;
+ refs.Add(i::ThreadId::Invalid());
+ }
+ prev->Start();
+ for (int i = 0; i < kNThreads; i++) {
+ semaphore->Wait();
+ }
+ for (int i = 0; i < kNThreads; i++) {
+ delete threads[i];
+ }
+}
diff --git a/test/cctest/test-utils.cc b/test/cctest/test-utils.cc
index 018018a..ce53f8e 100644
--- a/test/cctest/test-utils.cc
+++ b/test/cctest/test-utils.cc
@@ -89,8 +89,8 @@
memset(dst.start(), 0xFF, dst.length());
byte* to = dst.start() + 32 + destination_alignment;
byte* from = src.start() + source_alignment;
- int length = kMinComplexMemCopy + length_alignment;
- MemCopy(to, from, static_cast<size_t>(length));
+ int length = OS::kMinComplexMemCopy + length_alignment;
+ OS::MemCopy(to, from, static_cast<size_t>(length));
printf("[%d,%d,%d]\n",
source_alignment, destination_alignment, length_alignment);
for (int i = 0; i < length; i++) {
@@ -103,8 +103,9 @@
TEST(MemCopy) {
+ v8::V8::Initialize();
OS::Setup();
- const int N = kMinComplexMemCopy + 128;
+ const int N = OS::kMinComplexMemCopy + 128;
Vector<byte> buffer1 = Vector<byte>::New(N);
Vector<byte> buffer2 = Vector<byte>::New(N);
diff --git a/test/mjsunit/apply.js b/test/mjsunit/apply.js
index 613d37d..c166110 100644
--- a/test/mjsunit/apply.js
+++ b/test/mjsunit/apply.js
@@ -33,43 +33,43 @@
return a;
}
-assertTrue(this === f0.apply(), "1-0");
+assertSame(this, f0.apply(), "1-0");
-assertTrue(this === f0.apply(this), "2a");
-assertTrue(this === f0.apply(this, new Array(1)), "2b");
-assertTrue(this === f0.apply(this, new Array(2)), "2c");
-assertTrue(this === f0.apply(this, new Array(4242)), "2d");
+assertSame(this, f0.apply(this), "2a");
+assertSame(this, f0.apply(this, new Array(1)), "2b");
+assertSame(this, f0.apply(this, new Array(2)), "2c");
+assertSame(this, f0.apply(this, new Array(4242)), "2d");
-assertTrue(this === f0.apply(null), "3a");
-assertTrue(this === f0.apply(null, new Array(1)), "3b");
-assertTrue(this === f0.apply(null, new Array(2)), "3c");
-assertTrue(this === f0.apply(this, new Array(4242)), "3d");
+assertSame(this, f0.apply(null), "3a");
+assertSame(this, f0.apply(null, new Array(1)), "3b");
+assertSame(this, f0.apply(null, new Array(2)), "3c");
+assertSame(this, f0.apply(this, new Array(4242)), "3d");
-assertTrue(this === f0.apply(void 0), "4a");
-assertTrue(this === f0.apply(void 0, new Array(1)), "4b");
-assertTrue(this === f0.apply(void 0, new Array(2)), "4c");
+assertSame(this, f0.apply(void 0), "4a");
+assertSame(this, f0.apply(void 0, new Array(1)), "4b");
+assertSame(this, f0.apply(void 0, new Array(2)), "4c");
-assertTrue(void 0 === f1.apply(), "1-1");
+assertEquals(void 0, f1.apply(), "1-1");
-assertTrue(void 0 === f1.apply(this), "5a");
-assertTrue(void 0 === f1.apply(this, new Array(1)), "5b");
-assertTrue(void 0 === f1.apply(this, new Array(2)), "5c");
-assertTrue(void 0 === f1.apply(this, new Array(4242)), "5d");
-assertTrue(42 === f1.apply(this, new Array(42, 43)), "5e");
+assertEquals(void 0, f1.apply(this), "5a");
+assertEquals(void 0, f1.apply(this, new Array(1)), "5b");
+assertEquals(void 0, f1.apply(this, new Array(2)), "5c");
+assertEquals(void 0, f1.apply(this, new Array(4242)), "5d");
+assertEquals(42, f1.apply(this, new Array(42, 43)), "5e");
assertEquals("foo", f1.apply(this, new Array("foo", "bar", "baz", "bo")), "5f");
-assertTrue(void 0 === f1.apply(null), "6a");
-assertTrue(void 0 === f1.apply(null, new Array(1)), "6b");
-assertTrue(void 0 === f1.apply(null, new Array(2)), "6c");
-assertTrue(void 0 === f1.apply(null, new Array(4242)), "6d");
-assertTrue(42 === f1.apply(null, new Array(42, 43)), "6e");
+assertEquals(void 0, f1.apply(null), "6a");
+assertEquals(void 0, f1.apply(null, new Array(1)), "6b");
+assertEquals(void 0, f1.apply(null, new Array(2)), "6c");
+assertEquals(void 0, f1.apply(null, new Array(4242)), "6d");
+assertEquals(42, f1.apply(null, new Array(42, 43)), "6e");
assertEquals("foo", f1.apply(null, new Array("foo", "bar", "baz", "bo")), "6f");
-assertTrue(void 0 === f1.apply(void 0), "7a");
-assertTrue(void 0 === f1.apply(void 0, new Array(1)), "7b");
-assertTrue(void 0 === f1.apply(void 0, new Array(2)), "7c");
-assertTrue(void 0 === f1.apply(void 0, new Array(4242)), "7d");
-assertTrue(42 === f1.apply(void 0, new Array(42, 43)), "7e");
+assertEquals(void 0, f1.apply(void 0), "7a");
+assertEquals(void 0, f1.apply(void 0, new Array(1)), "7b");
+assertEquals(void 0, f1.apply(void 0, new Array(2)), "7c");
+assertEquals(void 0, f1.apply(void 0, new Array(4242)), "7d");
+assertEquals(42, f1.apply(void 0, new Array(42, 43)), "7e");
assertEquals("foo", f1.apply(void 0, new Array("foo", "bar", "ba", "b")), "7f");
var arr = new Array(42, "foo", "fish", "horse");
@@ -108,7 +108,7 @@
assertEquals("bar42foofishhorse", s.apply("bar", arr), "apply to string");
function al() {
- assertEquals(345, this);
+ assertEquals(Object(345), this);
return arguments.length + arguments[arguments.length - 1];
}
@@ -186,7 +186,7 @@
primes[1] = holey;
assertThrows("String.prototype.concat.apply.apply('foo', primes)");
assertEquals("morseper",
- String.prototype.concat.apply.apply(String.prototype.concat, primes),
+ String.prototype.concat.apply.apply(String.prototype.concat, primes),
"moreseper-prime");
delete(Array.prototype["1"]);
diff --git a/test/mjsunit/arguments-apply.js b/test/mjsunit/arguments-apply.js
index 5a91228..48c4234 100644
--- a/test/mjsunit/arguments-apply.js
+++ b/test/mjsunit/arguments-apply.js
@@ -73,11 +73,11 @@
return ReturnReceiver.apply(receiver, arguments);
}
-assertEquals(42, NonObjectReceiver(42));
+assertEquals(Object(42), NonObjectReceiver(42));
assertEquals("object", typeof NonObjectReceiver(42));
-assertTrue(NonObjectReceiver(42) instanceof Number);
-assertTrue(this === NonObjectReceiver(null));
-assertTrue(this === NonObjectReceiver(void 0));
+assertInstanceof(NonObjectReceiver(42), Number);
+assertSame(this, NonObjectReceiver(null));
+assertSame(this, NonObjectReceiver(void 0));
function FunctionReceiver() {
diff --git a/test/mjsunit/arguments-opt.js b/test/mjsunit/arguments-opt.js
index c74fc75..b8280b4 100644
--- a/test/mjsunit/arguments-opt.js
+++ b/test/mjsunit/arguments-opt.js
@@ -79,36 +79,38 @@
assertTrue(typeof(A(10000, 0)) == 'undefined');
// String access.
-assertEquals(0, A('0'));
-assertEquals(0, A('0',1));
+assertEquals('0', A('0'));
+assertEquals('0', A('0',1));
assertEquals(2, A('1',2));
assertEquals(2, A('1',2,3,4,5));
assertEquals(5, A('4',2,3,4,5));
-assertTrue(typeof A('1') == 'undefined');
-assertTrue(typeof A('3',2,1) == 'undefined');
+assertEquals('undefined', typeof A('1'));
+assertEquals('undefined', typeof A('3',2,1));
assertEquals(A, A('callee'));
assertEquals(1, A('length'));
assertEquals(2, A('length',2));
assertEquals(5, A('length',2,3,4,5));
assertEquals({}.toString, A('toString'));
assertEquals({}.isPrototypeOf, A('isPrototypeOf'));
-assertTrue(typeof A('xxx') == 'undefined');
+assertEquals('undefined', typeof A('xxx'));
// Object access.
function O(key) {
return { toString: function() { return key; } };
}
-assertEquals(0, A(O(0)));
-assertEquals(0, A(O(0),1));
+var O0 = O(0);
+assertSame(O0, A(O0));
+assertSame(O0, A(O0,1));
assertEquals(2, A(O(1),2));
assertEquals(2, A(O(1),2,3,4,5));
assertEquals(5, A(O(4),2,3,4,5));
assertTrue(typeof A(O(1)) == 'undefined');
assertTrue(typeof A(O(3),2,1) == 'undefined');
-assertEquals(0, A(O('0')));
-assertEquals(0, A(O('0'),1));
+O0 = O('0');
+assertSame(O0, A(O0));
+assertSame(O0, A(O0,1));
assertEquals(2, A(O('1'),2));
assertEquals(2, A(O('1'),2,3,4,5));
assertEquals(5, A(O('4'),2,3,4,5));
diff --git a/test/mjsunit/array-length.js b/test/mjsunit/array-length.js
index 967d720..16867db 100644
--- a/test/mjsunit/array-length.js
+++ b/test/mjsunit/array-length.js
@@ -102,7 +102,7 @@
var a = new Array();
-assertEquals(12, a.length = new Number(12));
+assertEquals(Object(12), a.length = new Number(12));
assertEquals(12, a.length);
diff --git a/test/mjsunit/compiler/array-length.js b/test/mjsunit/compiler/array-length.js
index 126c7a0..462a1e7 100644
--- a/test/mjsunit/compiler/array-length.js
+++ b/test/mjsunit/compiler/array-length.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
function ArrayLength(a) { return a.length; }
function Test(a0, a2, a5) {
@@ -36,7 +38,12 @@
var a0 = [];
var a2 = [1,2];
var a5 = [1,2,3,4,5];
-for (var i = 0; i < 100000; i++) Test(a0, a2, a5);
+for (var i = 0; i < 5; i++) Test(a0, a2, a5);
+%OptimizeFunctionOnNextCall(ArrayLength);
+%OptimizeFunctionOnNextCall(Test);
+Test(a0, a2, a5);
assertEquals("undefined", typeof(ArrayLength(0)));
-for (var i = 0; i < 100000; i++) Test(a0, a2, a5);
+for (var i = 0; i < 5; i++) Test(a0, a2, a5);
+%OptimizeFunctionOnNextCall(Test);
+Test(a0, a2, a5);
assertEquals(4, ArrayLength("hest"));
diff --git a/test/mjsunit/compiler/assignment-deopt.js b/test/mjsunit/compiler/assignment-deopt.js
index 74f185b..2b00625 100644
--- a/test/mjsunit/compiler/assignment-deopt.js
+++ b/test/mjsunit/compiler/assignment-deopt.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Test deopt with count operation on parameter.
var max_smi = 1073741823;
var o = {x:0};
@@ -44,12 +46,15 @@
assign2(o);
assertEquals("421", o.x);
-var s = max_smi - 10000;
+var s = max_smi - 10;
o.x = s;
-for(var i = 0; i < 20000; i++) {
+for(var i = 0; i < 20; i++) {
assign2(o);
+ if (i == 4) {
+ %OptimizeFunctionOnNextCall(assign2);
+ }
}
-assertEquals(max_smi + 10000, o.x);
+assertEquals(max_smi + 10, o.x);
// Test deopt with count operation on keyed property.
@@ -59,36 +64,48 @@
assign3(o, 0);
assertEquals("421", o[0]);
-var s = max_smi - 10000;
+var s = max_smi - 10;
o[0] = s;
-for(var i = 0; i < 20000; i++) {
+for(var i = 0; i < 20; i++) {
assign3(o, 0);
+ if (i == 4) {
+ %OptimizeFunctionOnNextCall(assign3);
+ }
}
-assertEquals(max_smi + 10000, o[0]);
+assertEquals(max_smi + 10, o[0]);
-assign3(o,"0");
+assign3(o, "0");
-assertEquals(max_smi + 10001, o[0]);
+assertEquals(max_smi + 11, o[0]);
// Test bailout when accessing a non-existing array element.
o[0] = 0;
-for(var i = 0; i < 10000; i++) {
+for(var i = 0; i < 5; i++) {
assign3(o, 0);
}
-assign3(o,1);
+%OptimizeFunctionOnNextCall(assign3);
+assign3(o, 0);
+assign3(o, 1);
// Test bailout with count operation in a value context.
function assign5(x,y) { return (x += 1) + y; }
-for (var i = 0; i < 10000; ++i) assertEquals(4, assign5(2, 1));
+for (var i = 0; i < 5; ++i) assertEquals(4, assign5(2, 1));
+%OptimizeFunctionOnNextCall(assign5);
+assertEquals(4, assign5(2, 1));
+
assertEquals(4.1, assign5(2, 1.1));
assertEquals(4.1, assign5(2.1, 1));
function assign7(o,y) { return (o.x += 1) + y; }
o = {x:0};
-for (var i = 0; i < 10000; ++i) {
+for (var i = 0; i < 5; ++i) {
o.x = 42;
assertEquals(44, assign7(o, 1));
}
+%OptimizeFunctionOnNextCall(assign7);
+o.x = 42;
+assertEquals(44, assign7(o, 1));
+
o.x = 42;
assertEquals(44.1, assign7(o, 1.1));
o.x = 42.1;
@@ -96,10 +113,14 @@
function assign9(o,y) { return (o[0] += 1) + y; }
q = [0];
-for (var i = 0; i < 10000; ++i) {
+for (var i = 0; i < 5; ++i) {
q[0] = 42;
assertEquals(44, assign9(q, 1));
}
+%OptimizeFunctionOnNextCall(assign9);
+q[0] = 42;
+assertEquals(44, assign9(q, 1));
+
q[0] = 42;
assertEquals(44.1, assign9(q, 1.1));
q[0] = 42.1;
@@ -109,11 +130,16 @@
function assign10(p) { return p.x += 1 }
var g1 = {x:0};
var g2 = {y:0, x:42};
-for (var i = 0; i < 10000; ++i) {
+for (var i = 0; i < 5; ++i) {
g1.x = 42;
assertEquals(43, assign10(g1));
assertEquals(43, g1.x);
}
+%OptimizeFunctionOnNextCall(assign10);
+g1.x = 42;
+assertEquals(43, assign10(g1));
+assertEquals(43, g1.x);
+
assertEquals(43, assign10(g2));
assertEquals(43, g2.x);
@@ -123,10 +149,14 @@
var g3 = { valueOf: function() { o.y = "bar"; return 42; }};
function assign11(p) { return p.x += 1; }
-for (var i = 0; i < 10000; i++) {
+for (var i = 0; i < 5; i++) {
o.x = "a";
assign11(o);
}
+%OptimizeFunctionOnNextCall(assign11);
+o.x = "a";
+assign11(o);
+
assertEquals("a11", assign11(o));
o.x = g3;
assertEquals(43, assign11(o));
@@ -136,10 +166,14 @@
var g4 = { valueOf: function() { o.y = "bar"; return 42; }};
function assign12(p) { return p[0] += 1; }
-for (var i = 0; i < 1000000; i++) {
+for (var i = 0; i < 5; i++) {
o[0] = "a";
assign12(o);
}
+%OptimizeFunctionOnNextCall(assign12);
+o[0] = "a";
+assign12(o);
+
assertEquals("a11", assign12(o));
o[0] = g4;
assertEquals(43, assign12(o));
diff --git a/test/mjsunit/compiler/count-deopt.js b/test/mjsunit/compiler/count-deopt.js
index dcd82f8..415dadc 100644
--- a/test/mjsunit/compiler/count-deopt.js
+++ b/test/mjsunit/compiler/count-deopt.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Test deopt with count operation on parameter.
var max_smi = 1073741823;
var o = {x:0};
@@ -44,12 +46,15 @@
inc2(o);
assertEquals(43, o.x);
-var s = max_smi - 10000;
+var s = max_smi - 10;
o.x = s;
-for(var i = 0; i < 20000; i++) {
+for(var i = 0; i < 20; i++) {
inc2(o);
+ if (i == 4) {
+ %OptimizeFunctionOnNextCall(inc2);
+ }
}
-assertEquals(max_smi + 10000, o.x);
+assertEquals(max_smi + 10, o.x);
// Test deopt with count operation on keyed property.
@@ -59,40 +64,52 @@
inc3(o, 0);
assertEquals(43, o[0]);
-var s = max_smi - 10000;
+var s = max_smi - 10;
o[0] = s;
-for(var i = 0; i < 20000; i++) {
+for(var i = 0; i < 20; i++) {
inc3(o, 0);
+ if (i == 4) {
+ %OptimizeFunctionOnNextCall(inc3);
+ }
}
-assertEquals(max_smi + 10000, o[0]);
+assertEquals(max_smi + 10, o[0]);
inc3(o,"0");
-assertEquals(max_smi + 10001, o[0]);
+assertEquals(max_smi + 11, o[0]);
// Test bailout when accessing a non-existing array element.
o[0] = 0;
-for(var i = 0; i < 10000; i++) {
+for(var i = 0; i < 5; i++) {
inc3(o, 0);
}
-inc3(o,1);
+%OptimizeFunctionOnNextCall(inc3);
+inc3(o, 0);
+inc3(o, 1);
// Test bailout with count operation in a value context.
function inc4(x,y) { return (x++) + y; }
-for (var i = 0; i < 100000; ++i) assertEquals(3, inc4(2, 1));
+for (var i = 0; i < 5; ++i) assertEquals(3, inc4(2, 1));
+%OptimizeFunctionOnNextCall(inc4);
+inc4(2, 1);
assertEquals(3.1, inc4(2, 1.1));
function inc5(x,y) { return (++x) + y; }
-for (var i = 0; i < 100000; ++i) assertEquals(4, inc5(2, 1));
+for (var i = 0; i < 5; ++i) assertEquals(4, inc5(2, 1));
+%OptimizeFunctionOnNextCall(inc5);
+assertEquals(4, inc5(2, 1));
assertEquals(4.1, inc5(2, 1.1));
assertEquals(4.1, inc5(2.1, 1));
function inc6(o,y) { return (o.x++) + y; }
o = {x:0};
-for (var i = 0; i < 10000; ++i) {
+for (var i = 0; i < 5; ++i) {
o.x = 42;
assertEquals(43, inc6(o, 1));
}
+%OptimizeFunctionOnNextCall(inc6);
+o.x = 42;
+assertEquals(43, inc6(o, 1));
o.x = 42;
assertEquals(43.1, inc6(o, 1.1));
o.x = 42.1;
@@ -100,10 +117,13 @@
function inc7(o,y) { return (++o.x) + y; }
o = {x:0};
-for (var i = 0; i < 10000; ++i) {
+for (var i = 0; i < 5; ++i) {
o.x = 42;
assertEquals(44, inc7(o, 1));
}
+%OptimizeFunctionOnNextCall(inc7);
+o.x = 42;
+assertEquals(44, inc7(o, 1));
o.x = 42;
assertEquals(44.1, inc7(o, 1.1));
o.x = 42.1;
@@ -111,10 +131,13 @@
function inc8(o,y) { return (o[0]++) + y; }
var q = [0];
-for (var i = 0; i < 100000; ++i) {
+for (var i = 0; i < 5; ++i) {
q[0] = 42;
assertEquals(43, inc8(q, 1));
}
+%OptimizeFunctionOnNextCall(inc8);
+q[0] = 42;
+assertEquals(43, inc8(q, 1));
q[0] = 42;
assertEquals(43.1, inc8(q, 1.1));
q[0] = 42.1;
@@ -122,10 +145,13 @@
function inc9(o,y) { return (++o[0]) + y; }
q = [0];
-for (var i = 0; i < 100000; ++i) {
+for (var i = 0; i < 5; ++i) {
q[0] = 42;
assertEquals(44, inc9(q, 1));
}
+%OptimizeFunctionOnNextCall(inc9);
+q[0] = 42;
+assertEquals(44, inc9(q, 1));
q[0] = 42;
assertEquals(44.1, inc9(q, 1.1));
q[0] = 42.1;
@@ -135,11 +161,15 @@
function inc10(p) { return p.x++ }
var g1 = {x:0};
var g2 = {y:0, x:42}
-for (var i = 0; i < 10000; ++i) {
+for (var i = 0; i < 5; ++i) {
g1.x = 42;
assertEquals(42, inc10(g1));
assertEquals(43, g1.x);
}
+%OptimizeFunctionOnNextCall(inc10);
+g1.x = 42;
+assertEquals(42, inc10(g1));
+assertEquals(43, g1.x);
assertEquals(42, inc10(g2));
assertEquals(43, g2.x);
diff --git a/test/mjsunit/compiler/deopt-args.js b/test/mjsunit/compiler/deopt-args.js
index 780e2a2..17c397c 100644
--- a/test/mjsunit/compiler/deopt-args.js
+++ b/test/mjsunit/compiler/deopt-args.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
function g(x) {
return x.f(0,1,2);
}
@@ -35,9 +37,11 @@
var object = { };
object.f = f;
-for (var i = 0; i < 10000000; i++) {
+for (var i = 0; i < 5; i++) {
assertEquals(42, g(object));
}
+%OptimizeFunctionOnNextCall(g);
+g(object);
object.f = function(a,b,c) { return 87; };
assertEquals(87, g(object));
diff --git a/src/x64/codegen-x64-inl.h b/test/mjsunit/compiler/global-accessors.js
similarity index 79%
rename from src/x64/codegen-x64-inl.h
rename to test/mjsunit/compiler/global-accessors.js
index 53caf91..bd031a8 100644
--- a/src/x64/codegen-x64-inl.h
+++ b/test/mjsunit/compiler/global-accessors.js
@@ -25,22 +25,23 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// This test tests that no bailouts are missing by not hitting asserts in debug
+// mode.
-#ifndef V8_X64_CODEGEN_X64_INL_H_
-#define V8_X64_CODEGEN_X64_INL_H_
+test_count_operation()
+test_compound_assignment()
-namespace v8 {
-namespace internal {
+function f() {}
+function test_count_operation()
+{
+ this.__defineSetter__('x', f);
+ this.__defineGetter__('x', f);
+ x = x++;
+}
-#define __ ACCESS_MASM(masm_)
-
-// Platform-specific inline functions.
-
-void DeferredCode::Jump() { __ jmp(&entry_label_); }
-void DeferredCode::Branch(Condition cc) { __ j(cc, &entry_label_); }
-
-#undef __
-
-} } // namespace v8::internal
-
-#endif // V8_X64_CODEGEN_X64_INL_H_
+function test_compound_assignment()
+{
+ this.__defineSetter__('y', f);
+ this.__defineGetter__('y', f);
+ y += y;
+}
diff --git a/test/mjsunit/compiler/inline-compare.js b/test/mjsunit/compiler/inline-compare.js
index 6efe154..d97dce2 100644
--- a/test/mjsunit/compiler/inline-compare.js
+++ b/test/mjsunit/compiler/inline-compare.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Test that we can inline a function that returns the result of
// a compare operation.
function TestInlineCompare(o) {
@@ -42,5 +44,7 @@
var o = {};
o.f = function() { return 0 === 1; };
-for (var i = 0; i < 10000000; i++) TestInlineCompare(o);
+for (var i = 0; i < 5; i++) TestInlineCompare(o);
+%OptimizeFunctionOnNextCall(TestInlineCompare);
+TestInlineCompare(o);
TestInlineCompare({f: o.f});
diff --git a/test/mjsunit/compiler/inline-global-access.js b/test/mjsunit/compiler/inline-global-access.js
index 3795173..b52652a 100644
--- a/test/mjsunit/compiler/inline-global-access.js
+++ b/test/mjsunit/compiler/inline-global-access.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Test that we can inline a function that returns the result of a
// global variable load.
var GLOBAL;
@@ -45,5 +47,7 @@
var o = {};
o.f = function() { return GLOBAL; };
-for (var i = 0; i < 10000000; i++) TestInlineGlobalLoad(o);
+for (var i = 0; i < 5; i++) TestInlineGlobalLoad(o);
+%OptimizeFunctionOnNextCall(TestInlineGlobalLoad);
+TestInlineGlobalLoad(o);
TestInlineGlobalLoad({f: o.f});
diff --git a/test/mjsunit/compiler/inline-param.js b/test/mjsunit/compiler/inline-param.js
index 8e0933a..8fa8008 100644
--- a/test/mjsunit/compiler/inline-param.js
+++ b/test/mjsunit/compiler/inline-param.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Test that we can inline a call with a parameter.
function TestInlineOneParam(o, p) {
// Effect context.
@@ -42,7 +44,9 @@
var obj = {x:42};
var o1 = {};
o1.f = function(o) { return o.x; };
-for (var i = 0; i < 10000; i++) TestInlineOneParam(o1, obj);
+for (var i = 0; i < 5; i++) TestInlineOneParam(o1, obj);
+%OptimizeFunctionOnNextCall(TestInlineOneParam);
+TestInlineOneParam(o1, obj);
TestInlineOneParam({f: o1.f}, {x:42});
@@ -76,5 +80,7 @@
var o2 = {};
o2.h = function(i, j) { return i < j; };
-for (var i = 0; i < 10000; i++) TestInlineTwoParams(o2, 42);
+for (var i = 0; i < 5; i++) TestInlineTwoParams(o2, 42);
+%OptimizeFunctionOnNextCall(TestInlineTwoParams);
+TestInlineTwoParams(o2, 42);
TestInlineTwoParams({h: o2.h}, 42);
diff --git a/src/x64/codegen-x64-inl.h b/test/mjsunit/compiler/inline-throw.js
similarity index 71%
copy from src/x64/codegen-x64-inl.h
copy to test/mjsunit/compiler/inline-throw.js
index 53caf91..e3aab39 100644
--- a/src/x64/codegen-x64-inl.h
+++ b/test/mjsunit/compiler/inline-throw.js
@@ -25,22 +25,45 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
-#ifndef V8_X64_CODEGEN_X64_INL_H_
-#define V8_X64_CODEGEN_X64_INL_H_
+// Test inlined functions contain throw.
+function doThrow() {
+ throw "uha";
+}
-namespace v8 {
-namespace internal {
+function f(x) {
+ if (x == 42) throw doThrow();
+ if (x == 43) throw "wow";
+ return x == 0;
+}
-#define __ ACCESS_MASM(masm_)
+function g(x) {
+ return f(x);
+}
-// Platform-specific inline functions.
+for (var i = 0; i < 5; i++) g(0);
+%OptimizeFunctionOnNextCall(g);
+assertEquals(true, g(0));
-void DeferredCode::Jump() { __ jmp(&entry_label_); }
-void DeferredCode::Branch(Condition cc) { __ j(cc, &entry_label_); }
+try {
+ g(42);
+} catch(e) {
+ assertEquals("uha", e);
+}
-#undef __
+// Test inlining in a test context.
+function h(x) {
+ return f(x) ? "yes" : "no";
+}
-} } // namespace v8::internal
+for (var i = 0; i < 5; i++) h(0);
+%OptimizeFunctionOnNextCall(h);
+assertEquals("yes", h(0));
-#endif // V8_X64_CODEGEN_X64_INL_H_
+try {
+ h(43);
+} catch(e) {
+ assertEquals("wow", e);
+}
+
diff --git a/test/mjsunit/compiler/inline-two.js b/test/mjsunit/compiler/inline-two.js
index 30f579d..68372a9 100644
--- a/test/mjsunit/compiler/inline-two.js
+++ b/test/mjsunit/compiler/inline-two.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Test that we can inline a function that calls another function.
function TestInlineX(o) {
// Effect context.
@@ -42,7 +44,9 @@
var o2 = {};
o2.size = function() { return 42; }
o2.g = function() { return this.size(); };
-for (var i = 0; i < 10000; i++) TestInlineX(o2);
+for (var i = 0; i < 5; i++) TestInlineX(o2);
+%OptimizeFunctionOnNextCall(TestInlineX);
+TestInlineX(o2);
TestInlineX({g: o2.g, size:o2.size});
@@ -65,7 +69,9 @@
var o3 = {};
o3.v = obj;
o3.h = function() { return this.v.foo(); };
-for (var i = 0; i < 10000; i++) TestInlineX2(o3);
+for (var i = 0; i < 5; i++) TestInlineX2(o3);
+%OptimizeFunctionOnNextCall(TestInlineX2);
+TestInlineX2(o3);
TestInlineX2({h: o3.h, v:obj});
@@ -89,5 +95,7 @@
o3.v = obj;
o3.f = function() { return this.v; }
o3.h = function() { return this.f().g(); };
-for (var i = 0; i < 10000; i++) TestInlineFG(o3);
+for (var i = 0; i < 5; i++) TestInlineFG(o3);
+%OptimizeFunctionOnNextCall(TestInlineFG);
+TestInlineFG(o3);
TestInlineFG({h: o3.h, f: o3.f, v:obj});
diff --git a/test/mjsunit/compiler/logical-and.js b/test/mjsunit/compiler/logical-and.js
index 1d31a0a..783edb6 100644
--- a/test/mjsunit/compiler/logical-and.js
+++ b/test/mjsunit/compiler/logical-and.js
@@ -46,8 +46,8 @@
assertFalse(AndBB(0, 1));
assertFalse(AndBB(1, 1));
-assertFalse(AndBN(0, 0));
-assertTrue(AndBN(0, 1));
+assertEquals(0, AndBN(0, 0));
+assertEquals(1, AndBN(0, 1));
assertFalse(AndBN(1, 0));
assertEquals(1, AndBN(0, 1));
assertEquals(2, AndBN(0, 2));
diff --git a/test/mjsunit/compiler/optimized-function-calls.js b/test/mjsunit/compiler/optimized-function-calls.js
index 1b5f3b0..c3e69d7 100644
--- a/test/mjsunit/compiler/optimized-function-calls.js
+++ b/test/mjsunit/compiler/optimized-function-calls.js
@@ -25,7 +25,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-// Flags: --expose-gc
+// Flags: --allow-natives-syntax --expose-gc
function f() {
gc();
@@ -48,7 +48,9 @@
function call_f(o) {
return o.f();
}
-for (var i = 0; i < 10000000; i++) call_f(object);
+for (var i = 0; i < 5; i++) call_f(object);
+%OptimizeFunctionOnNextCall(call_f);
+call_f(object);
// Check that nested global function calls work.
diff --git a/test/mjsunit/compiler/pic.js b/test/mjsunit/compiler/pic.js
index 06f2b3e..f5b136c 100644
--- a/test/mjsunit/compiler/pic.js
+++ b/test/mjsunit/compiler/pic.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
function GetX(o) { return o.x; }
function CallF(o) { return o.f(); }
function SetX(o) { o.x = 42; }
@@ -53,11 +55,15 @@
// Run the test until we're fairly sure we've optimized the
// polymorphic property access.
-for (var i = 0; i < 100000; i++) {
+for (var i = 0; i < 5; i++) {
Test(o1);
Test(o2);
Test(o3);
}
+%OptimizeFunctionOnNextCall(Test);
+Test(o1);
+Test(o2);
+Test(o3);
// Make sure that the following doesn't crash.
GetX(0);
diff --git a/test/mjsunit/compiler/property-calls.js b/test/mjsunit/compiler/property-calls.js
index 3366971..ad5ca81 100644
--- a/test/mjsunit/compiler/property-calls.js
+++ b/test/mjsunit/compiler/property-calls.js
@@ -25,12 +25,16 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
function f(o) { return o.g(); }
function g() { return 42; }
var object = { };
object.g = g;
-for (var i = 0; i < 10000000; i++) f(object);
+for (var i = 0; i < 5; i++) f(object);
+%OptimizeFunctionOnNextCall(f);
+f(object);
assertEquals(42, f(object));
object = { g: function() { return 87; } };
diff --git a/test/mjsunit/compiler/property-refs.js b/test/mjsunit/compiler/property-refs.js
index 3f6f793..6f1f19f 100644
--- a/test/mjsunit/compiler/property-refs.js
+++ b/test/mjsunit/compiler/property-refs.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
function Load(o) {
return o.outer.x | o.outer.inner.y;
}
@@ -45,7 +47,9 @@
return Load(object);
}
-for (var i = 0; i < 10000; i++) LoadXY(i, i);
+for (var i = 0; i < 5; i++) LoadXY(i, i);
+%OptimizeFunctionOnNextCall(LoadXY);
+LoadXY(6, 6);
assertEquals(42 | 87, LoadXY(42, 87));
assertEquals(42 | 87, LoadXY(42, 87));
assertEquals(42 | 99, LoadXY(42, "99"));
diff --git a/test/mjsunit/compiler/property-stores.js b/test/mjsunit/compiler/property-stores.js
index 0dec82a..4ffac07 100644
--- a/test/mjsunit/compiler/property-stores.js
+++ b/test/mjsunit/compiler/property-stores.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
var a = 42;
var obj = {x: 0,
@@ -33,11 +35,17 @@
h: function() { this.x = a; }};
var i;
-for (i = 0; i < 10000; i++) { obj.f(); }
+for (i = 0; i < 5; i++) { obj.f(); }
+%OptimizeFunctionOnNextCall(obj.f);
+obj.f();
assertEquals(7, obj.x);
-for (i = 0; i < 10000; i++) { obj.g(); }
+for (i = 0; i < 5; i++) { obj.g(); }
+%OptimizeFunctionOnNextCall(obj.g);
+obj.g();
assertEquals(43, obj.x);
-for (i = 0; i < 10000; i++) { obj.h(); }
+for (i = 0; i < 5; i++) { obj.h(); }
+%OptimizeFunctionOnNextCall(obj.h);
+obj.h();
assertEquals(42, obj.x);
diff --git a/test/mjsunit/compiler/recursive-deopt.js b/test/mjsunit/compiler/recursive-deopt.js
index 366f59a..c921ade 100644
--- a/test/mjsunit/compiler/recursive-deopt.js
+++ b/test/mjsunit/compiler/recursive-deopt.js
@@ -25,6 +25,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
function f(n) {
// Force deopt in both leaf case and when returning. To make
@@ -34,15 +35,11 @@
return f(n - 1) << one;
}
-function RunTests() {
- assertEquals(1 << 1, f(0));
- assertEquals(1 << 2, f(1));
- assertEquals(1 << 5, f(4));
-}
-
var one = 1;
-for (var i = 0; i < 1000000; i++) RunTests();
+for (var i = 0; i < 5; i++) assertEquals(1 << 5, f(4));
+%OptimizeFunctionOnNextCall(f);
+assertEquals(1 << 5, f(4));
var one = { valueOf: function() { return 1; } };
-for (var j = 0; j < 100000; j++) RunTests();
+assertEquals(1 << 5, f(4));
diff --git a/test/mjsunit/compiler/regress-3218915.js b/test/mjsunit/compiler/regress-3218915.js
index d27c319..dfce815 100644
--- a/test/mjsunit/compiler/regress-3218915.js
+++ b/test/mjsunit/compiler/regress-3218915.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Regression test for failure to deoptimize properly when the most recent
// side effect occurred in a comma expression in an effect context.
@@ -40,7 +42,9 @@
function test(x) { return observe(this, ((0, side_effect()), x + 1)); }
// Run test enough times to get it optimized.
-for (var i = 0; i < 1000000; ++i) test(0);
+for (var i = 0; i < 5; ++i) test(0);
+%OptimizeFunctionOnNextCall(test);
+test(0);
// Force test to deopt. If it behaves normally, it should return the global
// object. If the value of the call to side_effect() is lingering after the
diff --git a/test/mjsunit/compiler/regress-loadfield.js b/test/mjsunit/compiler/regress-loadfield.js
index a202891..a3da156 100644
--- a/test/mjsunit/compiler/regress-loadfield.js
+++ b/test/mjsunit/compiler/regress-loadfield.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Regression test for GVN on field loads.
function bar() {}
@@ -58,8 +60,10 @@
a.p10 = "";
a.p11 = "";
a.foo = "foo";
-for (var i = 0; i < 100000; i++) {
+for (var i = 0; i < 5; i++) {
test(a);
}
+%OptimizeFunctionOnNextCall(test);
+test(a);
test("");
diff --git a/test/mjsunit/compiler/regress-max.js b/test/mjsunit/compiler/regress-max.js
index 94c543a..ee2fd58 100644
--- a/test/mjsunit/compiler/regress-max.js
+++ b/test/mjsunit/compiler/regress-max.js
@@ -25,10 +25,12 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Test Math.max with negative zero as input.
-function f(x, y) { return Math.max(x, y) }
+for (var i = 0; i < 5; i++) Math.max(0, 0);
+%OptimizeFunctionOnNextCall(Math.max);
+Math.max(0, 0);
-for (var i = 0; i < 1000000; i++) f(0, 0);
-
-var r = f(-0, -0);
+var r = Math.max(-0, -0);
assertEquals(-Infinity, 1 / r);
diff --git a/test/mjsunit/compiler/regress-stacktrace-methods.js b/test/mjsunit/compiler/regress-stacktrace-methods.js
index 4900ccf..4d28727 100644
--- a/test/mjsunit/compiler/regress-stacktrace-methods.js
+++ b/test/mjsunit/compiler/regress-stacktrace-methods.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Test stack traces with method calls.
function Hest() {}
function Svin() {}
@@ -39,9 +41,12 @@
var s = new Svin();
var v = 0;
-for (var i = 0; i < 1000000; i++) {
+for (var i = 0; i < 5; i++) {
o.one(s);
}
+%OptimizeFunctionOnNextCall(Hest.prototype.one);
+%OptimizeFunctionOnNextCall(Hest.prototype.three);
+o.one(s);
v = 42;
@@ -57,8 +62,8 @@
assertTrue(p1 != -1);
assertTrue(p3 < p2);
assertTrue(p2 < p1);
- assertTrue(stack.indexOf("36:56") != -1);
- assertTrue(stack.indexOf("32:51") != -1);
- assertTrue(stack.indexOf("34:38") != -1);
- assertTrue(stack.indexOf("49:5") != -1);
+ assertTrue(stack.indexOf("38:56") != -1);
+ assertTrue(stack.indexOf("34:51") != -1);
+ assertTrue(stack.indexOf("36:38") != -1);
+ assertTrue(stack.indexOf("54:5") != -1);
}
diff --git a/test/mjsunit/compiler/simple-deopt.js b/test/mjsunit/compiler/simple-deopt.js
index 8befd9f..7f985ac 100644
--- a/test/mjsunit/compiler/simple-deopt.js
+++ b/test/mjsunit/compiler/simple-deopt.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
function f(x) {
return ~x;
}
@@ -59,7 +61,9 @@
function k(o) {
return o.g();
}
-for (var i = 0; i < 1000000; i++) k(obj);
+for (var i = 0; i < 5; i++) k(obj);
+%OptimizeFunctionOnNextCall(k);
+k(obj);
assertEquals(42, k(obj));
assertEquals(87, k({g: function() { return 87; }}));
@@ -88,9 +92,11 @@
var str = "abc";
var r;
function CallCharAt(n) { return str.charAt(n); }
-for (var i = 0; i < 1000000; i++) {
+for (var i = 0; i < 5; i++) {
r = CallCharAt(0);
}
+%OptimizeFunctionOnNextCall(CallCharAt);
+r = CallCharAt(0);
assertEquals("a", r);
diff --git a/test/mjsunit/compiler/simple-inlining.js b/test/mjsunit/compiler/simple-inlining.js
index 219580f..8bd37ea 100644
--- a/test/mjsunit/compiler/simple-inlining.js
+++ b/test/mjsunit/compiler/simple-inlining.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Test that we can inline a function that returns a constant.
function TestInlineConstant(o) {
// Effect context.
@@ -41,7 +43,9 @@
var o1 = {};
o1.f = function() { return 42; };
-for (var i = 0; i < 10000; i++) TestInlineConstant(o1);
+for (var i = 0; i < 5; i++) TestInlineConstant(o1);
+%OptimizeFunctionOnNextCall(TestInlineConstant);
+TestInlineConstant(o1);
TestInlineConstant({f: o1.f});
@@ -61,7 +65,9 @@
var o2 = {};
o2.g = function() { return this; };
-for (var i = 0; i < 10000; i++) TestInlineThis(o2);
+for (var i = 0; i < 5; i++) TestInlineThis(o2);
+%OptimizeFunctionOnNextCall(TestInlineThis);
+TestInlineThis(o2);
TestInlineThis({g: o2.g});
@@ -81,7 +87,9 @@
var o3 = {y:0,x:42};
o3.h = function() { return this.x; };
-for (var i = 0; i < 10000; i++) TestInlineThisX(o3);
+for (var i = 0; i < 5; i++) TestInlineThisX(o3);
+%OptimizeFunctionOnNextCall(TestInlineThisX);
+TestInlineThisX(o3);
TestInlineThisX({h: o3.h, x:42});
@@ -101,7 +109,9 @@
var o4 = {x:[1,2,3]};
o4.h = function() { return this.x.length; };
-for (var i = 0; i < 10000; i++) TestInlineThisXLength(o4);
+for (var i = 0; i < 5; i++) TestInlineThisXLength(o4);
+%OptimizeFunctionOnNextCall(TestInlineThisXLength);
+TestInlineThisXLength(o4);
TestInlineThisXLength({h: o4.h, x:[1,2,3]});
@@ -122,7 +132,9 @@
var o6 = {y:42}
var o5 = {e:o6};
o5.h = function() { return this.e.y; };
-for (var i = 0; i < 10000; i++) TestInlineThisXY(o5);
+for (var i = 0; i < 5; i++) TestInlineThisXY(o5);
+%OptimizeFunctionOnNextCall(TestInlineThisXY);
+TestInlineThisXY(o5);
TestInlineThisXY({h: o5.h, e:o6});
@@ -142,5 +154,7 @@
var o7 = {x:[42,43,44]};
o7.foo = function() { return this.x[0]; };
-for (var i = 0; i < 10000; i++) TestInlineThisX0(o7);
+for (var i = 0; i < 5; i++) TestInlineThisX0(o7);
+%OptimizeFunctionOnNextCall(TestInlineThisX0);
+TestInlineThisX0(o7);
TestInlineThisX0({foo: o7.foo, x:[42,0,0]});
diff --git a/test/mjsunit/compiler/switch-bailout.js b/test/mjsunit/compiler/switch-bailout.js
index 8011d44..084074e 100644
--- a/test/mjsunit/compiler/switch-bailout.js
+++ b/test/mjsunit/compiler/switch-bailout.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Test that bailing out of the optimized compilation doesn't mess with
// the labels in the AST.
function f(x) {
@@ -35,5 +37,7 @@
return 99;
}
-for (var i = 0; i < 10000; i++) f("foo");
+for (var i = 0; i < 5; i++) f("foo");
+%OptimizeFunctionOnNextCall(f);
+f("foo");
assertEquals(42, f("bar"));
diff --git a/test/mjsunit/const-eval-init.js b/test/mjsunit/const-eval-init.js
index 3f380d9..d350384 100644
--- a/test/mjsunit/const-eval-init.js
+++ b/test/mjsunit/const-eval-init.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Test the handling of initialization of deleted const variables.
// This only makes sense in local scopes since the declaration and
// initialization of consts in the global scope happen at the same
@@ -67,9 +69,11 @@
assertEquals(7, x);
}
-for (var i = 0; i < 10000; i++) {
+for (var i = 0; i < 5; i++) {
testAssignmentArgument();
}
+%OptimizeFunctionOnNextCall(testAssignmentArgument);
+testAssignmentArgument();
assertEquals(6, x);
__defineSetter__('x', function() { throw 42; });
diff --git a/test/mjsunit/const.js b/test/mjsunit/const.js
index a48e82d..adb0b7a 100644
--- a/test/mjsunit/const.js
+++ b/test/mjsunit/const.js
@@ -50,20 +50,20 @@
var valueOfCount = 0;
function g() {
- const o = { valueOf: function() { valueOfCount++; return 42; } }
- assertEquals(42, o);
+ const o = { valueOf: function() { valueOfCount++; return 42; } };
+ assertEquals(42, +o);
assertEquals(1, valueOfCount);
o++;
- assertEquals(42, o);
+ assertEquals(42, +o);
assertEquals(3, valueOfCount);
++o;
- assertEquals(42, o);
+ assertEquals(42, +o);
assertEquals(5, valueOfCount);
o--;
- assertEquals(42, o);
+ assertEquals(42, +o);
assertEquals(7, valueOfCount);
--o;
- assertEquals(42, o);
+ assertEquals(42, +o);
assertEquals(9, valueOfCount);
}
diff --git a/test/mjsunit/cyrillic.js b/test/mjsunit/cyrillic.js
index c5712e6..9b21c4f 100644
--- a/test/mjsunit/cyrillic.js
+++ b/test/mjsunit/cyrillic.js
@@ -187,9 +187,9 @@
var ignore_case = (j == 0);
var flag = ignore_case ? "i" : "";
var re = new RegExp(mixed, flag);
- assertEquals(ignore_case || (full && add_non_ascii_character_to_subject),
- re.test("A" + suffix),
- 58 + flag + f);
+ var expected =
+ ignore_case || (full && !!add_non_ascii_character_to_subject);
+ assertEquals(expected, re.test("A" + suffix), 58 + flag + f);
assertTrue(re.test("a" + suffix), 59 + flag + f);
assertTrue(re.test("~" + suffix), 60 + flag + f);
assertTrue(re.test(cyrillic.MIDDLE), 61 + flag + f);
diff --git a/test/mjsunit/debug-evaluate-with.js b/test/mjsunit/debug-evaluate-with.js
index 9d95a9f..c19a707 100644
--- a/test/mjsunit/debug-evaluate-with.js
+++ b/test/mjsunit/debug-evaluate-with.js
@@ -42,13 +42,13 @@
// Break point in first with block.
assertEquals(2, exec_state.frame(0).evaluate('a').value());
assertEquals(2, exec_state.frame(0).evaluate('b').value());
- } else {
+ } else if (breakPointCount == 2) {
// Break point in second with block.
assertEquals(3, exec_state.frame(0).evaluate('a').value());
assertEquals(1, exec_state.frame(0).evaluate('b').value());
-
- // Indicate that all was processed.
- listenerComplete = true;
+ } else if (breakPointCount == 3) {
+ // Break point in eval with block.
+ assertEquals('local', exec_state.frame(0).evaluate('foo').value());
}
}
} catch (e) {
@@ -72,6 +72,10 @@
};
f();
+
+var foo = "global";
+eval("with({bar:'with'}) { (function g() { var foo = 'local'; debugger; })(); }");
+
// Make sure that the debug event listener vas invoked.
-assertTrue(listenerComplete);
+assertEquals(3, breakPointCount);
assertFalse(exception, "exception in listener")
diff --git a/test/mjsunit/debug-scopes.js b/test/mjsunit/debug-scopes.js
index 37cefd1..40adf5b 100644
--- a/test/mjsunit/debug-scopes.js
+++ b/test/mjsunit/debug-scopes.js
@@ -31,9 +31,9 @@
// Get the Debug object exposed from the debug context global object.
-Debug = debug.Debug
+Debug = debug.Debug;
-var name;
+var test_name;
var listener_delegate;
var listener_called;
var exception;
@@ -48,7 +48,7 @@
if (event == Debug.DebugEvent.Break) {
break_count++;
listener_called = true;
- listener_delegate(exec_state)
+ listener_delegate(exec_state);
}
} catch (e) {
exception = e;
@@ -59,7 +59,7 @@
Debug.setListener(listener);
-// Initialize for a noew test.
+// Initialize for a new test.
function BeginTest(name) {
test_name = name;
listener_delegate = null;
@@ -72,7 +72,7 @@
// Check result of a test.
function EndTest() {
assertTrue(listener_called, "listerner not called for " + test_name);
- assertNull(exception, test_name)
+ assertNull(exception, test_name);
end_test_count++;
}
@@ -87,7 +87,9 @@
// Check the global object when hitting the global scope.
if (scopes[i] == debug.ScopeType.Global) {
- assertEquals(this, scope.scopeObject().value());
+ // Objects don't have same class (one is "global", other is "Object",
+ // so just check the properties directly.
+ assertPropertiesEqual(this, scope.scopeObject().value());
}
}
@@ -96,7 +98,7 @@
// Send a scopes request and check the result.
var json;
- request_json = '{"seq":0,"type":"request","command":"scopes"}'
+ var request_json = '{"seq":0,"type":"request","command":"scopes"}';
var response_json = dcp.processDebugJSONRequest(request_json);
var response = JSON.parse(response_json);
assertEquals(scopes.length, response.body.scopes.length);
@@ -121,7 +123,7 @@
// Check that the content of the scope is as expected. For functions just check
// that there is a function.
function CheckScopeContent(content, number, exec_state) {
- var scope = exec_state.frame().scope(number)
+ var scope = exec_state.frame().scope(number);
var count = 0;
for (var p in content) {
var property_mirror = scope.scopeObject().property(p);
@@ -144,6 +146,10 @@
if (!scope.scopeObject().property('.catch-var').isUndefined()) {
scope_size--;
}
+ // Skip property with empty name.
+ if (!scope.scopeObject().property('').isUndefined()) {
+ scope_size--;
+ }
if (count != scope_size) {
print('Names found in scope:');
@@ -159,9 +165,9 @@
// Send a scope request for information on a single scope and check the
// result.
- request_json = '{"seq":0,"type":"request","command":"scope","arguments":{"number":'
+ var request_json = '{"seq":0,"type":"request","command":"scope","arguments":{"number":';
request_json += scope.scopeIndex();
- request_json += '}}'
+ request_json += '}}';
var response_json = dcp.processDebugJSONRequest(request_json);
var response = JSON.parse(response_json);
assertEquals(scope.scopeType(), response.body.type);
@@ -191,8 +197,8 @@
CheckScopeChain([debug.ScopeType.Local,
debug.ScopeType.Global], exec_state);
CheckScopeContent({}, 0, exec_state);
-}
-local_1()
+};
+local_1();
EndTest();
@@ -207,8 +213,8 @@
CheckScopeChain([debug.ScopeType.Local,
debug.ScopeType.Global], exec_state);
CheckScopeContent({a:1}, 0, exec_state);
-}
-local_2(1)
+};
+local_2(1);
EndTest();
@@ -224,8 +230,8 @@
CheckScopeChain([debug.ScopeType.Local,
debug.ScopeType.Global], exec_state);
CheckScopeContent({a:1,x:3}, 0, exec_state);
-}
-local_3(1)
+};
+local_3(1);
EndTest();
@@ -242,8 +248,8 @@
CheckScopeChain([debug.ScopeType.Local,
debug.ScopeType.Global], exec_state);
CheckScopeContent({a:1,b:2,x:3,y:4}, 0, exec_state);
-}
-local_4(1, 2)
+};
+local_4(1, 2);
EndTest();
@@ -259,8 +265,8 @@
CheckScopeChain([debug.ScopeType.Local,
debug.ScopeType.Global], exec_state);
CheckScopeContent({}, 0, exec_state);
-}
-local_5()
+};
+local_5();
EndTest();
@@ -276,8 +282,8 @@
CheckScopeChain([debug.ScopeType.Local,
debug.ScopeType.Global], exec_state);
CheckScopeContent({i:5}, 0, exec_state);
-}
-local_6()
+};
+local_6();
EndTest();
@@ -297,8 +303,8 @@
CheckScopeChain([debug.ScopeType.Local,
debug.ScopeType.Global], exec_state);
CheckScopeContent({a:1,b:2,x:3,y:4,i:5,j:6}, 0, exec_state);
-}
-local_7(1, 2)
+};
+local_7(1, 2);
EndTest();
@@ -316,8 +322,8 @@
debug.ScopeType.Local,
debug.ScopeType.Global], exec_state);
CheckScopeContent({}, 0, exec_state);
-}
-with_1()
+};
+with_1();
EndTest();
@@ -339,8 +345,8 @@
debug.ScopeType.Global], exec_state);
CheckScopeContent({}, 0, exec_state);
CheckScopeContent({}, 1, exec_state);
-}
-with_2()
+};
+with_2();
EndTest();
@@ -358,8 +364,8 @@
debug.ScopeType.Local,
debug.ScopeType.Global], exec_state);
CheckScopeContent({a:1,b:2}, 0, exec_state);
-}
-with_3()
+};
+with_3();
EndTest();
@@ -381,8 +387,8 @@
debug.ScopeType.Global], exec_state);
CheckScopeContent({a:2,b:1}, 0, exec_state);
CheckScopeContent({a:1,b:2}, 1, exec_state);
-}
-with_4()
+};
+with_4();
EndTest();
@@ -407,8 +413,8 @@
CheckScopeContent(with_object, 1, exec_state);
assertEquals(exec_state.frame().scope(0).scopeObject(), exec_state.frame().scope(1).scopeObject());
assertEquals(with_object, exec_state.frame().scope(1).scopeObject().value());
-}
-with_5()
+};
+with_5();
EndTest();
@@ -429,8 +435,8 @@
debug.ScopeType.Closure,
debug.ScopeType.Global], exec_state);
CheckScopeContent({a:1}, 1, exec_state);
-}
-closure_1(1)()
+};
+closure_1(1)();
EndTest();
@@ -454,8 +460,8 @@
debug.ScopeType.Closure,
debug.ScopeType.Global], exec_state);
CheckScopeContent({a:1,x:3}, 1, exec_state);
-}
-closure_2(1, 2)()
+};
+closure_2(1, 2)();
EndTest();
@@ -480,8 +486,8 @@
debug.ScopeType.Closure,
debug.ScopeType.Global], exec_state);
CheckScopeContent({a:1,b:2,x:3,y:4}, 1, exec_state);
-}
-closure_3(1, 2)()
+};
+closure_3(1, 2)();
EndTest();
@@ -509,8 +515,8 @@
debug.ScopeType.Closure,
debug.ScopeType.Global], exec_state);
CheckScopeContent({a:1,b:2,x:3,y:4,f:function(){}}, 1, exec_state);
-}
-closure_4(1, 2)()
+};
+closure_4(1, 2)();
EndTest();
@@ -537,8 +543,8 @@
debug.ScopeType.Closure,
debug.ScopeType.Global], exec_state);
CheckScopeContent({a:1,b:2,x:3,y:4,f:function(){}}, 1, exec_state);
-}
-closure_5(1, 2)()
+};
+closure_5(1, 2)();
EndTest();
@@ -555,7 +561,7 @@
debugger;
some_global = a;
return f;
- }
+ };
}
return f(a, b);
}
@@ -567,8 +573,8 @@
debug.ScopeType.Global], exec_state);
CheckScopeContent({a:1}, 1, exec_state);
CheckScopeContent({f:function(){}}, 2, exec_state);
-}
-closure_6(1, 2)()
+};
+closure_6(1, 2)();
EndTest();
@@ -589,7 +595,7 @@
debugger;
some_global = a;
return f;
- }
+ };
}
return f(a, b);
}
@@ -602,8 +608,45 @@
CheckScopeContent({}, 0, exec_state);
CheckScopeContent({a:1,b:2,x:3,y:4,i:5,j:6}, 1, exec_state);
CheckScopeContent({a:1,b:2,x:3,y:4,i:5,j:6,f:function(){}}, 2, exec_state);
+};
+closure_7(1, 2)();
+EndTest();
+
+
+// Closure that may be optimized out.
+BeginTest("Closure 8");
+function closure_8() {
+ (function inner(x) {
+ debugger;
+ })(2);
}
-closure_7(1, 2)()
+
+listener_delegate = function(exec_state) {
+ CheckScopeChain([debug.ScopeType.Local,
+ debug.ScopeType.Global], exec_state);
+ CheckScopeContent({x: 2}, 0, exec_state);
+};
+closure_8();
+EndTest();
+
+
+BeginTest("Closure 9");
+function closure_9() {
+ eval("var y = 1;");
+ eval("var z = 1;");
+ (function inner(x) {
+ y++;
+ z++;
+ debugger;
+ })(2);
+}
+
+listener_delegate = function(exec_state) {
+ CheckScopeChain([debug.ScopeType.Local,
+ debug.ScopeType.Closure,
+ debug.ScopeType.Global], exec_state);
+};
+closure_9();
EndTest();
@@ -629,7 +672,7 @@
return f;
}
}
- }
+ };
}
}
return f(a, b);
@@ -649,15 +692,90 @@
CheckScopeContent({j:13}, 3, exec_state);
CheckScopeContent({a:1,b:2,x:9,y:10,i:11,j:12}, 4, exec_state);
CheckScopeContent({a:1,b:2,x:3,y:4,i:5,j:6,f:function(){}}, 5, exec_state);
-}
-the_full_monty(1, 2)()
+};
+the_full_monty(1, 2)();
EndTest();
+
+BeginTest("Closure inside With 1");
+function closure_in_with_1() {
+ with({x:1}) {
+ (function inner(x) {
+ debugger;
+ })(2);
+ }
+}
+
+listener_delegate = function(exec_state) {
+ CheckScopeChain([debug.ScopeType.Local,
+ debug.ScopeType.With,
+ debug.ScopeType.Closure,
+ debug.ScopeType.Global], exec_state);
+ CheckScopeContent({x: 2}, 0, exec_state);
+};
+closure_in_with_1();
+EndTest();
+
+
+BeginTest("Closure inside With 2");
+function closure_in_with_2() {
+ with({x:1}) {
+ (function inner(x) {
+ with({x:3}) {
+ debugger;
+ }
+ })(2);
+ }
+}
+
+listener_delegate = function(exec_state) {
+ CheckScopeChain([debug.ScopeType.With,
+ debug.ScopeType.Local,
+ debug.ScopeType.With,
+ debug.ScopeType.Closure,
+ debug.ScopeType.Global], exec_state);
+ CheckScopeContent({x: 3}, 0, exec_state);
+ CheckScopeContent({x: 2}, 1, exec_state);
+ CheckScopeContent({x: 1}, 2, exec_state);
+};
+closure_in_with_2();
+EndTest();
+
+
+BeginTest("Closure inside With 3");
+function createClosure(a) {
+ var b = a + 1;
+ return function closure() {
+ var c = b;
+ (function inner(x) {
+ with({x:c}) {
+ debugger;
+ }
+ })(2);
+ };
+}
+
+function closure_in_with_3() {
+ var f = createClosure(0);
+ f();
+}
+
+listener_delegate = function(exec_state) {
+ CheckScopeChain([debug.ScopeType.With,
+ debug.ScopeType.Local,
+ debug.ScopeType.Closure,
+ debug.ScopeType.Closure,
+ debug.ScopeType.Global], exec_state);
+}
+closure_in_with_3();
+EndTest();
+
+
// Test global scope.
BeginTest("Global");
listener_delegate = function(exec_state) {
CheckScopeChain([debug.ScopeType.Global], exec_state);
-}
+};
debugger;
EndTest();
@@ -677,8 +795,8 @@
debug.ScopeType.Local,
debug.ScopeType.Global], exec_state);
CheckScopeContent({e:'Exception'}, 0, exec_state);
-}
-catch_block_1()
+};
+catch_block_1();
EndTest();
@@ -701,8 +819,8 @@
debug.ScopeType.Global], exec_state);
CheckScopeContent({n:10}, 0, exec_state);
CheckScopeContent({e:'Exception'}, 1, exec_state);
-}
-catch_block_2()
+};
+catch_block_2();
EndTest();
@@ -725,8 +843,8 @@
debug.ScopeType.Global], exec_state);
CheckScopeContent({e:'Exception'}, 0, exec_state);
CheckScopeContent({y:78}, 1, exec_state);
-}
-catch_block_3()
+};
+catch_block_3();
EndTest();
@@ -752,10 +870,12 @@
CheckScopeContent({n:10}, 0, exec_state);
CheckScopeContent({e:'Exception'}, 1, exec_state);
CheckScopeContent({y:98}, 2, exec_state);
-}
-catch_block_4()
+};
+catch_block_4();
EndTest();
-assertEquals(begin_test_count, break_count, 'one or more tests did not enter the debugger');
-assertEquals(begin_test_count, end_test_count, 'one or more tests did not have its result checked');
+assertEquals(begin_test_count, break_count,
+ 'one or more tests did not enter the debugger');
+assertEquals(begin_test_count, end_test_count,
+ 'one or more tests did not have its result checked');
diff --git a/src/arm/register-allocator-arm.h b/test/mjsunit/external-array.js
similarity index 61%
copy from src/arm/register-allocator-arm.h
copy to test/mjsunit/external-array.js
index fdbc88f..45d8be5 100644
--- a/src/arm/register-allocator-arm.h
+++ b/test/mjsunit/external-array.js
@@ -1,4 +1,4 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -25,20 +25,41 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#ifndef V8_ARM_REGISTER_ALLOCATOR_ARM_H_
-#define V8_ARM_REGISTER_ALLOCATOR_ARM_H_
+// Flags: --allow-natives-syntax
-namespace v8 {
-namespace internal {
+// This is a regression test for overlapping key and value registers.
+function f(a) {
+ a[0] = 0;
+ a[1] = 0;
+}
-class RegisterAllocatorConstants : public AllStatic {
- public:
- // No registers are currently managed by the register allocator on ARM.
- static const int kNumRegisters = 0;
- static const int kInvalidRegister = -1;
-};
+var a = new Int32Array(2);
+for (var i = 0; i < 5; i++) {
+ f(a);
+}
+%OptimizeFunctionOnNextCall(f);
+f(a);
+assertEquals(0, a[0]);
+assertEquals(0, a[1]);
-} } // namespace v8::internal
+// Test the correct behavior of the |length| property (which is read-only).
+a = new Int32Array(42);
+assertEquals(42, a.length);
+a.length = 2;
+assertEquals(42, a.length);
+assertTrue(delete a.length);
+a.length = 2
+assertEquals(2, a.length);
-#endif // V8_ARM_REGISTER_ALLOCATOR_ARM_H_
+// Test the correct behavior of the |BYTES_PER_ELEMENT| property (which is
+// "constant", but not read-only).
+a = new Int32Array(2);
+assertEquals(4, a.BYTES_PER_ELEMENT);
+a.BYTES_PER_ELEMENT = 42;
+assertEquals(42, a.BYTES_PER_ELEMENT);
+a = new Uint8Array(2);
+assertEquals(1, a.BYTES_PER_ELEMENT);
+a = new Int16Array(2);
+assertEquals(2, a.BYTES_PER_ELEMENT);
+
diff --git a/src/x64/codegen-x64-inl.h b/test/mjsunit/global-accessors.js
similarity index 78%
copy from src/x64/codegen-x64-inl.h
copy to test/mjsunit/global-accessors.js
index 53caf91..47f4328 100644
--- a/src/x64/codegen-x64-inl.h
+++ b/test/mjsunit/global-accessors.js
@@ -25,22 +25,28 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Test accessors on the global object.
-#ifndef V8_X64_CODEGEN_X64_INL_H_
-#define V8_X64_CODEGEN_X64_INL_H_
+var x_ = 0;
-namespace v8 {
-namespace internal {
+__defineSetter__('x', function(x) { x_ = x; });
+__defineGetter__('x', function() { return x_; });
-#define __ ACCESS_MASM(masm_)
+__defineSetter__('y', function(x) { });
+__defineGetter__('y', function() { return 7; });
-// Platform-specific inline functions.
+function f(a) {
+ x = x + a;
+ return x;
+}
-void DeferredCode::Jump() { __ jmp(&entry_label_); }
-void DeferredCode::Branch(Condition cc) { __ j(cc, &entry_label_); }
+function g(a) {
+ y = y + a;
+ return y;
+}
-#undef __
+assertEquals(1, f(1));
+assertEquals(3, f(2));
-} } // namespace v8::internal
-
-#endif // V8_X64_CODEGEN_X64_INL_H_
+assertEquals(7, g(1));
+assertEquals(7, g(2));
diff --git a/test/mjsunit/math-abs.js b/test/mjsunit/math-abs.js
index bec1a01..33df6f4 100644
--- a/test/mjsunit/math-abs.js
+++ b/test/mjsunit/math-abs.js
@@ -35,13 +35,13 @@
function test() {
assertEquals(0, Math.abs(0));
assertEquals(0, Math.abs(zero()));
- assertEquals(1/0, 1/Math.abs(-0)); // 0 == -0, so we use reciprocals.
+ assertEquals(0, Math.abs(-0));
assertEquals(Infinity, Math.abs(Infinity));
assertEquals(Infinity, Math.abs(-Infinity));
- assertNaN(Math.abs(NaN));
- assertNaN(Math.abs(-NaN));
- assertEquals('Infinity', Math.abs(Number('+Infinity').toString()));
- assertEquals('Infinity', Math.abs(Number('-Infinity').toString()));
+ assertEquals(NaN, Math.abs(NaN));
+ assertEquals(NaN, Math.abs(-NaN));
+ assertEquals('Infinity', Math.abs(Number('+Infinity')).toString());
+ assertEquals('Infinity', Math.abs(Number('-Infinity')).toString());
assertEquals('NaN', Math.abs(NaN).toString());
assertEquals('NaN', Math.abs(-NaN).toString());
@@ -85,8 +85,8 @@
assertEquals(two_31 - 1, Math.abs(two_31 - 1));
assertEquals(two_31 - 1, Math.abs(-two_31 + 1));
- assertNaN(Math.abs("not a number"));
- assertNaN(Math.abs([1, 2, 3]));
+ assertEquals(NaN, Math.abs("not a number"));
+ assertEquals(NaN, Math.abs([1, 2, 3]));
assertEquals(42, Math.abs({valueOf: function() { return 42; } }));
assertEquals(42, Math.abs({valueOf: function() { return -42; } }));
}
diff --git a/test/mjsunit/math-floor.js b/test/mjsunit/math-floor.js
index 0d1c0ac..11f4cd7 100644
--- a/test/mjsunit/math-floor.js
+++ b/test/mjsunit/math-floor.js
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -25,7 +25,18 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-// Flags: --max-new-space-size=256
+// Flags: --max-new-space-size=256 --allow-natives-syntax
+
+function testFloor(expect, input) {
+ function test(n) {
+ return Math.floor(n);
+ }
+ assertEquals(expect, test(input));
+ assertEquals(expect, test(input));
+ assertEquals(expect, test(input));
+ %OptimizeFunctionOnNextCall(test);
+ assertEquals(expect, test(input));
+}
function zero() {
var x = 0.5;
@@ -33,82 +44,84 @@
}
function test() {
- assertEquals(0, Math.floor(0));
- assertEquals(0, Math.floor(zero()));
- assertEquals(1/-0, 1/Math.floor(-0)); // 0 == -0, so we use reciprocals.
- assertEquals(Infinity, Math.floor(Infinity));
- assertEquals(-Infinity, Math.floor(-Infinity));
- assertNaN(Math.floor(NaN));
+ testFloor(0, 0);
+ testFloor(0, zero());
+ testFloor(-0, -0);
+ testFloor(Infinity, Infinity);
+ testFloor(-Infinity, -Infinity);
+ testFloor(NaN, NaN);
- assertEquals(0, Math.floor(0.1));
- assertEquals(0, Math.floor(0.5));
- assertEquals(0, Math.floor(0.7));
- assertEquals(-1, Math.floor(-0.1));
- assertEquals(-1, Math.floor(-0.5));
- assertEquals(-1, Math.floor(-0.7));
- assertEquals(1, Math.floor(1));
- assertEquals(1, Math.floor(1.1));
- assertEquals(1, Math.floor(1.5));
- assertEquals(1, Math.floor(1.7));
- assertEquals(-1, Math.floor(-1));
- assertEquals(-2, Math.floor(-1.1));
- assertEquals(-2, Math.floor(-1.5));
- assertEquals(-2, Math.floor(-1.7));
+ testFloor(0, 0.1);
+ testFloor(0, 0.49999999999999994);
+ testFloor(0, 0.5);
+ testFloor(0, 0.7);
+ testFloor(-1, -0.1);
+ testFloor(-1, -0.49999999999999994);
+ testFloor(-1, -0.5);
+ testFloor(-1, -0.7);
+ testFloor(1, 1);
+ testFloor(1, 1.1);
+ testFloor(1, 1.5);
+ testFloor(1, 1.7);
+ testFloor(-1, -1);
+ testFloor(-2, -1.1);
+ testFloor(-2, -1.5);
+ testFloor(-2, -1.7);
- assertEquals(0, Math.floor(Number.MIN_VALUE));
- assertEquals(-1, Math.floor(-Number.MIN_VALUE));
- assertEquals(Number.MAX_VALUE, Math.floor(Number.MAX_VALUE));
- assertEquals(-Number.MAX_VALUE, Math.floor(-Number.MAX_VALUE));
- assertEquals(Infinity, Math.floor(Infinity));
- assertEquals(-Infinity, Math.floor(-Infinity));
+ testFloor(0, Number.MIN_VALUE);
+ testFloor(-1, -Number.MIN_VALUE);
+ testFloor(Number.MAX_VALUE, Number.MAX_VALUE);
+ testFloor(-Number.MAX_VALUE, -Number.MAX_VALUE);
+ testFloor(Infinity, Infinity);
+ testFloor(-Infinity, -Infinity);
// 2^30 is a smi boundary.
var two_30 = 1 << 30;
- assertEquals(two_30, Math.floor(two_30));
- assertEquals(two_30, Math.floor(two_30 + 0.1));
- assertEquals(two_30, Math.floor(two_30 + 0.5));
- assertEquals(two_30, Math.floor(two_30 + 0.7));
+ testFloor(two_30, two_30);
+ testFloor(two_30, two_30 + 0.1);
+ testFloor(two_30, two_30 + 0.5);
+ testFloor(two_30, two_30 + 0.7);
- assertEquals(two_30 - 1, Math.floor(two_30 - 1));
- assertEquals(two_30 - 1, Math.floor(two_30 - 1 + 0.1));
- assertEquals(two_30 - 1, Math.floor(two_30 - 1 + 0.5));
- assertEquals(two_30 - 1, Math.floor(two_30 - 1 + 0.7));
+ testFloor(two_30 - 1, two_30 - 1);
+ testFloor(two_30 - 1, two_30 - 1 + 0.1);
+ testFloor(two_30 - 1, two_30 - 1 + 0.5);
+ testFloor(two_30 - 1, two_30 - 1 + 0.7);
- assertEquals(-two_30, Math.floor(-two_30));
- assertEquals(-two_30, Math.floor(-two_30 + 0.1));
- assertEquals(-two_30, Math.floor(-two_30 + 0.5));
- assertEquals(-two_30, Math.floor(-two_30 + 0.7));
+ testFloor(-two_30, -two_30);
+ testFloor(-two_30, -two_30 + 0.1);
+ testFloor(-two_30, -two_30 + 0.5);
+ testFloor(-two_30, -two_30 + 0.7);
- assertEquals(-two_30 + 1, Math.floor(-two_30 + 1));
- assertEquals(-two_30 + 1, Math.floor(-two_30 + 1 + 0.1));
- assertEquals(-two_30 + 1, Math.floor(-two_30 + 1 + 0.5));
- assertEquals(-two_30 + 1, Math.floor(-two_30 + 1 + 0.7));
+ testFloor(-two_30 + 1, -two_30 + 1);
+ testFloor(-two_30 + 1, -two_30 + 1 + 0.1);
+ testFloor(-two_30 + 1, -two_30 + 1 + 0.5);
+ testFloor(-two_30 + 1, -two_30 + 1 + 0.7);
// 2^52 is a precision boundary.
var two_52 = (1 << 30) * (1 << 22);
- assertEquals(two_52, Math.floor(two_52));
- assertEquals(two_52, Math.floor(two_52 + 0.1));
+ testFloor(two_52, two_52);
+ testFloor(two_52, two_52 + 0.1);
assertEquals(two_52, two_52 + 0.5);
- assertEquals(two_52, Math.floor(two_52 + 0.5));
+ testFloor(two_52, two_52 + 0.5);
assertEquals(two_52 + 1, two_52 + 0.7);
- assertEquals(two_52 + 1, Math.floor(two_52 + 0.7));
+ testFloor(two_52 + 1, two_52 + 0.7);
- assertEquals(two_52 - 1, Math.floor(two_52 - 1));
- assertEquals(two_52 - 1, Math.floor(two_52 - 1 + 0.1));
- assertEquals(two_52 - 1, Math.floor(two_52 - 1 + 0.5));
- assertEquals(two_52 - 1, Math.floor(two_52 - 1 + 0.7));
+ testFloor(two_52 - 1, two_52 - 1);
+ testFloor(two_52 - 1, two_52 - 1 + 0.1);
+ testFloor(two_52 - 1, two_52 - 1 + 0.5);
+ testFloor(two_52 - 1, two_52 - 1 + 0.7);
- assertEquals(-two_52, Math.floor(-two_52));
- assertEquals(-two_52, Math.floor(-two_52 + 0.1));
- assertEquals(-two_52, Math.floor(-two_52 + 0.5));
- assertEquals(-two_52, Math.floor(-two_52 + 0.7));
+ testFloor(-two_52, -two_52);
+ testFloor(-two_52, -two_52 + 0.1);
+ testFloor(-two_52, -two_52 + 0.5);
+ testFloor(-two_52, -two_52 + 0.7);
- assertEquals(-two_52 + 1, Math.floor(-two_52 + 1));
- assertEquals(-two_52 + 1, Math.floor(-two_52 + 1 + 0.1));
- assertEquals(-two_52 + 1, Math.floor(-two_52 + 1 + 0.5));
- assertEquals(-two_52 + 1, Math.floor(-two_52 + 1 + 0.7));
+ testFloor(-two_52 + 1, -two_52 + 1);
+ testFloor(-two_52 + 1, -two_52 + 1 + 0.1);
+ testFloor(-two_52 + 1, -two_52 + 1 + 0.5);
+ testFloor(-two_52 + 1, -two_52 + 1 + 0.7);
}
diff --git a/test/mjsunit/math-min-max.js b/test/mjsunit/math-min-max.js
index 13d54a3..0833c5c 100644
--- a/test/mjsunit/math-min-max.js
+++ b/test/mjsunit/math-min-max.js
@@ -76,9 +76,9 @@
assertEquals(-1, Math.min(-1, +0, -0));
assertEquals(-1, Math.min(+0, -1, -0));
assertEquals(-1, Math.min(-0, -1, +0));
-assertNaN(Math.min('oxen'));
-assertNaN(Math.min('oxen', 1));
-assertNaN(Math.min(1, 'oxen'));
+assertEquals(NaN, Math.min('oxen'));
+assertEquals(NaN, Math.min('oxen', 1));
+assertEquals(NaN, Math.min(1, 'oxen'));
// Test Math.max().
@@ -109,9 +109,9 @@
assertEquals(1, Math.max(+1, +0, -0));
assertEquals(1, Math.max(+0, +1, -0));
assertEquals(1, Math.max(-0, +1, +0));
-assertNaN(Math.max('oxen'));
-assertNaN(Math.max('oxen', 1));
-assertNaN(Math.max(1, 'oxen'));
+assertEquals(NaN, Math.max('oxen'));
+assertEquals(NaN, Math.max('oxen', 1));
+assertEquals(NaN, Math.max(1, 'oxen'));
assertEquals(Infinity, 1/Math.max(ZERO, -0));
assertEquals(Infinity, 1/Math.max(-0, ZERO));
diff --git a/test/mjsunit/math-round.js b/test/mjsunit/math-round.js
index 3b06088..1366557 100644
--- a/test/mjsunit/math-round.js
+++ b/test/mjsunit/math-round.js
@@ -25,77 +25,133 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-assertEquals(0, Math.round(0));
-assertEquals(-0, Math.round(-0));
-assertEquals(Infinity, Math.round(Infinity));
-assertEquals(-Infinity, Math.round(-Infinity));
-assertNaN(Math.round(NaN));
+// Flags: --allow-natives-syntax
-assertEquals(1, Math.round(0.5));
-assertEquals(1, Math.round(0.7));
-assertEquals(1, Math.round(1));
-assertEquals(1, Math.round(1.1));
-assertEquals(1, Math.round(1.49999));
-assertEquals(1/-0, 1/Math.round(-0.5)); // Test for -0 result.
-assertEquals(-1, Math.round(-0.5000000000000001));
-assertEquals(-1, Math.round(-0.7));
-assertEquals(-1, Math.round(-1));
-assertEquals(-1, Math.round(-1.1));
-assertEquals(-1, Math.round(-1.49999));
-assertEquals(-1, Math.round(-1.5));
+function testRound(expect, input) {
+ function doRound(input) {
+ return Math.round(input);
+ }
+ assertEquals(expect, doRound(input));
+ assertEquals(expect, doRound(input));
+ assertEquals(expect, doRound(input));
+ %OptimizeFunctionOnNextCall(doRound);
+ assertEquals(expect, doRound(input));
+}
-assertEquals(9007199254740990, Math.round(9007199254740990));
-assertEquals(9007199254740991, Math.round(9007199254740991));
-assertEquals(-9007199254740990, Math.round(-9007199254740990));
-assertEquals(-9007199254740991, Math.round(-9007199254740991));
-assertEquals(Number.MAX_VALUE, Math.round(Number.MAX_VALUE));
-assertEquals(-Number.MAX_VALUE, Math.round(-Number.MAX_VALUE));
+testRound(0, 0);
+testRound(-0, -0);
+testRound(Infinity, Infinity);
+testRound(-Infinity, -Infinity);
+testRound(NaN, NaN);
-assertEquals(536870911, Math.round(536870910.5));
-assertEquals(536870911, Math.round(536870911));
-assertEquals(536870911, Math.round(536870911.4));
-assertEquals(536870912, Math.round(536870911.5));
-assertEquals(536870912, Math.round(536870912));
-assertEquals(536870912, Math.round(536870912.4));
-assertEquals(536870913, Math.round(536870912.5));
-assertEquals(536870913, Math.round(536870913));
-assertEquals(536870913, Math.round(536870913.4));
-assertEquals(1073741823, Math.round(1073741822.5));
-assertEquals(1073741823, Math.round(1073741823));
-assertEquals(1073741823, Math.round(1073741823.4));
-assertEquals(1073741824, Math.round(1073741823.5));
-assertEquals(1073741824, Math.round(1073741824));
-assertEquals(1073741824, Math.round(1073741824.4));
-assertEquals(1073741825, Math.round(1073741824.5));
-assertEquals(2147483647, Math.round(2147483646.5));
-assertEquals(2147483647, Math.round(2147483647));
-assertEquals(2147483647, Math.round(2147483647.4));
-assertEquals(2147483648, Math.round(2147483647.5));
-assertEquals(2147483648, Math.round(2147483648));
-assertEquals(2147483648, Math.round(2147483648.4));
-assertEquals(2147483649, Math.round(2147483648.5));
+testRound(1, 0.5);
+testRound(1, 0.7);
+testRound(1, 1);
+testRound(1, 1.1);
+testRound(1, 1.49999);
+testRound(-0, -0.5);
+testRound(-1, -0.5000000000000001);
+testRound(-1, -0.7);
+testRound(-1, -1);
+testRound(-1, -1.1);
+testRound(-1, -1.49999);
+testRound(-1, -1.5);
+
+testRound(9007199254740990, 9007199254740990);
+testRound(9007199254740991, 9007199254740991);
+testRound(-9007199254740990, -9007199254740990);
+testRound(-9007199254740991, -9007199254740991);
+testRound(Number.MAX_VALUE, Number.MAX_VALUE);
+testRound(-Number.MAX_VALUE, -Number.MAX_VALUE);
+
+testRound(536870911, 536870910.5);
+testRound(536870911, 536870911);
+testRound(536870911, 536870911.4);
+testRound(536870912, 536870911.5);
+testRound(536870912, 536870912);
+testRound(536870912, 536870912.4);
+testRound(536870913, 536870912.5);
+testRound(536870913, 536870913);
+testRound(536870913, 536870913.4);
+testRound(1073741823, 1073741822.5);
+testRound(1073741823, 1073741823);
+testRound(1073741823, 1073741823.4);
+testRound(1073741824, 1073741823.5);
+testRound(1073741824, 1073741824);
+testRound(1073741824, 1073741824.4);
+testRound(1073741825, 1073741824.5);
+testRound(2147483647, 2147483646.5);
+testRound(2147483647, 2147483647);
+testRound(2147483647, 2147483647.4);
+testRound(2147483648, 2147483647.5);
+testRound(2147483648, 2147483648);
+testRound(2147483648, 2147483648.4);
+testRound(2147483649, 2147483648.5);
// Tests based on WebKit LayoutTests
-assertEquals(0, Math.round(0.4));
-assertEquals(-0, Math.round(-0.4));
-assertEquals(-0, Math.round(-0.5));
-assertEquals(1, Math.round(0.6));
-assertEquals(-1, Math.round(-0.6));
-assertEquals(2, Math.round(1.5));
-assertEquals(2, Math.round(1.6));
-assertEquals(-2, Math.round(-1.6));
-assertEquals(8640000000000000, Math.round(8640000000000000));
-assertEquals(8640000000000001, Math.round(8640000000000001));
-assertEquals(8640000000000002, Math.round(8640000000000002));
-assertEquals(9007199254740990, Math.round(9007199254740990));
-assertEquals(9007199254740991, Math.round(9007199254740991));
-assertEquals(1.7976931348623157e+308, Math.round(1.7976931348623157e+308));
-assertEquals(-8640000000000000, Math.round(-8640000000000000));
-assertEquals(-8640000000000001, Math.round(-8640000000000001));
-assertEquals(-8640000000000002, Math.round(-8640000000000002));
-assertEquals(-9007199254740990, Math.round(-9007199254740990));
-assertEquals(-9007199254740991, Math.round(-9007199254740991));
-assertEquals(-1.7976931348623157e+308, Math.round(-1.7976931348623157e+308));
-assertEquals(Infinity, Math.round(Infinity));
-assertEquals(-Infinity, Math.round(-Infinity));
+testRound(0, 0.4);
+testRound(-0, -0.4);
+testRound(-0, -0.5);
+testRound(1, 0.6);
+testRound(-1, -0.6);
+testRound(2, 1.5);
+testRound(2, 1.6);
+testRound(-2, -1.6);
+testRound(8640000000000000, 8640000000000000);
+testRound(8640000000000001, 8640000000000001);
+testRound(8640000000000002, 8640000000000002);
+testRound(9007199254740990, 9007199254740990);
+testRound(9007199254740991, 9007199254740991);
+testRound(1.7976931348623157e+308, 1.7976931348623157e+308);
+testRound(-8640000000000000, -8640000000000000);
+testRound(-8640000000000001, -8640000000000001);
+testRound(-8640000000000002, -8640000000000002);
+testRound(-9007199254740990, -9007199254740990);
+testRound(-9007199254740991, -9007199254740991);
+testRound(-1.7976931348623157e+308, -1.7976931348623157e+308);
+testRound(Infinity, Infinity);
+testRound(-Infinity, -Infinity);
+
+ // Some special double number cases.
+var ulp = Math.pow(2, -1022 - 52);
+var max_denormal = (Math.pow(2, 52) - 1) * ulp;
+var min_normal = Math.pow(2, -1022);
+var max_fraction = Math.pow(2, 52) - 0.5;
+var min_nonfraction = Math.pow(2, 52);
+var max_non_infinite = Number.MAX_VALUE;
+
+var max_smi31 = Math.pow(2,30) - 1;
+var min_smi31 = -Math.pow(2,30);
+var max_smi32 = Math.pow(2,31) - 1;
+var min_smi32 = -Math.pow(2,31);
+
+testRound(0, ulp);
+testRound(0, max_denormal);
+testRound(0, min_normal);
+testRound(0, 0.49999999999999994);
+testRound(1, 0.5);
+testRound(Math.pow(2,52), max_fraction);
+testRound(min_nonfraction, min_nonfraction);
+testRound(max_non_infinite, max_non_infinite);
+
+testRound(max_smi31, max_smi31 - 0.5);
+testRound(max_smi31 + 1, max_smi31 + 0.5);
+testRound(max_smi32, max_smi32 - 0.5);
+testRound(max_smi32 + 1, max_smi32 + 0.5);
+
+testRound(-0, -ulp);
+testRound(-0, -max_denormal);
+testRound(-0, -min_normal);
+testRound(-0, -0.49999999999999994);
+testRound(-0, -0.5);
+testRound(-Math.pow(2,52)+1, -max_fraction);
+testRound(-min_nonfraction, -min_nonfraction);
+testRound(-max_non_infinite, -max_non_infinite);
+
+testRound(min_smi31, min_smi31 - 0.5);
+testRound(min_smi31 + 1, min_smi31 + 0.5);
+testRound(min_smi32, min_smi32 - 0.5);
+testRound(min_smi32 + 1, min_smi32 + 0.5);
+
+
diff --git a/test/mjsunit/mirror-number.js b/test/mjsunit/mirror-number.js
index 2db5df4..fc71c12 100644
--- a/test/mjsunit/mirror-number.js
+++ b/test/mjsunit/mirror-number.js
@@ -50,10 +50,10 @@
// Parse JSON representation and check.
var fromJSON = eval('(' + json + ')');
assertEquals('number', fromJSON.type);
- if (!isNaN(n)) {
+ if (isFinite(n)) {
assertEquals(n, fromJSON.value);
} else {
- // NaN values are encoded as strings.
+ // NaN and Infinity values are encoded as strings.
assertTrue(typeof fromJSON.value == 'string');
if (n === Infinity) {
assertEquals('Infinity', fromJSON.value);
diff --git a/test/mjsunit/mirror-object.js b/test/mjsunit/mirror-object.js
index 1888554..d4d228c 100644
--- a/test/mjsunit/mirror-object.js
+++ b/test/mjsunit/mirror-object.js
@@ -38,7 +38,7 @@
MirrorRefCache.prototype.lookup = function(handle) {
return this.refs_[handle];
-}
+};
function testObjectMirror(obj, cls_name, ctor_name, hasSpecialProperties) {
// Create mirror and JSON representation.
@@ -66,7 +66,7 @@
assertFalse(mirror.hasIndexedInterceptor(), 'No indexed interceptor expected');
var names = mirror.propertyNames();
- var properties = mirror.properties()
+ var properties = mirror.properties();
assertEquals(names.length, properties.length);
for (var i = 0; i < properties.length; i++) {
assertTrue(properties[i] instanceof debug.Mirror, 'Unexpected mirror hierachy');
@@ -130,15 +130,20 @@
assertTrue(typeof(fromJSON.properties[i].attributes) === 'undefined', 'Unexpected serialized attributes');
}
- // Lookup the serialized object from the handle reference.
+ // Lookup the serialized object from the handle reference.
var o = refs.lookup(fromJSON.properties[i].ref);
assertTrue(o != void 0, 'Referenced object is not serialized');
assertEquals(properties[i].value().type(), o.type, 'Unexpected serialized property type for ' + name);
if (properties[i].value().isPrimitive()) {
- // Special check for NaN as NaN == NaN is false.
- if (properties[i].value().isNumber() && isNaN(properties[i].value().value())) {
- assertEquals('NaN', o.value, 'Unexpected serialized property value for ' + name);
+ if (properties[i].value().type() == "null" ||
+ properties[i].value().type() == "undefined") {
+ // Null and undefined has no value property.
+ assertFalse("value" in o, 'Unexpected value property for ' + name);
+ } else if (properties[i].value().type() == "number" &&
+ !isFinite(properties[i].value().value())) {
+ assertEquals(String(properties[i].value().value()), o.value,
+ 'Unexpected serialized property value for ' + name);
} else {
assertEquals(properties[i].value().value(), o.value, 'Unexpected serialized property value for ' + name);
}
diff --git a/test/mjsunit/mjsunit.js b/test/mjsunit/mjsunit.js
index 436bdc8..faa5a43 100644
--- a/test/mjsunit/mjsunit.js
+++ b/test/mjsunit/mjsunit.js
@@ -31,234 +31,325 @@
this.stack = new Error("").stack;
}
-MjsUnitAssertionError.prototype.toString = function () {
- return this.message;
-}
-
/*
* This file is included in all mini jsunit test cases. The test
* framework expects lines that signal failed tests to start with
* the f-word and ignore all other lines.
*/
-function MjsUnitToString(value) {
- switch (typeof value) {
- case "string":
- return JSON.stringify(value);
- case "number":
- if (value === 0 && (1 / value) < 0) return "-0";
- case "boolean":
- case "null":
- case "undefined":
- case "function":
- return String(value);
- case "object":
- if (value === null) return "null";
- var clazz = Object.prototype.toString.call(value);
- clazz = clazz.substring(8, clazz.length - 1);
- switch (clazz) {
+
+MjsUnitAssertionError.prototype.toString = function () {
+ return this.message;
+};
+
+
+// Expected and found values the same objects, or the same primitive
+// values.
+// For known primitive values, please use assertEquals.
+var assertSame;
+
+// Expected and found values are identical primitive values or functions
+// or similarly structured objects (checking internal properties
+// of, e.g., Number and Date objects, the elements of arrays
+// and the properties of non-Array objects).
+var assertEquals;
+
+// The found object is an Array with the same length and elements
+// as the expected object. The expected object doesn't need to be an Array,
+// as long as it's "array-ish".
+var assertArrayEquals;
+
+// The found object must have the same enumerable properties as the
+// expected object. The type of object isn't checked.
+var assertPropertiesEqual;
+
+// Assert that the string conversion of the found value is equal to
+// the expected string. Only kept for backwards compatability, please
+// check the real structure of the found value.
+var assertToStringEquals;
+
+// Checks that the found value is true. Use with boolean expressions
+// for tests that doesn't have their own assertXXX function.
+var assertTrue;
+
+// Checks that the found value is false.
+var assertFalse;
+
+// Checks that the found value is null. Kept for historical compatability,
+// please just use assertEquals(null, expected).
+var assertNull;
+
+// Checks that the found value is *not* null.
+var assertNotNull;
+
+// Assert that the passed function or eval code throws an exception.
+// The optional second argument is an exception constructor that the
+// thrown exception is checked against with "instanceof".
+// The optional third argument is a message type string that is compared
+// to the type property on the thrown exception.
+var assertThrows;
+
+// Assert that the passed function or eval code does not throw an exception.
+var assertDoesNotThrow;
+
+// Asserts that the found value is an instance of the constructor passed
+// as the second argument.
+var assertInstanceof;
+
+// Assert that this code is never executed (i.e., always fails if executed).
+var assertUnreachable;
+
+(function () { // Scope for utility functions.
+
+ function classOf(object) {
+ // Argument must not be null or undefined.
+ var string = Object.prototype.toString.call(object);
+ // String has format [object <ClassName>].
+ return string.substring(8, string.length - 1);
+ }
+
+
+ function PrettyPrint(value) {
+ switch (typeof value) {
+ case "string":
+ return JSON.stringify(value);
+ case "number":
+ if (value === 0 && (1 / value) < 0) return "-0";
+ // FALLTHROUGH.
+ case "boolean":
+ case "undefined":
+ case "function":
+ return String(value);
+ case "object":
+ if (value === null) return "null";
+ var objectClass = classOf(value);
+ switch (objectClass) {
case "Number":
case "String":
case "Boolean":
case "Date":
- return clazz + "(" + MjsUnitToString(value.valueOf()) + ")";
+ return objectClass + "(" + PrettyPrint(value.valueOf()) + ")";
case "RegExp":
return value.toString();
case "Array":
- return "[" + value.map(MjsUnitArrayElementToString).join(",") + "]";
+ return "[" + value.map(PrettyPrintArrayElement).join(",") + "]";
case "Object":
break;
default:
- return clazz + "()";
- }
- // [[Class]] is "Object".
- var constructor = value.constructor.name;
- if (name) return name + "()";
- return "Object()";
- default:
- return "-- unknown value --";
- }
-}
-
-
-function MjsUnitArrayElementToString(value, index, array) {
- if (value === undefined && !(index in array)) return "";
- return MjsUnitToString(value);
-}
-
-
-function fail(expected, found, name_opt) {
- var message = "Fail" + "ure";
- if (name_opt) {
- // Fix this when we ditch the old test runner.
- message += " (" + name_opt + ")";
+ return objectClass + "()";
+ }
+ // [[Class]] is "Object".
+ var name = value.constructor.name;
+ if (name) return name + "()";
+ return "Object()";
+ default:
+ return "-- unknown value --";
+ }
}
- message += ": expected <" + MjsUnitToString(expected) +
- "> found <" + MjsUnitToString(found) + ">";
- throw new MjsUnitAssertionError(message);
-}
+
+ function PrettyPrintArrayElement(value, index, array) {
+ if (value === undefined && !(index in array)) return "";
+ return PrettyPrint(value);
+ }
-function deepObjectEquals(a, b) {
- var aProps = [];
- for (var key in a)
- aProps.push(key);
- var bProps = [];
- for (var key in b)
- bProps.push(key);
- aProps.sort();
- bProps.sort();
- if (!deepEquals(aProps, bProps))
- return false;
- for (var i = 0; i < aProps.length; i++) {
- if (!deepEquals(a[aProps[i]], b[aProps[i]]))
+ function fail(expectedText, found, name_opt) {
+ var message = "Fail" + "ure";
+ if (name_opt) {
+ // Fix this when we ditch the old test runner.
+ message += " (" + name_opt + ")";
+ }
+
+ message += ": expected <" + expectedText +
+ "> found <" + PrettyPrint(found) + ">";
+ throw new MjsUnitAssertionError(message);
+ }
+
+
+ function deepObjectEquals(a, b) {
+ var aProps = Object.keys(a);
+ aProps.sort();
+ var bProps = Object.keys(b);
+ bProps.sort();
+ if (!deepEquals(aProps, bProps)) {
return false;
- }
- return true;
-}
-
-
-function deepEquals(a, b) {
- if (a == b) {
- // Check for -0.
- if (a === 0 && b === 0) return (1 / a) === (1 / b);
- return true;
- }
- if (typeof a == "number" && typeof b == "number" && isNaN(a) && isNaN(b)) {
- return true;
- }
- if (a == null || b == null) return false;
- if (a.constructor === RegExp || b.constructor === RegExp) {
- return (a.constructor === b.constructor) && (a.toString() === b.toString());
- }
- if ((typeof a) !== 'object' || (typeof b) !== 'object' ||
- (a === null) || (b === null))
- return false;
- if (a.constructor === Array) {
- if (b.constructor !== Array)
- return false;
- if (a.length != b.length)
- return false;
- for (var i = 0; i < a.length; i++) {
- if (i in a) {
- if (!(i in b) || !(deepEquals(a[i], b[i])))
- return false;
- } else if (i in b) {
+ }
+ for (var i = 0; i < aProps.length; i++) {
+ if (!deepEquals(a[aProps[i]], b[aProps[i]])) {
return false;
}
}
return true;
- } else {
+ }
+
+
+ function deepEquals(a, b) {
+ if (a === b) {
+ // Check for -0.
+ if (a === 0) return (1 / a) === (1 / b);
+ return true;
+ }
+ if (typeof a != typeof b) return false;
+ if (typeof a == "number") return isNaN(a) && isNaN(b);
+ if (typeof a !== "object" && typeof a !== "function") return false;
+ // Neither a nor b is primitive.
+ var objectClass = classOf(a);
+ if (objectClass !== classOf(b)) return false;
+ if (objectClass === "RegExp") {
+ // For RegExp, just compare pattern and flags using its toString.
+ return (a.toString() === b.toString());
+ }
+ // Functions are only identical to themselves.
+ if (objectClass === "Function") return false;
+ if (objectClass === "Array") {
+ var elementCount = 0;
+ if (a.length != b.length) {
+ return false;
+ }
+ for (var i = 0; i < a.length; i++) {
+ if (!deepEquals(a[i], b[i])) return false;
+ }
+ return true;
+ }
+ if (objectClass == "String" || objectClass == "Number" ||
+ objectClass == "Boolean" || objectClass == "Date") {
+ if (a.valueOf() !== b.valueOf()) return false;
+ }
return deepObjectEquals(a, b);
}
-}
-function assertSame(expected, found, name_opt) {
- if (found !== expected) {
- fail(expected, found, name_opt);
- }
-}
-
-
-function assertEquals(expected, found, name_opt) {
- if (!deepEquals(found, expected)) {
- fail(expected, found, name_opt);
- }
-}
-
-
-function assertArrayEquals(expected, found, name_opt) {
- var start = "";
- if (name_opt) {
- start = name_opt + " - ";
- }
- assertEquals(expected.length, found.length, start + "array length");
- if (expected.length == found.length) {
- for (var i = 0; i < expected.length; ++i) {
- assertEquals(expected[i], found[i], start + "array element at index " + i);
+ assertSame = function assertSame(expected, found, name_opt) {
+ if (found === expected) {
+ if (expected !== 0 || (1 / expected) == (1 / found)) return;
+ } else if (isNaN(expected) && isNaN(found)) {
+ return;
}
- }
-}
+ fail(PrettyPrint(expected), found, name_opt);
+ };
-function assertTrue(value, name_opt) {
- assertEquals(true, value, name_opt);
-}
-
-
-function assertFalse(value, name_opt) {
- assertEquals(false, value, name_opt);
-}
-
-
-function assertNaN(value, name_opt) {
- if (!isNaN(value)) {
- fail("NaN", value, name_opt);
- }
-}
-
-
-function assertNull(value, name_opt) {
- if (value !== null) {
- fail("null", value, name_opt);
- }
-}
-
-
-function assertNotNull(value, name_opt) {
- if (value === null) {
- fail("not null", value, name_opt);
- }
-}
-
-
-function assertThrows(code, type_opt, cause_opt) {
- var threwException = true;
- try {
- if (typeof code == 'function') {
- code();
- } else {
- eval(code);
+ assertEquals = function assertEquals(expected, found, name_opt) {
+ if (!deepEquals(found, expected)) {
+ fail(PrettyPrint(expected), found, name_opt);
}
- threwException = false;
- } catch (e) {
- if (typeof type_opt == 'function')
- assertInstanceof(e, type_opt);
- if (arguments.length >= 3)
- assertEquals(e.type, cause_opt);
- // Do nothing.
- }
- if (!threwException) assertTrue(false, "did not throw exception");
-}
+ };
-function assertInstanceof(obj, type) {
- if (!(obj instanceof type)) {
- assertTrue(false, "Object <" + obj + "> is not an instance of <" + type + ">");
- }
-}
-
-
-function assertDoesNotThrow(code) {
- try {
- if (typeof code == 'function') {
- code();
- } else {
- eval(code);
+ assertArrayEquals = function assertArrayEquals(expected, found, name_opt) {
+ var start = "";
+ if (name_opt) {
+ start = name_opt + " - ";
}
- } catch (e) {
- assertTrue(false, "threw an exception: " + (e.message || e));
- }
-}
+ assertEquals(expected.length, found.length, start + "array length");
+ if (expected.length == found.length) {
+ for (var i = 0; i < expected.length; ++i) {
+ assertEquals(expected[i], found[i],
+ start + "array element at index " + i);
+ }
+ }
+ };
-function assertUnreachable(name_opt) {
- // Fix this when we ditch the old test runner.
- var message = "Fail" + "ure: unreachable";
- if (name_opt) {
- message += " - " + name_opt;
- }
- throw new MjsUnitAssertionError(message);
-}
+ assertPropertiesEqual = function assertPropertiesEqual(expected, found,
+ name_opt) {
+ // Check properties only.
+ if (!deepObjectEquals(expected, found)) {
+ fail(expected, found, name_opt);
+ }
+ };
+
+
+ assertToStringEquals = function assertToStringEquals(expected, found,
+ name_opt) {
+ if (expected != String(found)) {
+ fail(expected, found, name_opt);
+ }
+ };
+
+
+ assertTrue = function assertTrue(value, name_opt) {
+ assertEquals(true, value, name_opt);
+ };
+
+
+ assertFalse = function assertFalse(value, name_opt) {
+ assertEquals(false, value, name_opt);
+ };
+
+
+ assertNull = function assertNull(value, name_opt) {
+ if (value !== null) {
+ fail("null", value, name_opt);
+ }
+ };
+
+
+ assertNotNull = function assertNotNull(value, name_opt) {
+ if (value === null) {
+ fail("not null", value, name_opt);
+ }
+ };
+
+
+ assertThrows = function assertThrows(code, type_opt, cause_opt) {
+ var threwException = true;
+ try {
+ if (typeof code == 'function') {
+ code();
+ } else {
+ eval(code);
+ }
+ threwException = false;
+ } catch (e) {
+ if (typeof type_opt == 'function') {
+ assertInstanceof(e, type_opt);
+ }
+ if (arguments.length >= 3) {
+ assertEquals(e.type, cause_opt);
+ }
+ // Success.
+ return;
+ }
+ throw new MjsUnitAssertionError("Did not throw exception");
+ };
+
+
+ assertInstanceof = function assertInstanceof(obj, type) {
+ if (!(obj instanceof type)) {
+ var actualTypeName = null;
+ var actualConstructor = Object.prototypeOf(obj).constructor;
+ if (typeof actualConstructor == "function") {
+ actualTypeName = actualConstructor.name || String(actualConstructor);
+ }
+ fail("Object <" + PrettyPrint(obj) + "> is not an instance of <" +
+ (type.name || type) + ">" +
+ (actualTypeName ? " but of < " + actualTypeName + ">" : ""));
+ }
+ };
+
+
+ assertDoesNotThrow = function assertDoesNotThrow(code, name_opt) {
+ try {
+ if (typeof code == 'function') {
+ code();
+ } else {
+ eval(code);
+ }
+ } catch (e) {
+ fail("threw an exception: ", e.message || e, name_opt);
+ }
+ };
+
+ assertUnreachable = function assertUnreachable(name_opt) {
+ // Fix this when we ditch the old test runner.
+ var message = "Fail" + "ure: unreachable";
+ if (name_opt) {
+ message += " - " + name_opt;
+ }
+ throw new MjsUnitAssertionError(message);
+ };
+
+})();
+
diff --git a/test/mjsunit/number-string-index-call.js b/test/mjsunit/number-string-index-call.js
index 6f540c0..4391917 100644
--- a/test/mjsunit/number-string-index-call.js
+++ b/test/mjsunit/number-string-index-call.js
@@ -26,7 +26,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --call_regexp
-var callbacks = [ function() {return 'foo'}, "nonobject", /abc/ ];
+var callbacks = [ function() { return 'foo'; }, "nonobject", /abc/ ];
assertEquals('foo', callbacks['0']());
assertThrows("callbacks['1']()");
-assertEquals('abc', callbacks['2']("abcdefg"));
+assertEquals(['abc'], callbacks['2']("abcdefg"));
diff --git a/test/mjsunit/number-tostring.js b/test/mjsunit/number-tostring.js
index 8312080..35e77e2 100644
--- a/test/mjsunit/number-tostring.js
+++ b/test/mjsunit/number-tostring.js
@@ -55,7 +55,7 @@
assertEquals("-90.12", (-90.12).toString());
assertEquals("-0.1", (-0.1).toString());
assertEquals("-0.01", (-0.01).toString());
-assertEquals("-0.0123", (-0.0123).toString())
+assertEquals("-0.0123", (-0.0123).toString());
assertEquals("-111111111111111110000", (-111111111111111111111).toString());
assertEquals("-1.1111111111111111e+21", (-1111111111111111111111).toString());
assertEquals("-1.1111111111111111e+22", (-11111111111111111111111).toString());
@@ -219,7 +219,7 @@
// Test that we round up even when the last digit generated is even.
// dtoa does not do this in its original form.
assertEquals("1", 0.5.toFixed(0), "0.5.toFixed(0)");
-assertEquals("-1", -0.5.toFixed(0), "-0.5.toFixed(0)");
+assertEquals("-1", (-0.5).toFixed(0), "(-0.5).toFixed(0)");
assertEquals("1.3", 1.25.toFixed(1), "1.25.toFixed(1)");
// This is bizare, but Spidermonkey and KJS behave the same.
assertEquals("234.2040", (234.20405).toFixed(4), "234.2040.toFixed(4)");
diff --git a/test/mjsunit/object-seal.js b/test/mjsunit/object-seal.js
index 3ce2367..fb9bb92 100644
--- a/test/mjsunit/object-seal.js
+++ b/test/mjsunit/object-seal.js
@@ -91,7 +91,7 @@
// Since writable is not affected by seal we should still be able to
// update the values.
obj.x = "43";
-assertEquals(43, obj.x);
+assertEquals("43", obj.x);
// Test on accessors.
var obj2 = {};
@@ -142,7 +142,7 @@
Object.seal(arr);
assertTrue(Object.isSealed(arr));
assertFalse(Object.isExtensible(arr));
-// Since the values in the array is still writable this object
+// Since the values in the array is still writable this object
// is not frozen.
assertFalse(Object.isFrozen(arr));
@@ -186,5 +186,5 @@
assertFalse(Object.isSealed(obj4));
// Make sure that Object.seal returns the sealed object.
-var obj4 = {}
-assertTrue(obj4 === Object.seal(obj4))
+var obj4 = {};
+assertTrue(obj4 === Object.seal(obj4));
diff --git a/test/mjsunit/override-eval-with-non-function.js b/test/mjsunit/override-eval-with-non-function.js
index aa93b25..edbcb19 100644
--- a/test/mjsunit/override-eval-with-non-function.js
+++ b/test/mjsunit/override-eval-with-non-function.js
@@ -30,7 +30,7 @@
function test() {
eval = /foo/;
- assertEquals("foo", eval("foobar"));
+ assertEquals(["foo"], eval("foobar"));
}
test();
diff --git a/test/mjsunit/regexp-capture.js b/test/mjsunit/regexp-capture.js
index dc24491..8aae717 100755
--- a/test/mjsunit/regexp-capture.js
+++ b/test/mjsunit/regexp-capture.js
@@ -39,19 +39,20 @@
assertEquals("z", "y".replace(/(x)?\1y/, "z"));
assertEquals("", "y".replace(/(x)?y/, "$1"));
assertEquals("undefined", "y".replace(/(x)?\1y/,
- function($0, $1){
- return String($1);
+ function($0, $1){
+ return String($1);
}));
-assertEquals("undefined", "y".replace(/(x)?y/,
- function($0, $1){
- return String($1);
+assertEquals("undefined", "y".replace(/(x)?y/,
+ function($0, $1){
+ return String($1);
}));
-assertEquals("undefined", "y".replace(/(x)?y/,
- function($0, $1){
- return $1;
+assertEquals("undefined", "y".replace(/(x)?y/,
+ function($0, $1){
+ return $1;
}));
// See https://bugzilla.mozilla.org/show_bug.cgi?id=476146
-assertEquals("bbc,b", /^(b+|a){1,2}?bc/.exec("bbc"));
-assertEquals("bbaa,a,,a", /((\3|b)\2(a)){2,}/.exec("bbaababbabaaaaabbaaaabba"));
+assertEquals(["bbc", "b"], /^(b+|a){1,2}?bc/.exec("bbc"));
+assertEquals(["bbaa", "a", "", "a"],
+ /((\3|b)\2(a)){2,}/.exec("bbaababbabaaaaabbaaaabba"));
diff --git a/test/mjsunit/regexp-compile.js b/test/mjsunit/regexp-compile.js
index 6f8e751..6a24325 100644
--- a/test/mjsunit/regexp-compile.js
+++ b/test/mjsunit/regexp-compile.js
@@ -27,16 +27,16 @@
// Test that we don't cache the result of a regexp match across a
// compile event.
-var re = /x/;
+var re = /x/;
assertEquals("a.yb", "axyb".replace(re, "."));
-re.compile("y")
+re.compile("y");
assertEquals("ax.b", "axyb".replace(re, "."));
re.compile("(x)");
-assertEquals("x,x", re.exec("axyb"));
+assertEquals(["x", "x"], re.exec("axyb"));
re.compile("(y)");
-assertEquals("y,y", re.exec("axyb"));
+assertEquals(["y", "y"], re.exec("axyb"));
diff --git a/test/mjsunit/regexp-static.js b/test/mjsunit/regexp-static.js
index 9e73f3d..0f84968 100644
--- a/test/mjsunit/regexp-static.js
+++ b/test/mjsunit/regexp-static.js
@@ -134,7 +134,8 @@
assertEquals('abcd', 'abcd'.replace(re, f));
// lastParen where the last parenthesis didn't match.
-assertEquals("foo,", /foo(?:a(x))?/.exec("foobx"), "lastParen setup");
+assertEquals(["foo",undefined], /foo(?:a(x))?/.exec("foobx"),
+ "lastParen setup");
assertEquals("", RegExp.lastParen, "lastParen");
// The same test for $1 to $9.
diff --git a/test/mjsunit/regexp-string-methods.js b/test/mjsunit/regexp-string-methods.js
index ef3bf6e..56604a6 100644
--- a/test/mjsunit/regexp-string-methods.js
+++ b/test/mjsunit/regexp-string-methods.js
@@ -28,18 +28,18 @@
// Regexp shouldn't use String.prototype.slice()
var s = new String("foo");
assertEquals("f", s.slice(0,1));
-String.prototype.slice = function() { return "x"; }
+String.prototype.slice = function() { return "x"; };
assertEquals("x", s.slice(0,1));
-assertEquals("g", /g/.exec("gg"));
+assertEquals(["g"], /g/.exec("gg"));
// Regexp shouldn't use String.prototype.charAt()
var f1 = new RegExp("f", "i");
-assertEquals("F", f1.exec("F"));
+assertEquals(["F"], f1.exec("F"));
assertEquals("f", "foo".charAt(0));
String.prototype.charAt = function(idx) { return 'g'; };
assertEquals("g", "foo".charAt(0));
var f2 = new RegExp("[g]", "i");
-assertEquals("G", f2.exec("G"));
+assertEquals(["G"], f2.exec("G"));
assertTrue(f2.ignoreCase);
// On the other hand test is defined in a semi-coherent way as a call to exec.
@@ -47,5 +47,5 @@
// We match other browsers in using the original value of RegExp.prototype.exec.
// I.e., RegExp.prototype.test shouldn't use the current value of
// RegExp.prototype.exec.
-RegExp.prototype.exec = function(string) { return 'x'; }
+RegExp.prototype.exec = function(string) { return 'x'; };
assertFalse(/f/.test('x'));
diff --git a/test/mjsunit/regexp.js b/test/mjsunit/regexp.js
index 24e1b21..3c4f883 100644
--- a/test/mjsunit/regexp.js
+++ b/test/mjsunit/regexp.js
@@ -333,9 +333,9 @@
// Check decimal escapes doesn't overflow.
// (Note: \214 is interpreted as octal).
-assertEquals(/\2147483648/.exec("\x8c7483648"),
- ["\x8c7483648"],
- "Overflow decimal escape");
+assertArrayEquals(["\x8c7483648"],
+ /\2147483648/.exec("\x8c7483648"),
+ "Overflow decimal escape");
// Check numbers in quantifiers doesn't overflow and doesn't throw on
@@ -435,8 +435,8 @@
re.lastIndex = 42;
re.someOtherProperty = 42;
re.someDeletableProperty = 42;
-re[37] = 37;
-re[42] = 42;
+re[37] = 37;
+re[42] = 42;
re.compile("ra+", "i");
assertEquals("ra+", re.source);
@@ -466,7 +466,7 @@
assertEquals(37, re[42]);
// Test boundary-checks.
-function assertRegExpTest(re, input, test) {
+function assertRegExpTest(re, input, test) {
assertEquals(test, re.test(input), "test:" + re + ":" + input);
}
@@ -525,7 +525,7 @@
assertEquals(1, res.index);
assertEquals("axyzb", res.input);
assertEquals(undefined, res.foobar);
-
+
res.foobar = "Arglebargle";
res[3] = "Glopglyf";
assertEquals("Arglebargle", res.foobar);
@@ -534,18 +534,18 @@
// Test that we perform the spec required conversions in the correct order.
var log;
var string = "the string";
-var fakeLastIndex = {
- valueOf: function() {
+var fakeLastIndex = {
+ valueOf: function() {
log.push("li");
return 0;
- }
+ }
};
-var fakeString = {
+var fakeString = {
toString: function() {
log.push("ts");
return string;
- },
- length: 0
+ },
+ length: 0
};
var re = /str/;
diff --git a/test/mjsunit/regress/regress-1079.js b/test/mjsunit/regress/regress-1079.js
index f3f3ce5..208dc5b 100644
--- a/test/mjsunit/regress/regress-1079.js
+++ b/test/mjsunit/regress/regress-1079.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Getting the arguments property of an optimized function should not crash,
// even if called through our optimized version of Function.prototype.apply.
@@ -39,7 +41,8 @@
}
}
-for (var i = 0; i < 100000; ++i) {
+for (var i = 0; i < 5; ++i) {
assertEquals(3, optimized(1, 2, 3).length);
}
-
+%OptimizeFunctionOnNextCall(optimized);
+assertEquals(3, optimized(1, 2, 3).length);
diff --git a/test/mjsunit/regress/regress-1106.js b/test/mjsunit/regress/regress-1106.js
index 382fd1b..e462d5d 100644
--- a/test/mjsunit/regress/regress-1106.js
+++ b/test/mjsunit/regress/regress-1106.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Test for issue 1106, where the optimizing compiler broke when accessing
// a property lying on a prototype of the global object, and that prototype
// object was in dictionary mode.
@@ -37,14 +39,18 @@
function f() { return foo; }
-for (i=0 ; i < 100000; ++i) {
+for (i=0 ; i < 5; ++i) {
assertEquals(5, f());
}
+%OptimizeFunctionOnNextCall(f);
+assertEquals(5, f());
// Test calls on functions defined in the prototype of the global object.
x.gee = function() { return 42; }
function g() { return gee(); }
-for (i=0 ; i < 100000; ++i) {
+for (i=0 ; i < 5; ++i) {
assertEquals(42, g());
}
+%OptimizeFunctionOnNextCall(g);
+assertEquals(42, g());
diff --git a/test/mjsunit/regress/regress-1166.js b/test/mjsunit/regress/regress-1166.js
index d75d397..8278aba 100644
--- a/test/mjsunit/regress/regress-1166.js
+++ b/test/mjsunit/regress/regress-1166.js
@@ -25,11 +25,16 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Deoptimization after a short-circuit logical operation in an effect
// context should not see the value of the expression.
function observe(x, y) { return x; }
function test(x) { return observe(1, ((false || false), x + 1)); }
-for (var i = 0; i < 10000000; ++i) test(0);
+for (var i = 0; i < 5; ++i) test(0);
+%OptimizeFunctionOnNextCall(test);
+test(0);
+
test("a");
diff --git a/test/mjsunit/regress/regress-1167.js b/test/mjsunit/regress/regress-1167.js
index 8437d83..2206f3d 100644
--- a/test/mjsunit/regress/regress-1167.js
+++ b/test/mjsunit/regress/regress-1167.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Deoptimization after a logical not in an effect context should not see a
// value for the logical not expression.
function test0(n) {
@@ -68,5 +70,7 @@
x + 1));
}
-for (var i = 0; i < 1000000; ++i) test2(0);
+for (var i = 0; i < 5; ++i) test2(0);
+%OptimizeFunctionOnNextCall(test2);
+test2(0);
test2(test2);
diff --git a/test/mjsunit/regress/regress-1199637.js b/test/mjsunit/regress/regress-1199637.js
index d9116c1..9c560a9 100644
--- a/test/mjsunit/regress/regress-1199637.js
+++ b/test/mjsunit/regress/regress-1199637.js
@@ -34,43 +34,43 @@
const READ_ONLY = 1;
// Use DeclareGlobal...
-%SetProperty(this.__proto__, "a", "1234", NONE);
+%SetProperty(this.__proto__, "a", 1234, NONE);
assertEquals(1234, a);
eval("var a = 5678;");
assertEquals(5678, a);
-%SetProperty(this.__proto__, "b", "1234", NONE);
+%SetProperty(this.__proto__, "b", 1234, NONE);
assertEquals(1234, b);
eval("const b = 5678;");
assertEquals(5678, b);
-%SetProperty(this.__proto__, "c", "1234", READ_ONLY);
+%SetProperty(this.__proto__, "c", 1234, READ_ONLY);
assertEquals(1234, c);
eval("var c = 5678;");
assertEquals(5678, c);
-%SetProperty(this.__proto__, "d", "1234", READ_ONLY);
+%SetProperty(this.__proto__, "d", 1234, READ_ONLY);
assertEquals(1234, d);
eval("const d = 5678;");
assertEquals(5678, d);
// Use DeclareContextSlot...
-%SetProperty(this.__proto__, "x", "1234", NONE);
+%SetProperty(this.__proto__, "x", 1234, NONE);
assertEquals(1234, x);
eval("with({}) { var x = 5678; }");
assertEquals(5678, x);
-%SetProperty(this.__proto__, "y", "1234", NONE);
+%SetProperty(this.__proto__, "y", 1234, NONE);
assertEquals(1234, y);
eval("with({}) { const y = 5678; }");
assertEquals(5678, y);
-%SetProperty(this.__proto__, "z", "1234", READ_ONLY);
+%SetProperty(this.__proto__, "z", 1234, READ_ONLY);
assertEquals(1234, z);
eval("with({}) { var z = 5678; }");
assertEquals(5678, z);
-%SetProperty(this.__proto__, "w", "1234", READ_ONLY);
+%SetProperty(this.__proto__, "w", 1234, READ_ONLY);
assertEquals(1234, w);
eval("with({}) { const w = 5678; }");
assertEquals(5678, w);
diff --git a/test/mjsunit/regress/regress-1210.js b/test/mjsunit/regress/regress-1210.js
index 9c708a5..7d4735a 100644
--- a/test/mjsunit/regress/regress-1210.js
+++ b/test/mjsunit/regress/regress-1210.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Deoptimization of the key expression in an arguments access should see
// the arguments object as the value of the receiver.
@@ -43,6 +45,8 @@
// Run enough to optimize assuming global 'a' is a smi.
for (var i = 0; i < 1000000; ++i) test(0);
+%OptimizeFunctionOnNextCall(test);
+test(0);
a = "hello";
test(0);
diff --git a/test/mjsunit/regress/regress-1229.js b/test/mjsunit/regress/regress-1229.js
new file mode 100644
index 0000000..e16d278
--- /dev/null
+++ b/test/mjsunit/regress/regress-1229.js
@@ -0,0 +1,87 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --allow-natives-syntax
+
+// Check that %NewObjectFromBound works correctly when called from optimized
+// frame.
+function foo(x, y, z) {
+ assertEquals(1, x);
+ assertEquals(2, y);
+ assertEquals(3, z);
+}
+
+var bound_arg = [1];
+
+function f(y, z) {
+ return %NewObjectFromBound(foo, bound_arg);
+}
+
+// Check that %NewObjectFromBound looks at correct frame for inlined function.
+function g(z, y) {
+ return f(y, z); /* f should be inlined into g, note rotated arguments */
+}
+
+// Check that %NewObjectFromBound looks at correct frame for inlined function.
+function ff(x) { }
+function h(z2, y2) {
+ var local_z = z2 >> 1;
+ ff(local_z);
+ var local_y = y2 >> 1;
+ ff(local_y);
+ return f(local_y, local_z); /* f should be inlined into h */
+}
+
+for (var i = 0; i < 5; i++) f(2, 3);
+%OptimizeFunctionOnNextCall(f);
+f(2, 3);
+
+for (var i = 0; i < 5; i++) g(3, 2);
+%OptimizeFunctionOnNextCall(g);
+g(3, 2);
+
+for (var i = 0; i < 5; i++) h(6, 4);
+%OptimizeFunctionOnNextCall(h);
+h(6, 4);
+
+// Check that %_IsConstructCall returns correct value when inlined
+var NON_CONSTRUCT_MARKER = {};
+var CONSTRUCT_MARKER = {};
+function baz() {
+ return (!%_IsConstructCall()) ? NON_CONSTRUCT_MARKER : CONSTRUCT_MARKER;
+}
+
+function bar(x, y, z) {
+ var non_construct = baz(); /* baz should be inlined */
+ assertEquals(non_construct, NON_CONSTRUCT_MARKER);
+ var construct = new baz();
+ assertEquals(construct, CONSTRUCT_MARKER);
+}
+
+for (var i = 0; i < 5; i++) new bar(1, 2, 3);
+%OptimizeFunctionOnNextCall(bar);
+bar(1, 2, 3);
diff --git a/test/mjsunit/regress/regress-1237.js b/test/mjsunit/regress/regress-1237.js
index f97f978..111df80 100644
--- a/test/mjsunit/regress/regress-1237.js
+++ b/test/mjsunit/regress/regress-1237.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Deoptimization after a conditional expression in an effect context should
// not see the value of the expression.
function observe(x, y) { return x; }
@@ -32,5 +34,8 @@
return observe(1, ((x? observe(observe.prototype.x): 'c'), x + 1));
}
-for (var i = 0; i < 10000000; ++i) test(0);
+for (var i = 0; i < 5; ++i) test(0);
+%OptimizeFunctionOnNextCall(test);
+test(0);
+
test("a");
diff --git a/src/virtual-frame-inl.h b/test/mjsunit/regress/regress-1309.js
similarity index 81%
copy from src/virtual-frame-inl.h
copy to test/mjsunit/regress/regress-1309.js
index c9f4aac..122e591 100644
--- a/src/virtual-frame-inl.h
+++ b/test/mjsunit/regress/regress-1309.js
@@ -1,4 +1,4 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -25,15 +25,6 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#ifndef V8_VIRTUAL_FRAME_INL_H_
-#define V8_VIRTUAL_FRAME_INL_H_
-#include "virtual-frame.h"
-
-#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64
-#include "virtual-frame-heavy-inl.h"
-#else
-#include "virtual-frame-light-inl.h"
-#endif
-
-#endif // V8_VIRTUAL_FRAME_INL_H_
+var o = Object.preventExtensions({});
+assertThrows("o.__proto__ = {}");
diff --git a/src/arm/register-allocator-arm.h b/test/mjsunit/regress/regress-1323.js
similarity index 71%
rename from src/arm/register-allocator-arm.h
rename to test/mjsunit/regress/regress-1323.js
index fdbc88f..552a48d 100644
--- a/src/arm/register-allocator-arm.h
+++ b/test/mjsunit/regress/regress-1323.js
@@ -1,4 +1,4 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -25,20 +25,26 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#ifndef V8_ARM_REGISTER_ALLOCATOR_ARM_H_
-#define V8_ARM_REGISTER_ALLOCATOR_ARM_H_
+// Flags: --allow-natives-syntax
-namespace v8 {
-namespace internal {
+// Regression test for load/store operating with wrong number of bits.
+function get(a, index) {
+ return a[index];
+}
-class RegisterAllocatorConstants : public AllStatic {
- public:
- // No registers are currently managed by the register allocator on ARM.
- static const int kNumRegisters = 0;
- static const int kInvalidRegister = -1;
-};
+var a = new Float32Array(2);
+a[0] = 2.5;
+a[1] = 3.5;
+for (var i = 0; i < 5; i++) get(a, 0);
+%OptimizeFunctionOnNextCall(get);
+assertEquals(2.5, get(a, 0));
+assertEquals(3.5, get(a, 1));
-
-} } // namespace v8::internal
-
-#endif // V8_ARM_REGISTER_ALLOCATOR_ARM_H_
+function set(a, index, value) {
+ a[index] = value;
+}
+for (var i = 0; i < 5; i++) set(a, 0, 4.5);
+%OptimizeFunctionOnNextCall(set);
+set(a, 0, 4.5);
+assertEquals(4.5, a[0]);
+assertEquals(3.5, a[1]);
diff --git a/test/mjsunit/regress/regress-176.js b/test/mjsunit/regress/regress-176.js
index b204812..ef0c4f1 100644
--- a/test/mjsunit/regress/regress-176.js
+++ b/test/mjsunit/regress/regress-176.js
@@ -27,24 +27,24 @@
// See http://code.google.com/p/v8/issues/detail?id=176
-assertEquals("f,",
- "foo".match(/(?:(?=(f)o))?f/).toString(),
- "zero length match in (?:) with capture in lookahead");
-assertEquals("f,",
- "foo".match(/(?=(f)o)?f/).toString(),
- "zero length match in (?=) with capture in lookahead");
-assertEquals("fo,f",
- "foo".match(/(?:(?=(f)o)f)?o/),
- "non-zero length match with capture in lookahead");
-assertEquals("fo,f",
- "foo".match(/(?:(?=(f)o)f?)?o/),
- "non-zero length match with greedy ? in (?:)");
-assertEquals("fo,f",
- "foo".match(/(?:(?=(f)o)f??)?o/),
- "non-zero length match with non-greedy ? in (?:), o forces backtrack");
-assertEquals("fo,f",
- "foo".match(/(?:(?=(f)o)f??)?./),
- "non-zero length match with non-greedy ? in (?:), zero length match causes backtrack");
-assertEquals("f,",
- "foo".match(/(?:(?=(f)o)fx)?./),
- "x causes backtrack inside (?:)");
+assertArrayEquals(["f", undefined],
+ "foo".match(/(?:(?=(f)o))?f/),
+ "zero length match in (?:) with capture in lookahead");
+assertArrayEquals(["f", undefined],
+ "foo".match(/(?=(f)o)?f/),
+ "zero length match in (?=) with capture in lookahead");
+assertArrayEquals(["fo", "f"],
+ "foo".match(/(?:(?=(f)o)f)?o/),
+ "non-zero length match with capture in lookahead");
+assertArrayEquals(["fo", "f"],
+ "foo".match(/(?:(?=(f)o)f?)?o/),
+ "non-zero length match with greedy ? in (?:)");
+assertArrayEquals(["fo", "f"],
+ "foo".match(/(?:(?=(f)o)f??)?o/),
+ "non-zero length match with non-greedy ? in (?:), o forces backtrack");
+assertArrayEquals(["fo", "f"],
+ "foo".match(/(?:(?=(f)o)f??)?./),
+ "non-zero length match with non-greedy ? in (?:), zero length match causes backtrack");
+assertArrayEquals(["f", undefined],
+ "foo".match(/(?:(?=(f)o)fx)?./),
+ "x causes backtrack inside (?:)");
diff --git a/test/mjsunit/regress/regress-187.js b/test/mjsunit/regress/regress-187.js
index 44d8d7a..2f8b0a1 100644
--- a/test/mjsunit/regress/regress-187.js
+++ b/test/mjsunit/regress/regress-187.js
@@ -27,4 +27,4 @@
// See http://code.google.com/p/v8/issues/detail?id=187
-assertEquals("f,", "foo".match(/(?:(?=(f)o)fx|)./));
+assertEquals(["f", undefined], "foo".match(/(?:(?=(f)o)fx|)./));
diff --git a/test/mjsunit/regress/regress-3218915.js b/test/mjsunit/regress/regress-3218915.js
index 5fcbcec..4b08a6e 100644
--- a/test/mjsunit/regress/regress-3218915.js
+++ b/test/mjsunit/regress/regress-3218915.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
// Checks that comma expression in conditional context is processed correctly.
function withCommaExpressionInConditional(x) {
@@ -36,7 +38,9 @@
return (y = x + 1, y > 1) ? 'medium' : 'small';
}
-for (var i = 0; i < 10000; i++) {
+for (var i = 0; i < 5; i++) {
withCommaExpressionInConditional(i);
}
+%OptimizeFunctionOnNextCall(withCommaExpressionInConditional);
+withCommaExpressionInConditional(i);
withCommaExpressionInConditional("1")
diff --git a/test/mjsunit/regress/regress-399.js b/test/mjsunit/regress/regress-399.js
index 2ee998b..6c8eab5 100644
--- a/test/mjsunit/regress/regress-399.js
+++ b/test/mjsunit/regress/regress-399.js
@@ -28,5 +28,5 @@
// See http://code.google.com/p/v8/issues/detail?id=399
var date = new Date(1.009804e12);
-var year = String(date).match(/.*(200\d)/)[1];
+var year = Number(String(date).match(/.*(200\d)/)[1]);
assertEquals(year, date.getFullYear());
diff --git a/src/virtual-frame-inl.h b/test/mjsunit/regress/regress-78270.js
similarity index 81%
copy from src/virtual-frame-inl.h
copy to test/mjsunit/regress/regress-78270.js
index c9f4aac..b9ce286 100644
--- a/src/virtual-frame-inl.h
+++ b/test/mjsunit/regress/regress-78270.js
@@ -1,4 +1,4 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -25,15 +25,13 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#ifndef V8_VIRTUAL_FRAME_INL_H_
-#define V8_VIRTUAL_FRAME_INL_H_
-
-#include "virtual-frame.h"
-
-#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64
-#include "virtual-frame-heavy-inl.h"
-#else
-#include "virtual-frame-light-inl.h"
-#endif
-
-#endif // V8_VIRTUAL_FRAME_INL_H_
+for (var i = 0; i < 10000; i++) {
+ try {
+ var object = { };
+ function g(f0) {
+ var f0 = (object instanceof encodeURI)('foo');
+ }
+ g(75);
+ } catch (g) {
+ }
+}
diff --git a/test/mjsunit/regress/regress-962.js b/test/mjsunit/regress/regress-962.js
index f9f46e1..85ada0c 100644
--- a/test/mjsunit/regress/regress-962.js
+++ b/test/mjsunit/regress/regress-962.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
function L(scope) { this.s = new Object(); }
L.prototype.c = function() { return true; }
@@ -50,4 +52,6 @@
var ctx = new F;
-for (var i = 0; i < 10000; i++) ctx.foo();
+for (var i = 0; i < 5; i++) ctx.foo();
+%OptimizeFunctionOnNextCall(F.prototype.foo);
+ctx.foo();
diff --git a/test/mjsunit/strict-mode-opt.js b/test/mjsunit/strict-mode-opt.js
new file mode 100644
index 0000000..e2eae33
--- /dev/null
+++ b/test/mjsunit/strict-mode-opt.js
@@ -0,0 +1,98 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+var global = 0;
+var MAX = 1000000;
+
+// Attempt to inline strcit in non-strict.
+
+function strictToBeInlined(n) {
+ "use strict";
+ global = "strict";
+ if (n == MAX) { undefined_variable_strict = "value"; }
+}
+
+function nonstrictCallStrict(n) {
+ strictToBeInlined(n);
+}
+
+(function testInlineStrictInNonStrict() {
+ for (var i = 0; i <= MAX; i ++) {
+ try {
+ nonstrictCallStrict(i);
+ } catch (e) {
+ assertInstanceof(e, ReferenceError);
+ assertEquals(MAX, i);
+ return;
+ }
+ }
+ fail("ReferenceError after MAX iterations", "no exception");
+})();
+
+// Attempt to inline non-strict in strict.
+
+function nonstrictToBeInlined(n) {
+ global = "nonstrict";
+ if (n == MAX) { undefined_variable_nonstrict = "The nonstrict value"; }
+}
+
+function strictCallNonStrict(n) {
+ "use strict";
+ nonstrictToBeInlined(n);
+}
+
+(function testInlineNonStrictInStrict() {
+ for (var i = 0; i <= MAX; i ++) {
+ try {
+ strictCallNonStrict(i);
+ } catch (e) {
+ fail("no exception", "exception");
+ }
+ }
+ assertEquals("The nonstrict value", undefined_variable_nonstrict);
+})();
+
+// Optimize strict function.
+
+function strictAssignToUndefined(n) {
+ "use strict";
+ global = "strict";
+ if (n == MAX) { undefined_variable_strict_2 = "value"; }
+}
+
+(function testOptimizeStrictAssignToUndefined() {
+ for (var i = 0; i <= MAX; i ++) {
+ try {
+ strictAssignToUndefined(i);
+ } catch (e) {
+ assertInstanceof(e, ReferenceError);
+ assertEquals(MAX, i);
+ return;
+ }
+ }
+ fail("ReferenceError after MAX iterations", "no exception");
+})();
diff --git a/test/mjsunit/strict-mode.js b/test/mjsunit/strict-mode.js
index 84ccb30..beca582 100644
--- a/test/mjsunit/strict-mode.js
+++ b/test/mjsunit/strict-mode.js
@@ -387,10 +387,9 @@
testFutureReservedWord(future_reserved_words[i]);
}
-function testAssignToUndefined(should_throw) {
- "use strict";
+function testAssignToUndefined(test, should_throw) {
try {
- possibly_undefined_variable_for_strict_mode_test = "should throw?";
+ test();
} catch (e) {
assertTrue(should_throw, "strict mode");
assertInstanceof(e, ReferenceError, "strict mode");
@@ -399,33 +398,78 @@
assertFalse(should_throw, "strict mode");
}
-testAssignToUndefined(true);
-testAssignToUndefined(true);
-testAssignToUndefined(true);
-
-possibly_undefined_variable_for_strict_mode_test = "value";
-
-testAssignToUndefined(false);
-testAssignToUndefined(false);
-testAssignToUndefined(false);
-
-delete possibly_undefined_variable_for_strict_mode_test;
-
-testAssignToUndefined(true);
-testAssignToUndefined(true);
-testAssignToUndefined(true);
-
function repeat(n, f) {
- for (var i = 0; i < n; i ++) { f(); }
+ for (var i = 0; i < n; i ++) { f(); }
}
-repeat(10, function() { testAssignToUndefined(true); });
+function assignToUndefined() {
+ "use strict";
+ possibly_undefined_variable_for_strict_mode_test = "should throw?";
+}
+
+testAssignToUndefined(assignToUndefined, true);
+testAssignToUndefined(assignToUndefined, true);
+testAssignToUndefined(assignToUndefined, true);
+
possibly_undefined_variable_for_strict_mode_test = "value";
-repeat(10, function() { testAssignToUndefined(false); });
+
+testAssignToUndefined(assignToUndefined, false);
+testAssignToUndefined(assignToUndefined, false);
+testAssignToUndefined(assignToUndefined, false);
+
delete possibly_undefined_variable_for_strict_mode_test;
-repeat(10, function() { testAssignToUndefined(true); });
+
+testAssignToUndefined(assignToUndefined, true);
+testAssignToUndefined(assignToUndefined, true);
+testAssignToUndefined(assignToUndefined, true);
+
+repeat(10, function() { testAssignToUndefined(assignToUndefined, true); });
+possibly_undefined_variable_for_strict_mode_test = "value";
+repeat(10, function() { testAssignToUndefined(assignToUndefined, false); });
+delete possibly_undefined_variable_for_strict_mode_test;
+repeat(10, function() { testAssignToUndefined(assignToUndefined, true); });
possibly_undefined_variable_for_strict_mode_test = undefined;
-repeat(10, function() { testAssignToUndefined(false); });
+repeat(10, function() { testAssignToUndefined(assignToUndefined, false); });
+
+function assignToUndefinedWithEval() {
+ "use strict";
+ possibly_undefined_variable_for_strict_mode_test_with_eval = "should throw?";
+ eval("");
+}
+
+testAssignToUndefined(assignToUndefinedWithEval, true);
+testAssignToUndefined(assignToUndefinedWithEval, true);
+testAssignToUndefined(assignToUndefinedWithEval, true);
+
+possibly_undefined_variable_for_strict_mode_test_with_eval = "value";
+
+testAssignToUndefined(assignToUndefinedWithEval, false);
+testAssignToUndefined(assignToUndefinedWithEval, false);
+testAssignToUndefined(assignToUndefinedWithEval, false);
+
+delete possibly_undefined_variable_for_strict_mode_test_with_eval;
+
+testAssignToUndefined(assignToUndefinedWithEval, true);
+testAssignToUndefined(assignToUndefinedWithEval, true);
+testAssignToUndefined(assignToUndefinedWithEval, true);
+
+repeat(10, function() {
+ testAssignToUndefined(assignToUndefinedWithEval, true);
+ });
+possibly_undefined_variable_for_strict_mode_test_with_eval = "value";
+repeat(10, function() {
+ testAssignToUndefined(assignToUndefinedWithEval, false);
+ });
+delete possibly_undefined_variable_for_strict_mode_test_with_eval;
+repeat(10, function() {
+ testAssignToUndefined(assignToUndefinedWithEval, true);
+ });
+possibly_undefined_variable_for_strict_mode_test_with_eval = undefined;
+repeat(10, function() {
+ testAssignToUndefined(assignToUndefinedWithEval, false);
+ });
+
+
(function testDeleteNonConfigurable() {
function delete_property(o) {
diff --git a/test/mjsunit/string-charcodeat.js b/test/mjsunit/string-charcodeat.js
index f18d0a5..8be6a09 100644
--- a/test/mjsunit/string-charcodeat.js
+++ b/test/mjsunit/string-charcodeat.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
/**
* @fileoverview Check all sorts of borderline cases for charCodeAt.
*/
@@ -159,9 +161,11 @@
assertTrue(isNaN(str.charCodeAt(0x7fffffff)));
}
-for (var i = 0; i < 100000; i++) {
+for (var i = 0; i < 5; i++) {
ConsNotSmiIndex();
}
+%OptimizeFunctionOnNextCall(ConsNotSmiIndex);
+ConsNotSmiIndex();
for (var i = 0; i != 10; i++) {
@@ -222,6 +226,8 @@
assertEquals(99, "c".x(0));
}
-for (var i = 0; i < 10000; i++) {
+for (var i = 0; i < 5; i++) {
directlyOnPrototype();
}
+%OptimizeFunctionOnNextCall(directlyOnPrototype);
+directlyOnPrototype();
diff --git a/test/mjsunit/string-fromcharcode.js b/test/mjsunit/string-fromcharcode.js
index 7a2db5f..1986dda 100644
--- a/test/mjsunit/string-fromcharcode.js
+++ b/test/mjsunit/string-fromcharcode.js
@@ -65,8 +65,10 @@
assertEquals(" ", fcc(0x20 + 0.5, 0x20));
var receiver = (num < 5) ? String : (num < 9) ? "dummy" : 42;
- fcc2 = (num < 5) ? fcc : (num < 9) ? constFun("dummy") : constFun(42);
- var expected = (num < 5) ? " " : (num < 9) ? "dummy" : 42;
+ fcc2 = (num < 5) ? fcc
+ : (num < 9) ? constFun(Object("dummy"))
+ : constFun(Object(42));
+ var expected = (num < 5) ? " " : (num < 9) ? Object("dummy") : Object(42);
assertEquals(expected, receiver.fromCharCode(0x20));
assertEquals(expected, receiver.fromCharCode(0x20 - 0x10000));
assertEquals(expected, receiver.fromCharCode(0x20 + 0.5));
diff --git a/test/mjsunit/string-index.js b/test/mjsunit/string-index.js
index 1d6476e..315708c 100644
--- a/test/mjsunit/string-index.js
+++ b/test/mjsunit/string-index.js
@@ -61,7 +61,7 @@
assertEquals("undefined", typeof(foo[-2]), "negative index");
var S = new String("foo");
-assertEquals("foo", S);
+assertEquals(Object("foo"), S);
assertEquals("f", S[0], "string object");
assertEquals("f", S["0"], "string object");
S[0] = 'bente';
@@ -131,7 +131,7 @@
assertEquals(false, "3" in S);
var N = new Number(43);
-assertEquals(43, N);
+assertEquals(Object(43), N);
N[-2] = "Alpha";
assertEquals("Alpha", N[-2]);
N[0] = "Zappa";
diff --git a/test/mjsunit/sum-0-plus-undefined-is-NaN.js b/test/mjsunit/sum-0-plus-undefined-is-NaN.js
index fb98d0c..5d662d1 100644
--- a/test/mjsunit/sum-0-plus-undefined-is-NaN.js
+++ b/test/mjsunit/sum-0-plus-undefined-is-NaN.js
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
/**
* @fileoverview Test addition of 0 and undefined.
*/
@@ -32,9 +34,11 @@
function sum(a, b) { return a + b; }
function test(x, y, expectNaN) {
- for (var i = 0; i < 1000; i++) {
+ for (var i = 0; i < 5; i++) {
assertEquals(expectNaN, isNaN(sum(x, y)));
}
+ %OptimizeFunctionOnNextCall(sum);
+ assertEquals(expectNaN, isNaN(sum(x, y)));
}
test(0, 1, false);
diff --git a/test/mjsunit/testcfg.py b/test/mjsunit/testcfg.py
index 3dd6581..f7b6d4d 100644
--- a/test/mjsunit/testcfg.py
+++ b/test/mjsunit/testcfg.py
@@ -107,7 +107,7 @@
return self_script
def AfterRun(self, result):
- if self.self_script and (not result.HasPreciousOutput()):
+ if self.self_script and (not result or (not result.HasPreciousOutput())):
test.CheckedUnlink(self.self_script)
class MjsunitTestConfiguration(test.TestConfiguration):
diff --git a/test/mjsunit/third_party/object-keys.js b/test/mjsunit/third_party/object-keys.js
index 999ce70..d09265c 100644
--- a/test/mjsunit/third_party/object-keys.js
+++ b/test/mjsunit/third_party/object-keys.js
@@ -54,7 +54,7 @@
assertEquals('string', typeof(Object.keys([1])[0]));
function argsTest(a, b, c) {
- assertEquals([0, 1, 2], Object.keys(arguments));
+ assertEquals(['0', '1', '2'], Object.keys(arguments));
}
argsTest(1, 2, 3);
diff --git a/test/mjsunit/third_party/regexp-pcre.js b/test/mjsunit/third_party/regexp-pcre.js
index dcb1b32..c049fb4 100644
--- a/test/mjsunit/third_party/regexp-pcre.js
+++ b/test/mjsunit/third_party/regexp-pcre.js
@@ -1643,135 +1643,135 @@
res[1564] = /[^\xaa]/m;
res[1565] = /[^\xaa]/m;
res[1566] = / End of testinput10 /;
-assertEquals("abc", res[1].exec("abc"), 0);
-assertEquals("abc", res[1].exec("defabc"), 1);
-assertEquals("abc", res[1].exec("Aabc"), 2);
-assertEquals(null, res[1].exec("*** Failers", 3));
-assertEquals("abc", res[1].exec("Adefabc"), 4);
-assertEquals("ABC", res[1].exec("ABC"), 5);
-assertEquals("abc", res[2].exec("abc"), 6);
-assertEquals(null, res[2].exec("Aabc", 7));
-assertEquals(null, res[2].exec("*** Failers", 8));
-assertEquals(null, res[2].exec("defabc", 9));
-assertEquals(null, res[2].exec("Adefabc", 10));
-assertEquals("abc", res[7].exec("abc"), 11);
-assertEquals(null, res[7].exec("*** Failers", 12));
-assertEquals(null, res[7].exec("def\nabc", 13));
+assertToStringEquals("abc", res[1].exec("abc"), 0);
+assertToStringEquals("abc", res[1].exec("defabc"), 1);
+assertToStringEquals("abc", res[1].exec("Aabc"), 2);
+assertNull(res[1].exec("*** Failers", 3));
+assertToStringEquals("abc", res[1].exec("Adefabc"), 4);
+assertToStringEquals("ABC", res[1].exec("ABC"), 5);
+assertToStringEquals("abc", res[2].exec("abc"), 6);
+assertNull(res[2].exec("Aabc", 7));
+assertNull(res[2].exec("*** Failers", 8));
+assertNull(res[2].exec("defabc", 9));
+assertNull(res[2].exec("Adefabc", 10));
+assertToStringEquals("abc", res[7].exec("abc"), 11);
+assertNull(res[7].exec("*** Failers", 12));
+assertNull(res[7].exec("def\nabc", 13));
assertThrows("var re = /x{5,4}/;", 14);
assertThrows("var re = /[abcd/;", 15);
assertThrows("var re = /[z-a]/;", 16);
assertThrows("var re = /^*/;", 17);
assertThrows("var re = /(abc/;", 18);
assertThrows("var re = /(?# abc/;", 19);
-assertEquals("cat", res[11].exec("this sentence eventually mentions a cat"), 20);
-assertEquals("elephant", res[11].exec("this sentences rambles on and on for a while and then reaches elephant"), 21);
-assertEquals("cat", res[12].exec("this sentence eventually mentions a cat"), 22);
-assertEquals("elephant", res[12].exec("this sentences rambles on and on for a while and then reaches elephant"), 23);
-assertEquals("CAT", res[13].exec("this sentence eventually mentions a CAT cat"), 24);
-assertEquals("elephant", res[13].exec("this sentences rambles on and on for a while to elephant ElePhant"), 25);
+assertToStringEquals("cat", res[11].exec("this sentence eventually mentions a cat"), 20);
+assertToStringEquals("elephant", res[11].exec("this sentences rambles on and on for a while and then reaches elephant"), 21);
+assertToStringEquals("cat", res[12].exec("this sentence eventually mentions a cat"), 22);
+assertToStringEquals("elephant", res[12].exec("this sentences rambles on and on for a while and then reaches elephant"), 23);
+assertToStringEquals("CAT", res[13].exec("this sentence eventually mentions a CAT cat"), 24);
+assertToStringEquals("elephant", res[13].exec("this sentences rambles on and on for a while to elephant ElePhant"), 25);
assertThrows("var re = /{4,5}abc/;", 26);
-assertEquals("abcb,a,b,c", res[18].exec("abcb"), 27);
-assertEquals("abcb,a,b,c", res[18].exec("O0abcb"), 28);
-assertEquals("abcb,a,b,c", res[18].exec("O3abcb"), 29);
-assertEquals("abcb,a,b,c", res[18].exec("O6abcb"), 30);
-assertEquals("abcb,a,b,c", res[18].exec("O9abcb"), 31);
-assertEquals("abcb,a,b,c", res[18].exec("O12abcb"), 32);
-assertEquals("abc,a,,", res[19].exec("abc"), 33);
-assertEquals("abc,a,,", res[19].exec("O0abc"), 34);
-assertEquals("abc,a,,", res[19].exec("O3abc"), 35);
-assertEquals("abc,a,,", res[19].exec("O6abc"), 36);
-assertEquals("aba,,a,b", res[19].exec("aba"), 37);
-assertEquals("aba,,a,b", res[19].exec("O0aba"), 38);
-assertEquals("aba,,a,b", res[19].exec("O3aba"), 39);
-assertEquals("aba,,a,b", res[19].exec("O6aba"), 40);
-assertEquals("aba,,a,b", res[19].exec("O9aba"), 41);
-assertEquals("aba,,a,b", res[19].exec("O12aba"), 42);
-assertEquals("abc", res[20].exec("abc"), 43);
-assertEquals(null, res[20].exec("*** Failers", 44));
-assertEquals(null, res[20].exec("abc\n", 45));
-assertEquals(null, res[20].exec("abc\ndef", 46));
-assertEquals("the quick brown fox", res[22].exec("the quick brown fox"), 47);
-assertEquals("the quick brown fox", res[22].exec("this is a line with the quick brown fox"), 48);
-assertEquals("abc", res[23].exec("abcdef"), 49);
-assertEquals("abc", res[23].exec("abcdefB"), 50);
-assertEquals("defabc,abc,abc,", res[24].exec("defabc"), 51);
-assertEquals("Zdefabc,abc,abc,", res[24].exec("Zdefabc"), 52);
-assertEquals("abc", res[25].exec("abc"), 53);
-assertEquals(null, res[25].exec("*** Failers", 54));
-assertEquals("abc", res[26].exec("abcdef"), 55);
-assertEquals("abc", res[26].exec("abcdefB"), 56);
-assertEquals("defabc,abc,abc,", res[27].exec("defabc"), 57);
-assertEquals("Zdefabc,abc,abc,", res[27].exec("Zdefabc"), 58);
-assertEquals("the quick brown fox", res[28].exec("the quick brown fox"), 59);
-assertEquals(null, res[28].exec("*** Failers", 60));
-assertEquals("The Quick Brown Fox", res[28].exec("The Quick Brown Fox"), 61);
-assertEquals("the quick brown fox", res[29].exec("the quick brown fox"), 62);
-assertEquals("The Quick Brown Fox", res[29].exec("The Quick Brown Fox"), 63);
-assertEquals(null, res[30].exec("*** Failers", 64));
-assertEquals(null, res[30].exec("abc\ndef", 65));
-assertEquals("abc", res[31].exec("abc"), 66);
-assertEquals(null, res[31].exec("abc\n", 67));
-assertEquals("abc,abc", res[33].exec("abc"), 68);
+assertToStringEquals("abcb,a,b,c", res[18].exec("abcb"), 27);
+assertToStringEquals("abcb,a,b,c", res[18].exec("O0abcb"), 28);
+assertToStringEquals("abcb,a,b,c", res[18].exec("O3abcb"), 29);
+assertToStringEquals("abcb,a,b,c", res[18].exec("O6abcb"), 30);
+assertToStringEquals("abcb,a,b,c", res[18].exec("O9abcb"), 31);
+assertToStringEquals("abcb,a,b,c", res[18].exec("O12abcb"), 32);
+assertToStringEquals("abc,a,,", res[19].exec("abc"), 33);
+assertToStringEquals("abc,a,,", res[19].exec("O0abc"), 34);
+assertToStringEquals("abc,a,,", res[19].exec("O3abc"), 35);
+assertToStringEquals("abc,a,,", res[19].exec("O6abc"), 36);
+assertToStringEquals("aba,,a,b", res[19].exec("aba"), 37);
+assertToStringEquals("aba,,a,b", res[19].exec("O0aba"), 38);
+assertToStringEquals("aba,,a,b", res[19].exec("O3aba"), 39);
+assertToStringEquals("aba,,a,b", res[19].exec("O6aba"), 40);
+assertToStringEquals("aba,,a,b", res[19].exec("O9aba"), 41);
+assertToStringEquals("aba,,a,b", res[19].exec("O12aba"), 42);
+assertToStringEquals("abc", res[20].exec("abc"), 43);
+assertNull(res[20].exec("*** Failers", 44));
+assertNull(res[20].exec("abc\n", 45));
+assertNull(res[20].exec("abc\ndef", 46));
+assertToStringEquals("the quick brown fox", res[22].exec("the quick brown fox"), 47);
+assertToStringEquals("the quick brown fox", res[22].exec("this is a line with the quick brown fox"), 48);
+assertToStringEquals("abc", res[23].exec("abcdef"), 49);
+assertToStringEquals("abc", res[23].exec("abcdefB"), 50);
+assertToStringEquals("defabc,abc,abc,", res[24].exec("defabc"), 51);
+assertToStringEquals("Zdefabc,abc,abc,", res[24].exec("Zdefabc"), 52);
+assertToStringEquals("abc", res[25].exec("abc"), 53);
+assertNull(res[25].exec("*** Failers", 54));
+assertToStringEquals("abc", res[26].exec("abcdef"), 55);
+assertToStringEquals("abc", res[26].exec("abcdefB"), 56);
+assertToStringEquals("defabc,abc,abc,", res[27].exec("defabc"), 57);
+assertToStringEquals("Zdefabc,abc,abc,", res[27].exec("Zdefabc"), 58);
+assertToStringEquals("the quick brown fox", res[28].exec("the quick brown fox"), 59);
+assertNull(res[28].exec("*** Failers", 60));
+assertToStringEquals("The Quick Brown Fox", res[28].exec("The Quick Brown Fox"), 61);
+assertToStringEquals("the quick brown fox", res[29].exec("the quick brown fox"), 62);
+assertToStringEquals("The Quick Brown Fox", res[29].exec("The Quick Brown Fox"), 63);
+assertNull(res[30].exec("*** Failers", 64));
+assertNull(res[30].exec("abc\ndef", 65));
+assertToStringEquals("abc", res[31].exec("abc"), 66);
+assertNull(res[31].exec("abc\n", 67));
+assertToStringEquals("abc,abc", res[33].exec("abc"), 68);
assertThrows("var re = /)/;", 69);
-assertEquals("-pr", res[35].exec("co-processors, and for"), 70);
-assertEquals("<def>ghi<klm>", res[36].exec("abc<def>ghi<klm>nop"), 71);
-assertEquals("<def>", res[37].exec("abc<def>ghi<klm>nop"), 72);
-assertEquals("<def>", res[37].exec("abc<def>ghi<klm>nop"), 73);
-assertEquals(null, res[37].exec("abc========def", 74));
-assertEquals(null, res[37].exec("foo", 75));
-assertEquals(null, res[37].exec("catfoo", 76));
-assertEquals(null, res[37].exec("*** Failers", 77));
-assertEquals(null, res[37].exec("the barfoo", 78));
-assertEquals(null, res[37].exec("and cattlefoo", 79));
-assertEquals("a", res[40].exec("a"), 80);
-assertEquals(null, res[40].exec("a\n", 81));
-assertEquals(null, res[40].exec("*** Failers", 82));
-assertEquals("a", res[40].exec("Za"), 83);
-assertEquals(null, res[40].exec("Za\n", 84));
-assertEquals("a", res[41].exec("a"), 85);
-assertEquals("a", res[41].exec("a\n"), 86);
-assertEquals("a", res[41].exec("Za\n"), 87);
-assertEquals(null, res[41].exec("*** Failers", 88));
-assertEquals("a", res[41].exec("Za"), 89);
-assertEquals("b", res[44].exec("foo\nbarbar"), 90);
-assertEquals("a", res[44].exec("***Failers"), 91);
-assertEquals("b", res[44].exec("rhubarb"), 92);
-assertEquals("b", res[44].exec("barbell"), 93);
-assertEquals("a", res[44].exec("abc\nbarton"), 94);
-assertEquals("b", res[44].exec("foo\nbarbar"), 95);
-assertEquals("a", res[44].exec("***Failers"), 96);
-assertEquals("b", res[44].exec("rhubarb"), 97);
-assertEquals("b", res[44].exec("barbell"), 98);
-assertEquals("a", res[44].exec("abc\nbarton"), 99);
-assertEquals("a", res[44].exec("abc"), 100);
-assertEquals("a", res[44].exec("def\nabc"), 101);
-assertEquals("a", res[44].exec("*** Failers"), 102);
-assertEquals("a", res[44].exec("defabc"), 103);
-assertEquals(null, res[45].exec("the bullock-cart", 104));
-assertEquals(null, res[45].exec("a donkey-cart race", 105));
-assertEquals(null, res[45].exec("*** Failers", 106));
-assertEquals(null, res[45].exec("cart", 107));
-assertEquals(null, res[45].exec("horse-and-cart", 108));
-assertEquals(null, res[45].exec("alphabetabcd", 109));
-assertEquals(null, res[45].exec("endingxyz", 110));
-assertEquals(null, res[45].exec("abxyZZ", 111));
-assertEquals(null, res[45].exec("abXyZZ", 112));
-assertEquals(null, res[45].exec("ZZZ", 113));
-assertEquals(null, res[45].exec("zZZ", 114));
-assertEquals(null, res[45].exec("bZZ", 115));
-assertEquals(null, res[45].exec("BZZ", 116));
-assertEquals(null, res[45].exec("*** Failers", 117));
-assertEquals(null, res[45].exec("ZZ", 118));
-assertEquals(null, res[45].exec("abXYZZ", 119));
-assertEquals(null, res[45].exec("zzz", 120));
-assertEquals(null, res[45].exec("bzz", 121));
-assertEquals(null, res[45].exec("bar", 122));
-assertEquals(null, res[45].exec("foobbar", 123));
-assertEquals(null, res[45].exec("*** Failers", 124));
-assertEquals(null, res[45].exec("fooabar", 125));
-assertEquals(null, res[46].exec("*** Failers", 126));
-assertEquals(null, res[46].exec("a", 127));
-assertEquals(null, res[48].exec("aaaaaa", 128));
+assertToStringEquals("-pr", res[35].exec("co-processors, and for"), 70);
+assertToStringEquals("<def>ghi<klm>", res[36].exec("abc<def>ghi<klm>nop"), 71);
+assertToStringEquals("<def>", res[37].exec("abc<def>ghi<klm>nop"), 72);
+assertToStringEquals("<def>", res[37].exec("abc<def>ghi<klm>nop"), 73);
+assertNull(res[37].exec("abc========def", 74));
+assertNull(res[37].exec("foo", 75));
+assertNull(res[37].exec("catfoo", 76));
+assertNull(res[37].exec("*** Failers", 77));
+assertNull(res[37].exec("the barfoo", 78));
+assertNull(res[37].exec("and cattlefoo", 79));
+assertToStringEquals("a", res[40].exec("a"), 80);
+assertNull(res[40].exec("a\n", 81));
+assertNull(res[40].exec("*** Failers", 82));
+assertToStringEquals("a", res[40].exec("Za"), 83);
+assertNull(res[40].exec("Za\n", 84));
+assertToStringEquals("a", res[41].exec("a"), 85);
+assertToStringEquals("a", res[41].exec("a\n"), 86);
+assertToStringEquals("a", res[41].exec("Za\n"), 87);
+assertNull(res[41].exec("*** Failers", 88));
+assertToStringEquals("a", res[41].exec("Za"), 89);
+assertToStringEquals("b", res[44].exec("foo\nbarbar"), 90);
+assertToStringEquals("a", res[44].exec("***Failers"), 91);
+assertToStringEquals("b", res[44].exec("rhubarb"), 92);
+assertToStringEquals("b", res[44].exec("barbell"), 93);
+assertToStringEquals("a", res[44].exec("abc\nbarton"), 94);
+assertToStringEquals("b", res[44].exec("foo\nbarbar"), 95);
+assertToStringEquals("a", res[44].exec("***Failers"), 96);
+assertToStringEquals("b", res[44].exec("rhubarb"), 97);
+assertToStringEquals("b", res[44].exec("barbell"), 98);
+assertToStringEquals("a", res[44].exec("abc\nbarton"), 99);
+assertToStringEquals("a", res[44].exec("abc"), 100);
+assertToStringEquals("a", res[44].exec("def\nabc"), 101);
+assertToStringEquals("a", res[44].exec("*** Failers"), 102);
+assertToStringEquals("a", res[44].exec("defabc"), 103);
+assertNull(res[45].exec("the bullock-cart", 104));
+assertNull(res[45].exec("a donkey-cart race", 105));
+assertNull(res[45].exec("*** Failers", 106));
+assertNull(res[45].exec("cart", 107));
+assertNull(res[45].exec("horse-and-cart", 108));
+assertNull(res[45].exec("alphabetabcd", 109));
+assertNull(res[45].exec("endingxyz", 110));
+assertNull(res[45].exec("abxyZZ", 111));
+assertNull(res[45].exec("abXyZZ", 112));
+assertNull(res[45].exec("ZZZ", 113));
+assertNull(res[45].exec("zZZ", 114));
+assertNull(res[45].exec("bZZ", 115));
+assertNull(res[45].exec("BZZ", 116));
+assertNull(res[45].exec("*** Failers", 117));
+assertNull(res[45].exec("ZZ", 118));
+assertNull(res[45].exec("abXYZZ", 119));
+assertNull(res[45].exec("zzz", 120));
+assertNull(res[45].exec("bzz", 121));
+assertNull(res[45].exec("bar", 122));
+assertNull(res[45].exec("foobbar", 123));
+assertNull(res[45].exec("*** Failers", 124));
+assertNull(res[45].exec("fooabar", 125));
+assertNull(res[46].exec("*** Failers", 126));
+assertNull(res[46].exec("a", 127));
+assertNull(res[48].exec("aaaaaa", 128));
assertThrows("var re = /a[b-a]/;", 129);
assertThrows("var re = /a[/;", 130);
assertThrows("var re = /*a/;", 131);
@@ -1794,4810 +1794,4810 @@
assertThrows("var re = /a(?{\"{\"}})b/;", 148);
assertThrows("var re = /[a[:xyz:/;", 149);
assertThrows("var re = /a{37,17}/;", 150);
-assertEquals("abcd,a,d", res[58].exec("abcd"), 151);
-assertEquals("abcd,a,d", res[58].exec("abcdC2"), 152);
-assertEquals("abcd,a,d", res[58].exec("abcdC5"), 153);
-assertEquals("abcdefghijklmnopqrst,abcdefghijklmnopqrst", res[59].exec("abcdefghijklmnopqrstuvwxyz"), 154);
-assertEquals("abcdefghijklmnopqrst,abcdefghijklmnopqrst", res[59].exec("abcdefghijklmnopqrstuvwxyzC1"), 155);
-assertEquals("abcdefghijklmnopqrst,abcdefghijklmnopqrst", res[59].exec("abcdefghijklmnopqrstuvwxyzG1"), 156);
-assertEquals("abcdefghijklmno,abcdefghijklmno", res[60].exec("abcdefghijklmnopqrstuvwxyz"), 157);
-assertEquals("abcdefghijklmno,abcdefghijklmno", res[60].exec("abcdefghijklmnopqrstuvwxyzC1G1"), 158);
-assertEquals("abcdefghijklmnop,abcdefghijklmnop", res[61].exec("abcdefghijklmnopqrstuvwxyz"), 159);
-assertEquals("abcdefghijklmnop,abcdefghijklmnop", res[61].exec("abcdefghijklmnopqrstuvwxyzC1G1L"), 160);
-assertEquals("adef,a,,f", res[62].exec("adefG1G2G3G4L"), 161);
-assertEquals("bcdef,bc,bc,f", res[62].exec("bcdefG1G2G3G4L"), 162);
-assertEquals("adef,a,,f", res[62].exec("adefghijkC0"), 163);
-assertEquals("abc\x00def", res[63].exec("abc\x00defLC0"), 164);
-assertEquals("iss", res[69].exec("Mississippi"), 165);
-assertEquals("iss", res[70].exec("Mississippi"), 166);
-assertEquals("iss", res[71].exec("Mississippi"), 167);
-assertEquals("iss", res[72].exec("Mississippi"), 168);
-assertEquals("iss", res[73].exec("Mississippi"), 169);
-assertEquals(null, res[73].exec("*** Failers", 170));
-assertEquals("iss", res[73].exec("MississippiA"), 171);
-assertEquals("iss", res[73].exec("Mississippi"), 172);
-assertEquals(null, res[73].exec("Mississippi", 173));
-assertEquals("iss", res[74].exec("ississippi"), 174);
-assertEquals("abciss", res[75].exec("abciss\nxyzisspqr"), 175);
-assertEquals("Mis", res[76].exec("Mississippi"), 176);
-assertEquals("sis", res[76].exec("MississippiA"), 177);
-assertEquals("ri ", res[76].exec("Missouri river"), 178);
-assertEquals("riv", res[76].exec("Missouri riverA"), 179);
-assertEquals("Mis", res[77].exec("Mississippi"), 180);
-assertEquals("ab\n", res[78].exec("ab\nab\ncd"), 181);
-assertEquals("ab\n", res[79].exec("ab\nab\ncd"), 182);
-assertEquals("a", res[115].exec("a"), 183);
-assertEquals("b", res[115].exec("b"), 184);
-assertEquals("ab", res[115].exec("ab"), 185);
-assertEquals("", res[115].exec("\\"), 186);
-assertEquals("", res[115].exec("*** Failers"), 187);
-assertEquals("", res[115].exec("N"), 188);
-assertEquals("", res[116].exec("abcd"), 189);
-assertEquals("", res[116].exec("-abc"), 190);
-assertEquals("", res[116].exec("Nab-c"), 191);
-assertEquals("", res[116].exec("*** Failers"), 192);
-assertEquals("", res[116].exec("Nabc"), 193);
-assertEquals("aaaabbbbzz,bbbb,z,z", res[117].exec("aaaabbbbzzzz"), 194);
-assertEquals("aaaabbbbzz,bbbb,z,z", res[117].exec("aaaabbbbzzzzO0"), 195);
-assertEquals("aaaabbbbzz,bbbb,z,z", res[117].exec("aaaabbbbzzzzO1"), 196);
-assertEquals("aaaabbbbzz,bbbb,z,z", res[117].exec("aaaabbbbzzzzO2"), 197);
-assertEquals("aaaabbbbzz,bbbb,z,z", res[117].exec("aaaabbbbzzzzO3"), 198);
-assertEquals("aaaabbbbzz,bbbb,z,z", res[117].exec("aaaabbbbzzzzO4"), 199);
-assertEquals("aaaabbbbzz,bbbb,z,z", res[117].exec("aaaabbbbzzzzO5"), 200);
-assertEquals("(abcd", res[118].exec("(abcd)"), 201);
-assertEquals("(abcd", res[118].exec("(abcd)xyz"), 202);
-assertEquals(null, res[118].exec("xyz(abcd)", 203));
-assertEquals(null, res[118].exec("(ab(xy)cd)pqr", 204));
-assertEquals(null, res[118].exec("(ab(xycd)pqr", 205));
-assertEquals(null, res[118].exec("() abc ()", 206));
-assertEquals(null, res[118].exec("12(abcde(fsh)xyz(foo(bar))lmno)89", 207));
-assertEquals(null, res[118].exec("*** Failers", 208));
-assertEquals("abcd", res[118].exec("abcd"), 209);
-assertEquals("abcd", res[118].exec("abcd)"), 210);
-assertEquals("(abcd", res[118].exec("(abcd"), 211);
-assertEquals(null, res[118].exec("(ab(xy)cd)pqr", 212));
-assertEquals(null, res[118].exec("1(abcd)(x(y)z)pqr", 213));
-assertEquals("(abcd", res[118].exec("(abcd)"), 214);
-assertEquals(null, res[118].exec("(ab(xy)cd)", 215));
-assertEquals(null, res[118].exec("(a(b(c)d)e)", 216));
-assertEquals(null, res[118].exec("((ab))", 217));
-assertEquals(null, res[118].exec("*** Failers", 218));
-assertEquals(null, res[118].exec("()", 219));
-assertEquals(null, res[118].exec("()", 220));
-assertEquals(null, res[118].exec("12(abcde(fsh)xyz(foo(bar))lmno)89", 221));
-assertEquals(null, res[118].exec("(ab(xy)cd)", 222));
-assertEquals(null, res[118].exec("(ab(xy)cd)", 223));
-assertEquals(null, res[118].exec("(ab(xy)cd)", 224));
-assertEquals(null, res[118].exec("(123ab(xy)cd)", 225));
-assertEquals(null, res[118].exec("(ab(xy)cd)", 226));
-assertEquals(null, res[118].exec("(123ab(xy)cd)", 227));
-assertEquals(null, res[118].exec("(ab(xy)cd)", 228));
-assertEquals("(abcd", res[118].exec("(abcd(xyz<p>qrs)123)"), 229);
-assertEquals(null, res[118].exec("(ab(cd)ef)", 230));
-assertEquals(null, res[118].exec("(ab(cd(ef)gh)ij)", 231));
-assertEquals(null, res[146].exec("A", 232));
-assertEquals(null, res[146].exec("a", 233));
-assertEquals(null, res[147].exec("A", 234));
-assertEquals(null, res[147].exec("a", 235));
-assertEquals(null, res[147].exec("ab", 236));
-assertEquals(null, res[147].exec("aB", 237));
-assertEquals(null, res[147].exec("*** Failers", 238));
-assertEquals(null, res[147].exec("Ab", 239));
-assertEquals(null, res[147].exec("AB", 240));
+assertToStringEquals("abcd,a,d", res[58].exec("abcd"), 151);
+assertToStringEquals("abcd,a,d", res[58].exec("abcdC2"), 152);
+assertToStringEquals("abcd,a,d", res[58].exec("abcdC5"), 153);
+assertToStringEquals("abcdefghijklmnopqrst,abcdefghijklmnopqrst", res[59].exec("abcdefghijklmnopqrstuvwxyz"), 154);
+assertToStringEquals("abcdefghijklmnopqrst,abcdefghijklmnopqrst", res[59].exec("abcdefghijklmnopqrstuvwxyzC1"), 155);
+assertToStringEquals("abcdefghijklmnopqrst,abcdefghijklmnopqrst", res[59].exec("abcdefghijklmnopqrstuvwxyzG1"), 156);
+assertToStringEquals("abcdefghijklmno,abcdefghijklmno", res[60].exec("abcdefghijklmnopqrstuvwxyz"), 157);
+assertToStringEquals("abcdefghijklmno,abcdefghijklmno", res[60].exec("abcdefghijklmnopqrstuvwxyzC1G1"), 158);
+assertToStringEquals("abcdefghijklmnop,abcdefghijklmnop", res[61].exec("abcdefghijklmnopqrstuvwxyz"), 159);
+assertToStringEquals("abcdefghijklmnop,abcdefghijklmnop", res[61].exec("abcdefghijklmnopqrstuvwxyzC1G1L"), 160);
+assertToStringEquals("adef,a,,f", res[62].exec("adefG1G2G3G4L"), 161);
+assertToStringEquals("bcdef,bc,bc,f", res[62].exec("bcdefG1G2G3G4L"), 162);
+assertToStringEquals("adef,a,,f", res[62].exec("adefghijkC0"), 163);
+assertToStringEquals("abc\x00def", res[63].exec("abc\x00defLC0"), 164);
+assertToStringEquals("iss", res[69].exec("Mississippi"), 165);
+assertToStringEquals("iss", res[70].exec("Mississippi"), 166);
+assertToStringEquals("iss", res[71].exec("Mississippi"), 167);
+assertToStringEquals("iss", res[72].exec("Mississippi"), 168);
+assertToStringEquals("iss", res[73].exec("Mississippi"), 169);
+assertNull(res[73].exec("*** Failers", 170));
+assertToStringEquals("iss", res[73].exec("MississippiA"), 171);
+assertToStringEquals("iss", res[73].exec("Mississippi"), 172);
+assertNull(res[73].exec("Mississippi", 173));
+assertToStringEquals("iss", res[74].exec("ississippi"), 174);
+assertToStringEquals("abciss", res[75].exec("abciss\nxyzisspqr"), 175);
+assertToStringEquals("Mis", res[76].exec("Mississippi"), 176);
+assertToStringEquals("sis", res[76].exec("MississippiA"), 177);
+assertToStringEquals("ri ", res[76].exec("Missouri river"), 178);
+assertToStringEquals("riv", res[76].exec("Missouri riverA"), 179);
+assertToStringEquals("Mis", res[77].exec("Mississippi"), 180);
+assertToStringEquals("ab\n", res[78].exec("ab\nab\ncd"), 181);
+assertToStringEquals("ab\n", res[79].exec("ab\nab\ncd"), 182);
+assertToStringEquals("a", res[115].exec("a"), 183);
+assertToStringEquals("b", res[115].exec("b"), 184);
+assertToStringEquals("ab", res[115].exec("ab"), 185);
+assertToStringEquals("", res[115].exec("\\"), 186);
+assertToStringEquals("", res[115].exec("*** Failers"), 187);
+assertToStringEquals("", res[115].exec("N"), 188);
+assertToStringEquals("", res[116].exec("abcd"), 189);
+assertToStringEquals("", res[116].exec("-abc"), 190);
+assertToStringEquals("", res[116].exec("Nab-c"), 191);
+assertToStringEquals("", res[116].exec("*** Failers"), 192);
+assertToStringEquals("", res[116].exec("Nabc"), 193);
+assertToStringEquals("aaaabbbbzz,bbbb,z,z", res[117].exec("aaaabbbbzzzz"), 194);
+assertToStringEquals("aaaabbbbzz,bbbb,z,z", res[117].exec("aaaabbbbzzzzO0"), 195);
+assertToStringEquals("aaaabbbbzz,bbbb,z,z", res[117].exec("aaaabbbbzzzzO1"), 196);
+assertToStringEquals("aaaabbbbzz,bbbb,z,z", res[117].exec("aaaabbbbzzzzO2"), 197);
+assertToStringEquals("aaaabbbbzz,bbbb,z,z", res[117].exec("aaaabbbbzzzzO3"), 198);
+assertToStringEquals("aaaabbbbzz,bbbb,z,z", res[117].exec("aaaabbbbzzzzO4"), 199);
+assertToStringEquals("aaaabbbbzz,bbbb,z,z", res[117].exec("aaaabbbbzzzzO5"), 200);
+assertToStringEquals("(abcd", res[118].exec("(abcd)"), 201);
+assertToStringEquals("(abcd", res[118].exec("(abcd)xyz"), 202);
+assertNull(res[118].exec("xyz(abcd)", 203));
+assertNull(res[118].exec("(ab(xy)cd)pqr", 204));
+assertNull(res[118].exec("(ab(xycd)pqr", 205));
+assertNull(res[118].exec("() abc ()", 206));
+assertNull(res[118].exec("12(abcde(fsh)xyz(foo(bar))lmno)89", 207));
+assertNull(res[118].exec("*** Failers", 208));
+assertToStringEquals("abcd", res[118].exec("abcd"), 209);
+assertToStringEquals("abcd", res[118].exec("abcd)"), 210);
+assertToStringEquals("(abcd", res[118].exec("(abcd"), 211);
+assertNull(res[118].exec("(ab(xy)cd)pqr", 212));
+assertNull(res[118].exec("1(abcd)(x(y)z)pqr", 213));
+assertToStringEquals("(abcd", res[118].exec("(abcd)"), 214);
+assertNull(res[118].exec("(ab(xy)cd)", 215));
+assertNull(res[118].exec("(a(b(c)d)e)", 216));
+assertNull(res[118].exec("((ab))", 217));
+assertNull(res[118].exec("*** Failers", 218));
+assertNull(res[118].exec("()", 219));
+assertNull(res[118].exec("()", 220));
+assertNull(res[118].exec("12(abcde(fsh)xyz(foo(bar))lmno)89", 221));
+assertNull(res[118].exec("(ab(xy)cd)", 222));
+assertNull(res[118].exec("(ab(xy)cd)", 223));
+assertNull(res[118].exec("(ab(xy)cd)", 224));
+assertNull(res[118].exec("(123ab(xy)cd)", 225));
+assertNull(res[118].exec("(ab(xy)cd)", 226));
+assertNull(res[118].exec("(123ab(xy)cd)", 227));
+assertNull(res[118].exec("(ab(xy)cd)", 228));
+assertToStringEquals("(abcd", res[118].exec("(abcd(xyz<p>qrs)123)"), 229);
+assertNull(res[118].exec("(ab(cd)ef)", 230));
+assertNull(res[118].exec("(ab(cd(ef)gh)ij)", 231));
+assertNull(res[146].exec("A", 232));
+assertNull(res[146].exec("a", 233));
+assertNull(res[147].exec("A", 234));
+assertNull(res[147].exec("a", 235));
+assertNull(res[147].exec("ab", 236));
+assertNull(res[147].exec("aB", 237));
+assertNull(res[147].exec("*** Failers", 238));
+assertNull(res[147].exec("Ab", 239));
+assertNull(res[147].exec("AB", 240));
assertThrows("var re = /[\\200-\\110]/;", 241);
-assertEquals("1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 ABC ABC,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16 ,17 ,18 ,19 ,20 ,21 ,22 ,23 ,24 ,25 ,26 ,27 ,28 ,29 ,30 ,31 ,32 ,33 ,34 ,35 ,36 ,37 ,38 ,39 ,40 ,41 ,42 ,43 ,44 ,45 ,46 ,47 ,48 ,49 ,50 ,51 ,52 ,53 ,54 ,55 ,56 ,57 ,58 ,59 ,60 ,61 ,62 ,63 ,64 ,65 ,66 ,67 ,68 ,69 ,70 ,71 ,72 ,73 ,74 ,75 ,76 ,77 ,78 ,79 ,80 ,81 ,82 ,83 ,84 ,85 ,86 ,87 ,88 ,89 ,90 ,91 ,92 ,93 ,94 ,95 ,96 ,97 ,98 ,99 ,100 ,101 ,102 ,103 ,104 ,105 ,106 ,107 ,108 ,109 ,110 ,111 ,112 ,113 ,114 ,115 ,116 ,117 ,118 ,119 ,120 ,121 ,122 ,123 ,124 ,125 ,126 ,127 ,128 ,129 ,130 ,131 ,132 ,133 ,134 ,135 ,136 ,137 ,138 ,139 ,140 ,141 ,142 ,143 ,144 ,145 ,146 ,147 ,148 ,149 ,150 ,151 ,152 ,153 ,154 ,155 ,156 ,157 ,158 ,159 ,160 ,161 ,162 ,163 ,164 ,165 ,166 ,167 ,168 ,169 ,170 ,171 ,172 ,173 ,174 ,175 ,176 ,177 ,178 ,179 ,180 ,181 ,182 ,183 ,184 ,185 ,186 ,187 ,188 ,189 ,190 ,191 ,192 ,193 ,194 ,195 ,196 ,197 ,198 ,199 ,200 ,201 ,202 ,203 ,204 ,205 ,206 ,207 ,208 ,209 ,210 ,211 ,212 ,213 ,214 ,215 ,216 ,217 ,218 ,219 ,220 ,221 ,222 ,223 ,224 ,225 ,226 ,227 ,228 ,229 ,230 ,231 ,232 ,233 ,234 ,235 ,236 ,237 ,238 ,239 ,240 ,241 ,242 ,243 ,244 ,245 ,246 ,247 ,248 ,249 ,250 ,251 ,252 ,253 ,254 ,255 ,256 ,257 ,258 ,259 ,260 ,261 ,262 ,263 ,264 ,265 ,266 ,267 ,268 ,269 ,ABC,ABC", res[149].exec("O900 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 ABC ABC"), 242);
-assertEquals("mainmain,main,", res[151].exec("mainmain"), 243);
-assertEquals("mainOmain,main,", res[151].exec("mainOmain"), 244);
-assertEquals("aba,a,", res[153].exec("aba"), 245);
-assertEquals("aabbaa,aa,", res[154].exec("aabbaa"), 246);
-assertEquals("aabbaa,aa,", res[155].exec("aabbaa"), 247);
-assertEquals("aabbaa,aa,", res[156].exec("aabbaa"), 248);
-assertEquals("aabbaa,", res[157].exec("aabbaa"), 249);
-assertEquals("aabbaa,aa,,", res[158].exec("aabbaa"), 250);
-assertEquals("aabbaa,,", res[159].exec("aabbaa"), 251);
-assertEquals("aabbaa,", res[160].exec("aabbaa"), 252);
-assertEquals("aabbbaa,", res[161].exec("aabbbaa"), 253);
-assertEquals("aabbbaa,", res[162].exec("aabbbaa"), 254);
-assertEquals("aabbaa,", res[163].exec("aabbaa"), 255);
-assertEquals("aabbbaa,", res[164].exec("aabbbaa"), 256);
-assertEquals("aabbbaa,aa,,", res[165].exec("aabbbaa"), 257);
-assertEquals("aabbbbaa,aa,,", res[166].exec("aabbbbaa"), 258);
+assertToStringEquals("1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 ABC ABC,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16 ,17 ,18 ,19 ,20 ,21 ,22 ,23 ,24 ,25 ,26 ,27 ,28 ,29 ,30 ,31 ,32 ,33 ,34 ,35 ,36 ,37 ,38 ,39 ,40 ,41 ,42 ,43 ,44 ,45 ,46 ,47 ,48 ,49 ,50 ,51 ,52 ,53 ,54 ,55 ,56 ,57 ,58 ,59 ,60 ,61 ,62 ,63 ,64 ,65 ,66 ,67 ,68 ,69 ,70 ,71 ,72 ,73 ,74 ,75 ,76 ,77 ,78 ,79 ,80 ,81 ,82 ,83 ,84 ,85 ,86 ,87 ,88 ,89 ,90 ,91 ,92 ,93 ,94 ,95 ,96 ,97 ,98 ,99 ,100 ,101 ,102 ,103 ,104 ,105 ,106 ,107 ,108 ,109 ,110 ,111 ,112 ,113 ,114 ,115 ,116 ,117 ,118 ,119 ,120 ,121 ,122 ,123 ,124 ,125 ,126 ,127 ,128 ,129 ,130 ,131 ,132 ,133 ,134 ,135 ,136 ,137 ,138 ,139 ,140 ,141 ,142 ,143 ,144 ,145 ,146 ,147 ,148 ,149 ,150 ,151 ,152 ,153 ,154 ,155 ,156 ,157 ,158 ,159 ,160 ,161 ,162 ,163 ,164 ,165 ,166 ,167 ,168 ,169 ,170 ,171 ,172 ,173 ,174 ,175 ,176 ,177 ,178 ,179 ,180 ,181 ,182 ,183 ,184 ,185 ,186 ,187 ,188 ,189 ,190 ,191 ,192 ,193 ,194 ,195 ,196 ,197 ,198 ,199 ,200 ,201 ,202 ,203 ,204 ,205 ,206 ,207 ,208 ,209 ,210 ,211 ,212 ,213 ,214 ,215 ,216 ,217 ,218 ,219 ,220 ,221 ,222 ,223 ,224 ,225 ,226 ,227 ,228 ,229 ,230 ,231 ,232 ,233 ,234 ,235 ,236 ,237 ,238 ,239 ,240 ,241 ,242 ,243 ,244 ,245 ,246 ,247 ,248 ,249 ,250 ,251 ,252 ,253 ,254 ,255 ,256 ,257 ,258 ,259 ,260 ,261 ,262 ,263 ,264 ,265 ,266 ,267 ,268 ,269 ,ABC,ABC", res[149].exec("O900 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 ABC ABC"), 242);
+assertToStringEquals("mainmain,main,", res[151].exec("mainmain"), 243);
+assertToStringEquals("mainOmain,main,", res[151].exec("mainOmain"), 244);
+assertToStringEquals("aba,a,", res[153].exec("aba"), 245);
+assertToStringEquals("aabbaa,aa,", res[154].exec("aabbaa"), 246);
+assertToStringEquals("aabbaa,aa,", res[155].exec("aabbaa"), 247);
+assertToStringEquals("aabbaa,aa,", res[156].exec("aabbaa"), 248);
+assertToStringEquals("aabbaa,", res[157].exec("aabbaa"), 249);
+assertToStringEquals("aabbaa,aa,,", res[158].exec("aabbaa"), 250);
+assertToStringEquals("aabbaa,,", res[159].exec("aabbaa"), 251);
+assertToStringEquals("aabbaa,", res[160].exec("aabbaa"), 252);
+assertToStringEquals("aabbbaa,", res[161].exec("aabbbaa"), 253);
+assertToStringEquals("aabbbaa,", res[162].exec("aabbbaa"), 254);
+assertToStringEquals("aabbaa,", res[163].exec("aabbaa"), 255);
+assertToStringEquals("aabbbaa,", res[164].exec("aabbbaa"), 256);
+assertToStringEquals("aabbbaa,aa,,", res[165].exec("aabbbaa"), 257);
+assertToStringEquals("aabbbbaa,aa,,", res[166].exec("aabbbbaa"), 258);
assertThrows("var re = //;", 259);
-assertEquals("a", res[169].exec("ab"), 260);
-assertEquals("a", res[169].exec("aB"), 261);
-assertEquals("*", res[169].exec("*** Failers"), 262);
-assertEquals("A", res[169].exec("AB"), 263);
-assertEquals("a", res[169].exec("ab"), 264);
-assertEquals("a", res[169].exec("aB"), 265);
-assertEquals("*", res[169].exec("*** Failers"), 266);
-assertEquals("A", res[169].exec("AB"), 267);
-assertEquals(null, res[172].exec("\\", 268));
-assertEquals(null, res[177].exec("*** Failers", 269));
-assertEquals(null, res[177].exec("xxxxx", 270));
-assertEquals(null, res[177].exec("now is the time for all good men to come to the aid of the party", 271));
-assertEquals(null, res[177].exec("*** Failers", 272));
-assertEquals(null, res[177].exec("this is not a line with only words and spaces!", 273));
-assertEquals(null, res[177].exec("12345a", 274));
-assertEquals(null, res[177].exec("*** Failers", 275));
-assertEquals(null, res[177].exec("12345+", 276));
-assertEquals(null, res[177].exec("aaab", 277));
-assertEquals(null, res[177].exec("aaab", 278));
-assertEquals(null, res[177].exec("aaab", 279));
-assertEquals(null, res[177].exec("((abc(ade)ufh()()x", 280));
-assertEquals(null, res[177].exec("(abc)", 281));
-assertEquals(null, res[177].exec("(abc(def)xyz)", 282));
-assertEquals(null, res[177].exec("*** Failers", 283));
-assertEquals(null, res[177].exec("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 284));
-assertEquals(null, res[177].exec("xaaaab", 285));
-assertEquals(null, res[177].exec("xaaaab", 286));
+assertToStringEquals("a", res[169].exec("ab"), 260);
+assertToStringEquals("a", res[169].exec("aB"), 261);
+assertToStringEquals("*", res[169].exec("*** Failers"), 262);
+assertToStringEquals("A", res[169].exec("AB"), 263);
+assertToStringEquals("a", res[169].exec("ab"), 264);
+assertToStringEquals("a", res[169].exec("aB"), 265);
+assertToStringEquals("*", res[169].exec("*** Failers"), 266);
+assertToStringEquals("A", res[169].exec("AB"), 267);
+assertNull(res[172].exec("\\", 268));
+assertNull(res[177].exec("*** Failers", 269));
+assertNull(res[177].exec("xxxxx", 270));
+assertNull(res[177].exec("now is the time for all good men to come to the aid of the party", 271));
+assertNull(res[177].exec("*** Failers", 272));
+assertNull(res[177].exec("this is not a line with only words and spaces!", 273));
+assertNull(res[177].exec("12345a", 274));
+assertNull(res[177].exec("*** Failers", 275));
+assertNull(res[177].exec("12345+", 276));
+assertNull(res[177].exec("aaab", 277));
+assertNull(res[177].exec("aaab", 278));
+assertNull(res[177].exec("aaab", 279));
+assertNull(res[177].exec("((abc(ade)ufh()()x", 280));
+assertNull(res[177].exec("(abc)", 281));
+assertNull(res[177].exec("(abc(def)xyz)", 282));
+assertNull(res[177].exec("*** Failers", 283));
+assertNull(res[177].exec("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 284));
+assertNull(res[177].exec("xaaaab", 285));
+assertNull(res[177].exec("xaaaab", 286));
assertThrows("var re = /[/;", 287);
assertThrows("var re = /[a-/;", 288);
-assertEquals(null, res[189].exec("<>", 289));
-assertEquals(null, res[189].exec("<abcd>", 290));
-assertEquals(null, res[189].exec("<abc <123> hij>", 291));
-assertEquals(null, res[189].exec("<abc <def> hij>", 292));
-assertEquals(null, res[189].exec("<abc<>def>", 293));
-assertEquals(null, res[189].exec("<abc<>", 294));
-assertEquals(null, res[189].exec("*** Failers", 295));
-assertEquals(null, res[189].exec("<abc", 296));
-assertEquals("bc123bc,bc,bc", res[195].exec("abc123bc"), 297);
-assertEquals("abc", res[215].exec("abcdef"), 298);
-assertEquals("abc", res[215].exec("1234abcdef"), 299);
-assertEquals(null, res[215].exec("*** Failers", 300));
-assertEquals("abc", res[215].exec("abcxyz"), 301);
-assertEquals("abc", res[215].exec("abcxyzf"), 302);
-assertEquals("abc", res[215].exec("123abcdef"), 303);
-assertEquals("abc", res[215].exec("1234abcdef"), 304);
-assertEquals(null, res[215].exec("*** Failers", 305));
-assertEquals("abc", res[215].exec("abcdef"), 306);
-assertEquals(null, res[215].exec("*** Failers", 307));
-assertEquals("abc", res[215].exec("\x83x0abcdef"), 308);
-assertEquals("abc", res[215].exec("123abcdef"), 309);
-assertEquals("abc", res[215].exec("123abcdefC+"), 310);
-assertEquals("abc", res[215].exec("123abcdefC-"), 311);
-assertEquals(null, res[215].exec("*** Failers", 312));
-assertEquals("abc", res[215].exec("123abcdefC!1"), 313);
-assertEquals("abc", res[215].exec("abcabcabc"), 314);
-assertEquals("abc", res[215].exec("abcabcC!1!3"), 315);
-assertEquals(null, res[215].exec("*** Failers", 316));
-assertEquals("abc", res[215].exec("abcabcabcC!1!3"), 317);
-assertEquals("C", res[215].exec("123C+"), 318);
-assertEquals("C", res[215].exec("123456C+"), 319);
-assertEquals("C", res[215].exec("123456789C+"), 320);
-assertEquals("abc", res[215].exec("xyzabcC+"), 321);
-assertEquals("abc", res[215].exec("XxyzabcC+"), 322);
-assertEquals("abc", res[215].exec("abcdefC+"), 323);
-assertEquals("abc", res[215].exec("abcxyzC+"), 324);
-assertEquals("c", res[215].exec("abbbbbcccC*1"), 325);
-assertEquals("c", res[215].exec("abbbbbcccC*1"), 326);
-assertEquals(null, res[215].exec("xab", 327));
-assertEquals("c", res[215].exec("xbc"), 328);
-assertEquals(null, res[215].exec("xde", 329));
-assertEquals(null, res[215].exec("xxab", 330));
-assertEquals(null, res[215].exec("xxxab", 331));
-assertEquals(null, res[215].exec("*** Failers", 332));
-assertEquals(null, res[215].exec("xyab", 333));
-assertEquals("abc", res[215].exec("abc"), 334);
-assertEquals("c", res[215].exec("a(b)c"), 335);
-assertEquals("c", res[215].exec("a(b(c))d"), 336);
-assertEquals(null, res[215].exec("*** Failers)", 337));
-assertEquals("c", res[215].exec("a(b(c)d"), 338);
-assertEquals(null, res[215].exec("1221", 339));
-assertEquals("c", res[215].exec("Satan, oscillate my metallic sonatas!"), 340);
-assertEquals("c", res[215].exec("A man, a plan, a canal: Panama!"), 341);
-assertEquals(null, res[215].exec("Able was I ere I saw Elba.", 342));
-assertEquals(null, res[215].exec("*** Failers", 343));
-assertEquals("c", res[215].exec("The quick brown fox"), 344);
-assertEquals(null, res[215].exec("12", 345));
-assertEquals(null, res[215].exec("(((2+2)*-3)-7)", 346));
-assertEquals(null, res[215].exec("-12", 347));
-assertEquals(null, res[215].exec("*** Failers", 348));
-assertEquals(null, res[215].exec("((2+2)*-3)-7)", 349));
-assertEquals(null, res[215].exec("xyz", 350));
-assertEquals(null, res[215].exec("xxyzxyzz", 351));
-assertEquals(null, res[215].exec("*** Failers", 352));
-assertEquals(null, res[215].exec("xxyzz", 353));
-assertEquals(null, res[215].exec("xxyzxyzxyzz", 354));
-assertEquals(null, res[215].exec("<>", 355));
-assertEquals("abc", res[215].exec("<abcd>"), 356);
-assertEquals("abc", res[215].exec("<abc <123> hij>"), 357);
-assertEquals("abc", res[215].exec("<abc <def> hij>"), 358);
-assertEquals("abc", res[215].exec("<abc<>def>"), 359);
-assertEquals("abc", res[215].exec("<abc<>"), 360);
-assertEquals(null, res[215].exec("*** Failers", 361));
-assertEquals("abc", res[215].exec("<abc"), 362);
-assertEquals("abc", res[215].exec("abcdefabc"), 363);
-assertEquals(null, res[215].exec("a=a", 364));
-assertEquals(null, res[215].exec("a=b", 365));
-assertEquals("c", res[215].exec("a=bc"), 366);
-assertEquals(null, res[215].exec("a=a", 367));
-assertEquals(null, res[215].exec("a=b", 368));
-assertEquals("c", res[215].exec("a=bc"), 369);
-assertEquals(null, res[215].exec("abde", 370));
-assertEquals("c", res[215].exec("acde"), 371);
-assertEquals(null, res[215].exec("1221", 372));
-assertEquals("c", res[215].exec("Satan, oscillate my metallic sonatas!"), 373);
-assertEquals("c", res[215].exec("A man, a plan, a canal: Panama!"), 374);
-assertEquals(null, res[215].exec("Able was I ere I saw Elba.", 375));
-assertEquals(null, res[215].exec("*** Failers", 376));
-assertEquals("c", res[215].exec("The quick brown fox"), 377);
-assertEquals(null, res[228].exec("abcdefgh", 378));
-assertEquals(null, res[228].exec("abcdefghC1Gtwo", 379));
-assertEquals(null, res[228].exec("abcdefghConeCtwo", 380));
-assertEquals(null, res[228].exec("abcdefghCthree", 381));
-assertEquals("zz,", res[228].exec("zzaaCZ"), 382);
-assertEquals("zz,", res[228].exec("zzaaCA"), 383);
-assertEquals(null, res[228].exec("[10,20,30,5,5,4,4,2,43,23,4234]", 384));
-assertEquals(null, res[228].exec("*** Failers", 385));
-assertEquals(null, res[228].exec("[]", 386));
-assertEquals(null, res[228].exec("[10,20,30,5,5,4,4,2,43,23,4234]", 387));
-assertEquals(null, res[228].exec("[]", 388));
-assertEquals(" Baby Bjorn Active Carrier - With free SHIPPING!!, Baby Bjorn Active Carrier - With free SHIPPING!!,,", res[229].exec(" Baby Bjorn Active Carrier - With free SHIPPING!!"), 389);
-assertEquals(" Baby Bjorn Active Carrier - With free SHIPPING!!, Baby Bjorn Active Carrier - With free SHIPPING!!,,", res[230].exec(" Baby Bjorn Active Carrier - With free SHIPPING!!"), 390);
-assertEquals(null, res[238].exec("Note: that { does NOT introduce a quantifier", 391));
-assertEquals("aacaacaacaacaac123,aac", res[239].exec("aacaacaacaacaac123"), 392);
-assertEquals(null, res[243].exec("abP", 393));
-assertEquals(null, res[243].exec("abcP", 394));
-assertEquals(null, res[243].exec("abcdP", 395));
-assertEquals("abcde", res[243].exec("abcdeP"), 396);
-assertEquals(null, res[243].exec("the quick brown abcP", 397));
-assertEquals(null, res[243].exec("** FailersP", 398));
-assertEquals(null, res[243].exec("the quick brown abxyz foxP", 399));
-assertEquals(null, res[243].exec("13/05/04P", 400));
-assertEquals(null, res[243].exec("13/5/2004P", 401));
-assertEquals(null, res[243].exec("02/05/09P", 402));
-assertEquals(null, res[243].exec("1P", 403));
-assertEquals(null, res[243].exec("1/2P", 404));
-assertEquals(null, res[243].exec("1/2/0P", 405));
-assertEquals(null, res[243].exec("1/2/04P", 406));
-assertEquals(null, res[243].exec("0P", 407));
-assertEquals(null, res[243].exec("02/P", 408));
-assertEquals(null, res[243].exec("02/0P", 409));
-assertEquals(null, res[243].exec("02/1P", 410));
-assertEquals(null, res[243].exec("** FailersP", 411));
-assertEquals(null, res[243].exec("P", 412));
-assertEquals(null, res[243].exec("123P", 413));
-assertEquals(null, res[243].exec("33/4/04P", 414));
-assertEquals(null, res[243].exec("3/13/04P", 415));
-assertEquals(null, res[243].exec("0/1/2003P", 416));
-assertEquals(null, res[243].exec("0/P", 417));
-assertEquals(null, res[243].exec("02/0/P", 418));
-assertEquals(null, res[243].exec("02/13P", 419));
-assertEquals("123", res[248].exec("123P"), 420);
-assertEquals(null, res[248].exec("aP", 421));
-assertEquals(null, res[248].exec("bP", 422));
-assertEquals(null, res[248].exec("cP", 423));
-assertEquals(null, res[248].exec("c12P", 424));
-assertEquals("c123", res[248].exec("c123P"), 425);
-assertEquals(null, res[249].exec("1P", 426));
-assertEquals(null, res[249].exec("123P", 427));
-assertEquals("123X", res[249].exec("123X"), 428);
-assertEquals(null, res[249].exec("1234P", 429));
-assertEquals("1234X", res[249].exec("1234X"), 430);
-assertEquals(null, res[249].exec("12345P", 431));
-assertEquals("12345X", res[249].exec("12345X"), 432);
-assertEquals(null, res[249].exec("*** Failers", 433));
-assertEquals(null, res[249].exec("1X", 434));
-assertEquals(null, res[249].exec("123456P", 435));
-assertEquals(null, res[249].exec("abc", 436));
-assertEquals(null, res[249].exec("** Failers", 437));
-assertEquals(null, res[249].exec("bca", 438));
-assertEquals(null, res[249].exec("abc", 439));
-assertEquals(null, res[249].exec("** Failers", 440));
-assertEquals(null, res[249].exec("bca", 441));
-assertEquals(null, res[249].exec("abc", 442));
-assertEquals(null, res[249].exec("** Failers", 443));
-assertEquals(null, res[249].exec("def", 444));
-assertEquals(null, res[249].exec("abc", 445));
-assertEquals(null, res[249].exec("** Failers", 446));
-assertEquals(null, res[249].exec("def", 447));
-assertEquals(null, res[249].exec("<!DOCTYPE seite SYSTEM \"http://www.lco.lineas.de/xmlCms.dtd\">\n<seite>\n<dokumenteninformation>\n<seitentitel>Partner der LCO</seitentitel>\n<sprache>de</sprache>\n<seitenbeschreibung>Partner der LINEAS Consulting\nGmbH</seitenbeschreibung>\n<schluesselworte>LINEAS Consulting GmbH Hamburg\nPartnerfirmen</schluesselworte>\n<revisit>30 days</revisit>\n<robots>index,follow</robots>\n<menueinformation>\n<aktiv>ja</aktiv>\n<menueposition>3</menueposition>\n<menuetext>Partner</menuetext>\n</menueinformation>\n<lastedited>\n<autor>LCO</autor>\n<firma>LINEAS Consulting</firma>\n<datum>15.10.2003</datum>\n</lastedited>\n</dokumenteninformation>\n<inhalt>\n\n<absatzueberschrift>Die Partnerfirmen der LINEAS Consulting\nGmbH</absatzueberschrift>\n\n<absatz><link ziel=\"http://www.ca.com/\" zielfenster=\"_blank\">\n<bild name=\"logo_ca.gif\" rahmen=\"no\"/></link> <link\nziel=\"http://www.ey.com/\" zielfenster=\"_blank\"><bild\nname=\"logo_euy.gif\" rahmen=\"no\"/></link>\n</absatz>\n\n<absatz><link ziel=\"http://www.cisco.de/\" zielfenster=\"_blank\">\n<bild name=\"logo_cisco.gif\" rahmen=\"ja\"/></link></absatz>\n\n<absatz><link ziel=\"http://www.atelion.de/\"\nzielfenster=\"_blank\"><bild\nname=\"logo_atelion.gif\" rahmen=\"no\"/></link>\n</absatz>\n\n<absatz><link ziel=\"http://www.line-information.de/\"\nzielfenster=\"_blank\">\n<bild name=\"logo_line_information.gif\" rahmen=\"no\"/></link>\n</absatz>\n\n<absatz><bild name=\"logo_aw.gif\" rahmen=\"no\"/></absatz>\n\n<absatz><link ziel=\"http://www.incognis.de/\"\nzielfenster=\"_blank\"><bild\nname=\"logo_incognis.gif\" rahmen=\"no\"/></link></absatz>\n\n<absatz><link ziel=\"http://www.addcraft.com/\"\nzielfenster=\"_blank\"><bild\nname=\"logo_addcraft.gif\" rahmen=\"no\"/></link></absatz>\n\n<absatz><link ziel=\"http://www.comendo.com/\"\nzielfenster=\"_blank\"><bild\nname=\"logo_comendo.gif\" rahmen=\"no\"/></link></absatz>\n\n</inhalt>\n</seite>", 448));
-assertEquals("line\nbreak", res[251].exec("this is a line\nbreak"), 449);
-assertEquals("line\nbreak", res[251].exec("line one\nthis is a line\nbreak in the second line"), 450);
-assertEquals("line\nbreak", res[252].exec("this is a line\nbreak"), 451);
-assertEquals(null, res[252].exec("** Failers", 452));
-assertEquals("line\nbreak", res[252].exec("line one\nthis is a line\nbreak in the second line"), 453);
-assertEquals("line\nbreak", res[253].exec("this is a line\nbreak"), 454);
-assertEquals(null, res[253].exec("** Failers", 455));
-assertEquals("line\nbreak", res[253].exec("line one\nthis is a line\nbreak in the second line"), 456);
-assertEquals("ab-cd", res[254].exec("ab-cd"), 457);
-assertEquals("ab=cd", res[254].exec("ab=cd"), 458);
-assertEquals(null, res[254].exec("** Failers", 459));
-assertEquals(null, res[254].exec("ab\ncd", 460));
-assertEquals("ab-cd", res[255].exec("ab-cd"), 461);
-assertEquals("ab=cd", res[255].exec("ab=cd"), 462);
-assertEquals(null, res[255].exec("ab\ncd", 463));
-assertEquals(null, res[255].exec("AbCd", 464));
-assertEquals(null, res[255].exec("** Failers", 465));
-assertEquals(null, res[255].exec("abcd", 466));
+assertNull(res[189].exec("<>", 289));
+assertNull(res[189].exec("<abcd>", 290));
+assertNull(res[189].exec("<abc <123> hij>", 291));
+assertNull(res[189].exec("<abc <def> hij>", 292));
+assertNull(res[189].exec("<abc<>def>", 293));
+assertNull(res[189].exec("<abc<>", 294));
+assertNull(res[189].exec("*** Failers", 295));
+assertNull(res[189].exec("<abc", 296));
+assertToStringEquals("bc123bc,bc,bc", res[195].exec("abc123bc"), 297);
+assertToStringEquals("abc", res[215].exec("abcdef"), 298);
+assertToStringEquals("abc", res[215].exec("1234abcdef"), 299);
+assertNull(res[215].exec("*** Failers", 300));
+assertToStringEquals("abc", res[215].exec("abcxyz"), 301);
+assertToStringEquals("abc", res[215].exec("abcxyzf"), 302);
+assertToStringEquals("abc", res[215].exec("123abcdef"), 303);
+assertToStringEquals("abc", res[215].exec("1234abcdef"), 304);
+assertNull(res[215].exec("*** Failers", 305));
+assertToStringEquals("abc", res[215].exec("abcdef"), 306);
+assertNull(res[215].exec("*** Failers", 307));
+assertToStringEquals("abc", res[215].exec("\x83x0abcdef"), 308);
+assertToStringEquals("abc", res[215].exec("123abcdef"), 309);
+assertToStringEquals("abc", res[215].exec("123abcdefC+"), 310);
+assertToStringEquals("abc", res[215].exec("123abcdefC-"), 311);
+assertNull(res[215].exec("*** Failers", 312));
+assertToStringEquals("abc", res[215].exec("123abcdefC!1"), 313);
+assertToStringEquals("abc", res[215].exec("abcabcabc"), 314);
+assertToStringEquals("abc", res[215].exec("abcabcC!1!3"), 315);
+assertNull(res[215].exec("*** Failers", 316));
+assertToStringEquals("abc", res[215].exec("abcabcabcC!1!3"), 317);
+assertToStringEquals("C", res[215].exec("123C+"), 318);
+assertToStringEquals("C", res[215].exec("123456C+"), 319);
+assertToStringEquals("C", res[215].exec("123456789C+"), 320);
+assertToStringEquals("abc", res[215].exec("xyzabcC+"), 321);
+assertToStringEquals("abc", res[215].exec("XxyzabcC+"), 322);
+assertToStringEquals("abc", res[215].exec("abcdefC+"), 323);
+assertToStringEquals("abc", res[215].exec("abcxyzC+"), 324);
+assertToStringEquals("c", res[215].exec("abbbbbcccC*1"), 325);
+assertToStringEquals("c", res[215].exec("abbbbbcccC*1"), 326);
+assertNull(res[215].exec("xab", 327));
+assertToStringEquals("c", res[215].exec("xbc"), 328);
+assertNull(res[215].exec("xde", 329));
+assertNull(res[215].exec("xxab", 330));
+assertNull(res[215].exec("xxxab", 331));
+assertNull(res[215].exec("*** Failers", 332));
+assertNull(res[215].exec("xyab", 333));
+assertToStringEquals("abc", res[215].exec("abc"), 334);
+assertToStringEquals("c", res[215].exec("a(b)c"), 335);
+assertToStringEquals("c", res[215].exec("a(b(c))d"), 336);
+assertNull(res[215].exec("*** Failers)", 337));
+assertToStringEquals("c", res[215].exec("a(b(c)d"), 338);
+assertNull(res[215].exec("1221", 339));
+assertToStringEquals("c", res[215].exec("Satan, oscillate my metallic sonatas!"), 340);
+assertToStringEquals("c", res[215].exec("A man, a plan, a canal: Panama!"), 341);
+assertNull(res[215].exec("Able was I ere I saw Elba.", 342));
+assertNull(res[215].exec("*** Failers", 343));
+assertToStringEquals("c", res[215].exec("The quick brown fox"), 344);
+assertNull(res[215].exec("12", 345));
+assertNull(res[215].exec("(((2+2)*-3)-7)", 346));
+assertNull(res[215].exec("-12", 347));
+assertNull(res[215].exec("*** Failers", 348));
+assertNull(res[215].exec("((2+2)*-3)-7)", 349));
+assertNull(res[215].exec("xyz", 350));
+assertNull(res[215].exec("xxyzxyzz", 351));
+assertNull(res[215].exec("*** Failers", 352));
+assertNull(res[215].exec("xxyzz", 353));
+assertNull(res[215].exec("xxyzxyzxyzz", 354));
+assertNull(res[215].exec("<>", 355));
+assertToStringEquals("abc", res[215].exec("<abcd>"), 356);
+assertToStringEquals("abc", res[215].exec("<abc <123> hij>"), 357);
+assertToStringEquals("abc", res[215].exec("<abc <def> hij>"), 358);
+assertToStringEquals("abc", res[215].exec("<abc<>def>"), 359);
+assertToStringEquals("abc", res[215].exec("<abc<>"), 360);
+assertNull(res[215].exec("*** Failers", 361));
+assertToStringEquals("abc", res[215].exec("<abc"), 362);
+assertToStringEquals("abc", res[215].exec("abcdefabc"), 363);
+assertNull(res[215].exec("a=a", 364));
+assertNull(res[215].exec("a=b", 365));
+assertToStringEquals("c", res[215].exec("a=bc"), 366);
+assertNull(res[215].exec("a=a", 367));
+assertNull(res[215].exec("a=b", 368));
+assertToStringEquals("c", res[215].exec("a=bc"), 369);
+assertNull(res[215].exec("abde", 370));
+assertToStringEquals("c", res[215].exec("acde"), 371);
+assertNull(res[215].exec("1221", 372));
+assertToStringEquals("c", res[215].exec("Satan, oscillate my metallic sonatas!"), 373);
+assertToStringEquals("c", res[215].exec("A man, a plan, a canal: Panama!"), 374);
+assertNull(res[215].exec("Able was I ere I saw Elba.", 375));
+assertNull(res[215].exec("*** Failers", 376));
+assertToStringEquals("c", res[215].exec("The quick brown fox"), 377);
+assertNull(res[228].exec("abcdefgh", 378));
+assertNull(res[228].exec("abcdefghC1Gtwo", 379));
+assertNull(res[228].exec("abcdefghConeCtwo", 380));
+assertNull(res[228].exec("abcdefghCthree", 381));
+assertToStringEquals("zz,", res[228].exec("zzaaCZ"), 382);
+assertToStringEquals("zz,", res[228].exec("zzaaCA"), 383);
+assertNull(res[228].exec("[10,20,30,5,5,4,4,2,43,23,4234]", 384));
+assertNull(res[228].exec("*** Failers", 385));
+assertNull(res[228].exec("[]", 386));
+assertNull(res[228].exec("[10,20,30,5,5,4,4,2,43,23,4234]", 387));
+assertNull(res[228].exec("[]", 388));
+assertToStringEquals(" Baby Bjorn Active Carrier - With free SHIPPING!!, Baby Bjorn Active Carrier - With free SHIPPING!!,,", res[229].exec(" Baby Bjorn Active Carrier - With free SHIPPING!!"), 389);
+assertToStringEquals(" Baby Bjorn Active Carrier - With free SHIPPING!!, Baby Bjorn Active Carrier - With free SHIPPING!!,,", res[230].exec(" Baby Bjorn Active Carrier - With free SHIPPING!!"), 390);
+assertNull(res[238].exec("Note: that { does NOT introduce a quantifier", 391));
+assertToStringEquals("aacaacaacaacaac123,aac", res[239].exec("aacaacaacaacaac123"), 392);
+assertNull(res[243].exec("abP", 393));
+assertNull(res[243].exec("abcP", 394));
+assertNull(res[243].exec("abcdP", 395));
+assertToStringEquals("abcde", res[243].exec("abcdeP"), 396);
+assertNull(res[243].exec("the quick brown abcP", 397));
+assertNull(res[243].exec("** FailersP", 398));
+assertNull(res[243].exec("the quick brown abxyz foxP", 399));
+assertNull(res[243].exec("13/05/04P", 400));
+assertNull(res[243].exec("13/5/2004P", 401));
+assertNull(res[243].exec("02/05/09P", 402));
+assertNull(res[243].exec("1P", 403));
+assertNull(res[243].exec("1/2P", 404));
+assertNull(res[243].exec("1/2/0P", 405));
+assertNull(res[243].exec("1/2/04P", 406));
+assertNull(res[243].exec("0P", 407));
+assertNull(res[243].exec("02/P", 408));
+assertNull(res[243].exec("02/0P", 409));
+assertNull(res[243].exec("02/1P", 410));
+assertNull(res[243].exec("** FailersP", 411));
+assertNull(res[243].exec("P", 412));
+assertNull(res[243].exec("123P", 413));
+assertNull(res[243].exec("33/4/04P", 414));
+assertNull(res[243].exec("3/13/04P", 415));
+assertNull(res[243].exec("0/1/2003P", 416));
+assertNull(res[243].exec("0/P", 417));
+assertNull(res[243].exec("02/0/P", 418));
+assertNull(res[243].exec("02/13P", 419));
+assertToStringEquals("123", res[248].exec("123P"), 420);
+assertNull(res[248].exec("aP", 421));
+assertNull(res[248].exec("bP", 422));
+assertNull(res[248].exec("cP", 423));
+assertNull(res[248].exec("c12P", 424));
+assertToStringEquals("c123", res[248].exec("c123P"), 425);
+assertNull(res[249].exec("1P", 426));
+assertNull(res[249].exec("123P", 427));
+assertToStringEquals("123X", res[249].exec("123X"), 428);
+assertNull(res[249].exec("1234P", 429));
+assertToStringEquals("1234X", res[249].exec("1234X"), 430);
+assertNull(res[249].exec("12345P", 431));
+assertToStringEquals("12345X", res[249].exec("12345X"), 432);
+assertNull(res[249].exec("*** Failers", 433));
+assertNull(res[249].exec("1X", 434));
+assertNull(res[249].exec("123456P", 435));
+assertNull(res[249].exec("abc", 436));
+assertNull(res[249].exec("** Failers", 437));
+assertNull(res[249].exec("bca", 438));
+assertNull(res[249].exec("abc", 439));
+assertNull(res[249].exec("** Failers", 440));
+assertNull(res[249].exec("bca", 441));
+assertNull(res[249].exec("abc", 442));
+assertNull(res[249].exec("** Failers", 443));
+assertNull(res[249].exec("def", 444));
+assertNull(res[249].exec("abc", 445));
+assertNull(res[249].exec("** Failers", 446));
+assertNull(res[249].exec("def", 447));
+assertNull(res[249].exec("<!DOCTYPE seite SYSTEM \"http://www.lco.lineas.de/xmlCms.dtd\">\n<seite>\n<dokumenteninformation>\n<seitentitel>Partner der LCO</seitentitel>\n<sprache>de</sprache>\n<seitenbeschreibung>Partner der LINEAS Consulting\nGmbH</seitenbeschreibung>\n<schluesselworte>LINEAS Consulting GmbH Hamburg\nPartnerfirmen</schluesselworte>\n<revisit>30 days</revisit>\n<robots>index,follow</robots>\n<menueinformation>\n<aktiv>ja</aktiv>\n<menueposition>3</menueposition>\n<menuetext>Partner</menuetext>\n</menueinformation>\n<lastedited>\n<autor>LCO</autor>\n<firma>LINEAS Consulting</firma>\n<datum>15.10.2003</datum>\n</lastedited>\n</dokumenteninformation>\n<inhalt>\n\n<absatzueberschrift>Die Partnerfirmen der LINEAS Consulting\nGmbH</absatzueberschrift>\n\n<absatz><link ziel=\"http://www.ca.com/\" zielfenster=\"_blank\">\n<bild name=\"logo_ca.gif\" rahmen=\"no\"/></link> <link\nziel=\"http://www.ey.com/\" zielfenster=\"_blank\"><bild\nname=\"logo_euy.gif\" rahmen=\"no\"/></link>\n</absatz>\n\n<absatz><link ziel=\"http://www.cisco.de/\" zielfenster=\"_blank\">\n<bild name=\"logo_cisco.gif\" rahmen=\"ja\"/></link></absatz>\n\n<absatz><link ziel=\"http://www.atelion.de/\"\nzielfenster=\"_blank\"><bild\nname=\"logo_atelion.gif\" rahmen=\"no\"/></link>\n</absatz>\n\n<absatz><link ziel=\"http://www.line-information.de/\"\nzielfenster=\"_blank\">\n<bild name=\"logo_line_information.gif\" rahmen=\"no\"/></link>\n</absatz>\n\n<absatz><bild name=\"logo_aw.gif\" rahmen=\"no\"/></absatz>\n\n<absatz><link ziel=\"http://www.incognis.de/\"\nzielfenster=\"_blank\"><bild\nname=\"logo_incognis.gif\" rahmen=\"no\"/></link></absatz>\n\n<absatz><link ziel=\"http://www.addcraft.com/\"\nzielfenster=\"_blank\"><bild\nname=\"logo_addcraft.gif\" rahmen=\"no\"/></link></absatz>\n\n<absatz><link ziel=\"http://www.comendo.com/\"\nzielfenster=\"_blank\"><bild\nname=\"logo_comendo.gif\" rahmen=\"no\"/></link></absatz>\n\n</inhalt>\n</seite>", 448));
+assertToStringEquals("line\nbreak", res[251].exec("this is a line\nbreak"), 449);
+assertToStringEquals("line\nbreak", res[251].exec("line one\nthis is a line\nbreak in the second line"), 450);
+assertToStringEquals("line\nbreak", res[252].exec("this is a line\nbreak"), 451);
+assertNull(res[252].exec("** Failers", 452));
+assertToStringEquals("line\nbreak", res[252].exec("line one\nthis is a line\nbreak in the second line"), 453);
+assertToStringEquals("line\nbreak", res[253].exec("this is a line\nbreak"), 454);
+assertNull(res[253].exec("** Failers", 455));
+assertToStringEquals("line\nbreak", res[253].exec("line one\nthis is a line\nbreak in the second line"), 456);
+assertToStringEquals("ab-cd", res[254].exec("ab-cd"), 457);
+assertToStringEquals("ab=cd", res[254].exec("ab=cd"), 458);
+assertNull(res[254].exec("** Failers", 459));
+assertNull(res[254].exec("ab\ncd", 460));
+assertToStringEquals("ab-cd", res[255].exec("ab-cd"), 461);
+assertToStringEquals("ab=cd", res[255].exec("ab=cd"), 462);
+assertNull(res[255].exec("ab\ncd", 463));
+assertNull(res[255].exec("AbCd", 464));
+assertNull(res[255].exec("** Failers", 465));
+assertNull(res[255].exec("abcd", 466));
// We are compatible with JSC, and don't throw an exception in this case.
// assertThrows("var re = /(){2,4294967295}/;", 467);
-assertEquals(null, res[255].exec("abcdefghijklAkB", 468));
-assertEquals(null, res[255].exec("abcdefghijklAkB", 469));
-assertEquals(null, res[255].exec("abcdefghijklAkB", 470));
-assertEquals(null, res[255].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 471));
-assertEquals(null, res[255].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 472));
-assertEquals(null, res[255].exec("(this(and)that", 473));
-assertEquals(null, res[255].exec("(this(and)that)", 474));
-assertEquals(null, res[255].exec("(this(and)that)stuff", 475));
-assertEquals(null, res[255].exec("(this(and)that", 476));
-assertEquals(null, res[255].exec("(this(and)that)", 477));
-assertEquals(null, res[255].exec("(this(and)that", 478));
-assertEquals(null, res[255].exec("(this(and)that)", 479));
-assertEquals(null, res[255].exec("(this(and)that", 480));
-assertEquals(null, res[255].exec("(this(and)that)", 481));
-assertEquals(null, res[255].exec("((this))", 482));
-assertEquals(null, res[255].exec("(this(and)that", 483));
-assertEquals(null, res[255].exec("(this(and)that)", 484));
-assertEquals(null, res[255].exec("(this)", 485));
-assertEquals(null, res[255].exec("((this))", 486));
-assertEquals("abc,b", res[256].exec("abc"), 487);
-assertEquals("abc,b", res[256].exec("abc"), 488);
-assertEquals(null, res[256].exec("a1bCA", 489));
-assertEquals(null, res[256].exec("a2bCA", 490));
-assertEquals(null, res[257].exec("a bc dCACBCC", 491));
-assertEquals(null, res[257].exec("aabc", 492));
-assertEquals(null, res[257].exec("bc", 493));
-assertEquals(null, res[257].exec("** Failers", 494));
-assertEquals(null, res[257].exec("abc", 495));
-assertEquals(null, res[257].exec("bXaX", 496));
-assertEquals(null, res[257].exec("bbXaaX", 497));
-assertEquals(null, res[257].exec("(b)\\Xa\\X", 498));
-assertEquals(null, res[257].exec("bXXaYYaY", 499));
-assertEquals(null, res[257].exec("bXYaXXaX", 500));
-assertEquals(null, res[257].exec("bXXaYYaY", 501));
-assertEquals("\x0b,\x0b", res[259].exec("\x0b,\x0b"), 502);
-assertEquals("\x0c,\x0d", res[259].exec("\x0c,\x0d"), 503);
-assertEquals("abc", res[260].exec("xyz\nabc"), 504);
-assertEquals("abc", res[260].exec("xyz\nabc<lf>"), 505);
-assertEquals("abc", res[260].exec("xyz\x0d\nabc<lf>"), 506);
-assertEquals("abc", res[260].exec("xyz\x0dabc<cr>"), 507);
-assertEquals("abc", res[260].exec("xyz\x0d\nabc<crlf>"), 508);
-assertEquals(null, res[260].exec("** Failers", 509));
-assertEquals("abc", res[260].exec("xyz\nabc<cr>"), 510);
-assertEquals("abc", res[260].exec("xyz\x0d\nabc<cr>"), 511);
-assertEquals("abc", res[260].exec("xyz\nabc<crlf>"), 512);
-assertEquals("abc", res[260].exec("xyz\x0dabc<crlf>"), 513);
-assertEquals("abc", res[260].exec("xyz\x0dabc<lf>"), 514);
-assertEquals("abc", res[261].exec("xyzabc"), 515);
-assertEquals("abc", res[261].exec("xyzabc\n"), 516);
-assertEquals("abc", res[261].exec("xyzabc\npqr"), 517);
-assertEquals("abc", res[261].exec("xyzabc\x0d<cr>"), 518);
-assertEquals("abc", res[261].exec("xyzabc\x0dpqr<cr>"), 519);
-assertEquals("abc", res[261].exec("xyzabc\x0d\n<crlf>"), 520);
-assertEquals("abc", res[261].exec("xyzabc\x0d\npqr<crlf>"), 521);
-assertEquals(null, res[261].exec("** Failers", 522));
-assertEquals("abc", res[261].exec("xyzabc\x0d"), 523);
-assertEquals("abc", res[261].exec("xyzabc\x0dpqr"), 524);
-assertEquals("abc", res[261].exec("xyzabc\x0d\n"), 525);
-assertEquals("abc", res[261].exec("xyzabc\x0d\npqr"), 526);
-assertEquals("abc", res[262].exec("xyz\x0dabcdef"), 527);
-assertEquals("abc", res[262].exec("xyz\nabcdef<lf>"), 528);
-assertEquals(null, res[262].exec("** Failers", 529));
-assertEquals("abc", res[262].exec("xyz\nabcdef"), 530);
-assertEquals("abc", res[263].exec("xyz\nabcdef"), 531);
-assertEquals("abc", res[263].exec("xyz\x0dabcdef<cr>"), 532);
-assertEquals(null, res[263].exec("** Failers", 533));
-assertEquals("abc", res[263].exec("xyz\x0dabcdef"), 534);
-assertEquals("abc", res[264].exec("xyz\x0d\nabcdef"), 535);
-assertEquals("abc", res[264].exec("xyz\x0dabcdef<cr>"), 536);
-assertEquals(null, res[264].exec("** Failers", 537));
-assertEquals("abc", res[264].exec("xyz\x0dabcdef"), 538);
-assertEquals("abc", res[266].exec("xyz\x0dabc<bad>"), 539);
-assertEquals("abc", res[266].exec("abc"), 540);
-assertEquals("abc", res[267].exec("abc\ndef"), 541);
-assertEquals("abc", res[267].exec("abc\x0ddef"), 542);
-assertEquals("abc", res[267].exec("abc\x0d\ndef"), 543);
-assertEquals("<cr>abc", res[267].exec("<cr>abc\ndef"), 544);
-assertEquals("<cr>abc", res[267].exec("<cr>abc\x0ddef"), 545);
-assertEquals("<cr>abc", res[267].exec("<cr>abc\x0d\ndef"), 546);
-assertEquals("<crlf>abc", res[267].exec("<crlf>abc\ndef"), 547);
-assertEquals("<crlf>abc", res[267].exec("<crlf>abc\x0ddef"), 548);
-assertEquals("<crlf>abc", res[267].exec("<crlf>abc\x0d\ndef"), 549);
-assertEquals(null, res[268].exec("abc\ndef", 550));
-assertEquals(null, res[268].exec("abc\x0ddef", 551));
-assertEquals(null, res[268].exec("abc\x0d\ndef", 552));
-assertEquals("XY,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,XY,Y", res[269].exec("XYO400"), 553);
-assertEquals("aaaA5", res[278].exec("aaaA5"), 554);
-assertEquals(null, res[278].exec("** Failers", 555));
-assertEquals(null, res[278].exec("aaaa5", 556));
-assertEquals("aaaA5", res[279].exec("aaaA5"), 557);
-assertEquals("aaaa5", res[279].exec("aaaa5"), 558);
-assertEquals("x", res[350].exec("xyCabcCxyz"), 559);
-assertEquals("x", res[350].exec("xyCabcCxyz"), 560);
-assertEquals("b", res[350].exec("bXaX"), 561);
-assertEquals("b", res[350].exec("bXbX"), 562);
-assertEquals("*", res[350].exec("** Failers"), 563);
-assertEquals("aX", res[350].exec("aXaX"), 564);
-assertEquals("aX", res[350].exec("aXbX"), 565);
-assertEquals("x", res[350].exec("xx"), 566);
-assertEquals("x", res[350].exec("xy"), 567);
-assertEquals("y", res[350].exec("yy"), 568);
-assertEquals("y", res[350].exec("yx"), 569);
-assertEquals("x", res[350].exec("xx"), 570);
-assertEquals("x", res[350].exec("xy"), 571);
-assertEquals("y", res[350].exec("yy"), 572);
-assertEquals("y", res[350].exec("yx"), 573);
-assertEquals("b", res[350].exec("bxay"), 574);
-assertEquals("b", res[350].exec("bxby"), 575);
-assertEquals("*", res[350].exec("** Failers"), 576);
-assertEquals("ax", res[350].exec("axby"), 577);
-assertEquals("X", res[350].exec("XxXxxx"), 578);
-assertEquals("X", res[350].exec("XxXyyx"), 579);
-assertEquals("X", res[350].exec("XxXyxx"), 580);
-assertEquals("*", res[350].exec("** Failers"), 581);
-assertEquals("x", res[350].exec("x"), 582);
-assertEquals("ab", res[350].exec("abcabc"), 583);
-assertEquals("Xaaa,a", res[351].exec("Xaaa"), 584);
-assertEquals("Xaba,a", res[351].exec("Xaba"), 585);
+assertNull(res[255].exec("abcdefghijklAkB", 468));
+assertNull(res[255].exec("abcdefghijklAkB", 469));
+assertNull(res[255].exec("abcdefghijklAkB", 470));
+assertNull(res[255].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 471));
+assertNull(res[255].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 472));
+assertNull(res[255].exec("(this(and)that", 473));
+assertNull(res[255].exec("(this(and)that)", 474));
+assertNull(res[255].exec("(this(and)that)stuff", 475));
+assertNull(res[255].exec("(this(and)that", 476));
+assertNull(res[255].exec("(this(and)that)", 477));
+assertNull(res[255].exec("(this(and)that", 478));
+assertNull(res[255].exec("(this(and)that)", 479));
+assertNull(res[255].exec("(this(and)that", 480));
+assertNull(res[255].exec("(this(and)that)", 481));
+assertNull(res[255].exec("((this))", 482));
+assertNull(res[255].exec("(this(and)that", 483));
+assertNull(res[255].exec("(this(and)that)", 484));
+assertNull(res[255].exec("(this)", 485));
+assertNull(res[255].exec("((this))", 486));
+assertToStringEquals("abc,b", res[256].exec("abc"), 487);
+assertToStringEquals("abc,b", res[256].exec("abc"), 488);
+assertNull(res[256].exec("a1bCA", 489));
+assertNull(res[256].exec("a2bCA", 490));
+assertNull(res[257].exec("a bc dCACBCC", 491));
+assertNull(res[257].exec("aabc", 492));
+assertNull(res[257].exec("bc", 493));
+assertNull(res[257].exec("** Failers", 494));
+assertNull(res[257].exec("abc", 495));
+assertNull(res[257].exec("bXaX", 496));
+assertNull(res[257].exec("bbXaaX", 497));
+assertNull(res[257].exec("(b)\\Xa\\X", 498));
+assertNull(res[257].exec("bXXaYYaY", 499));
+assertNull(res[257].exec("bXYaXXaX", 500));
+assertNull(res[257].exec("bXXaYYaY", 501));
+assertToStringEquals("\x0b,\x0b", res[259].exec("\x0b,\x0b"), 502);
+assertToStringEquals("\x0c,\x0d", res[259].exec("\x0c,\x0d"), 503);
+assertToStringEquals("abc", res[260].exec("xyz\nabc"), 504);
+assertToStringEquals("abc", res[260].exec("xyz\nabc<lf>"), 505);
+assertToStringEquals("abc", res[260].exec("xyz\x0d\nabc<lf>"), 506);
+assertToStringEquals("abc", res[260].exec("xyz\x0dabc<cr>"), 507);
+assertToStringEquals("abc", res[260].exec("xyz\x0d\nabc<crlf>"), 508);
+assertNull(res[260].exec("** Failers", 509));
+assertToStringEquals("abc", res[260].exec("xyz\nabc<cr>"), 510);
+assertToStringEquals("abc", res[260].exec("xyz\x0d\nabc<cr>"), 511);
+assertToStringEquals("abc", res[260].exec("xyz\nabc<crlf>"), 512);
+assertToStringEquals("abc", res[260].exec("xyz\x0dabc<crlf>"), 513);
+assertToStringEquals("abc", res[260].exec("xyz\x0dabc<lf>"), 514);
+assertToStringEquals("abc", res[261].exec("xyzabc"), 515);
+assertToStringEquals("abc", res[261].exec("xyzabc\n"), 516);
+assertToStringEquals("abc", res[261].exec("xyzabc\npqr"), 517);
+assertToStringEquals("abc", res[261].exec("xyzabc\x0d<cr>"), 518);
+assertToStringEquals("abc", res[261].exec("xyzabc\x0dpqr<cr>"), 519);
+assertToStringEquals("abc", res[261].exec("xyzabc\x0d\n<crlf>"), 520);
+assertToStringEquals("abc", res[261].exec("xyzabc\x0d\npqr<crlf>"), 521);
+assertNull(res[261].exec("** Failers", 522));
+assertToStringEquals("abc", res[261].exec("xyzabc\x0d"), 523);
+assertToStringEquals("abc", res[261].exec("xyzabc\x0dpqr"), 524);
+assertToStringEquals("abc", res[261].exec("xyzabc\x0d\n"), 525);
+assertToStringEquals("abc", res[261].exec("xyzabc\x0d\npqr"), 526);
+assertToStringEquals("abc", res[262].exec("xyz\x0dabcdef"), 527);
+assertToStringEquals("abc", res[262].exec("xyz\nabcdef<lf>"), 528);
+assertNull(res[262].exec("** Failers", 529));
+assertToStringEquals("abc", res[262].exec("xyz\nabcdef"), 530);
+assertToStringEquals("abc", res[263].exec("xyz\nabcdef"), 531);
+assertToStringEquals("abc", res[263].exec("xyz\x0dabcdef<cr>"), 532);
+assertNull(res[263].exec("** Failers", 533));
+assertToStringEquals("abc", res[263].exec("xyz\x0dabcdef"), 534);
+assertToStringEquals("abc", res[264].exec("xyz\x0d\nabcdef"), 535);
+assertToStringEquals("abc", res[264].exec("xyz\x0dabcdef<cr>"), 536);
+assertNull(res[264].exec("** Failers", 537));
+assertToStringEquals("abc", res[264].exec("xyz\x0dabcdef"), 538);
+assertToStringEquals("abc", res[266].exec("xyz\x0dabc<bad>"), 539);
+assertToStringEquals("abc", res[266].exec("abc"), 540);
+assertToStringEquals("abc", res[267].exec("abc\ndef"), 541);
+assertToStringEquals("abc", res[267].exec("abc\x0ddef"), 542);
+assertToStringEquals("abc", res[267].exec("abc\x0d\ndef"), 543);
+assertToStringEquals("<cr>abc", res[267].exec("<cr>abc\ndef"), 544);
+assertToStringEquals("<cr>abc", res[267].exec("<cr>abc\x0ddef"), 545);
+assertToStringEquals("<cr>abc", res[267].exec("<cr>abc\x0d\ndef"), 546);
+assertToStringEquals("<crlf>abc", res[267].exec("<crlf>abc\ndef"), 547);
+assertToStringEquals("<crlf>abc", res[267].exec("<crlf>abc\x0ddef"), 548);
+assertToStringEquals("<crlf>abc", res[267].exec("<crlf>abc\x0d\ndef"), 549);
+assertNull(res[268].exec("abc\ndef", 550));
+assertNull(res[268].exec("abc\x0ddef", 551));
+assertNull(res[268].exec("abc\x0d\ndef", 552));
+assertToStringEquals("XY,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,XY,Y", res[269].exec("XYO400"), 553);
+assertToStringEquals("aaaA5", res[278].exec("aaaA5"), 554);
+assertNull(res[278].exec("** Failers", 555));
+assertNull(res[278].exec("aaaa5", 556));
+assertToStringEquals("aaaA5", res[279].exec("aaaA5"), 557);
+assertToStringEquals("aaaa5", res[279].exec("aaaa5"), 558);
+assertToStringEquals("x", res[350].exec("xyCabcCxyz"), 559);
+assertToStringEquals("x", res[350].exec("xyCabcCxyz"), 560);
+assertToStringEquals("b", res[350].exec("bXaX"), 561);
+assertToStringEquals("b", res[350].exec("bXbX"), 562);
+assertToStringEquals("*", res[350].exec("** Failers"), 563);
+assertToStringEquals("aX", res[350].exec("aXaX"), 564);
+assertToStringEquals("aX", res[350].exec("aXbX"), 565);
+assertToStringEquals("x", res[350].exec("xx"), 566);
+assertToStringEquals("x", res[350].exec("xy"), 567);
+assertToStringEquals("y", res[350].exec("yy"), 568);
+assertToStringEquals("y", res[350].exec("yx"), 569);
+assertToStringEquals("x", res[350].exec("xx"), 570);
+assertToStringEquals("x", res[350].exec("xy"), 571);
+assertToStringEquals("y", res[350].exec("yy"), 572);
+assertToStringEquals("y", res[350].exec("yx"), 573);
+assertToStringEquals("b", res[350].exec("bxay"), 574);
+assertToStringEquals("b", res[350].exec("bxby"), 575);
+assertToStringEquals("*", res[350].exec("** Failers"), 576);
+assertToStringEquals("ax", res[350].exec("axby"), 577);
+assertToStringEquals("X", res[350].exec("XxXxxx"), 578);
+assertToStringEquals("X", res[350].exec("XxXyyx"), 579);
+assertToStringEquals("X", res[350].exec("XxXyxx"), 580);
+assertToStringEquals("*", res[350].exec("** Failers"), 581);
+assertToStringEquals("x", res[350].exec("x"), 582);
+assertToStringEquals("ab", res[350].exec("abcabc"), 583);
+assertToStringEquals("Xaaa,a", res[351].exec("Xaaa"), 584);
+assertToStringEquals("Xaba,a", res[351].exec("Xaba"), 585);
assertThrows("var re = /^[a-\\Q\\E]/;", 586);
-assertEquals(null, res[353].exec("(xy)x", 587));
-assertEquals(null, res[353].exec("1221", 588));
-assertEquals(null, res[353].exec("Satan, oscillate my metallic sonatas!", 589));
-assertEquals(null, res[353].exec("A man, a plan, a canal: Panama!", 590));
-assertEquals(null, res[353].exec("Able was I ere I saw Elba.", 591));
-assertEquals(null, res[353].exec("*** Failers", 592));
-assertEquals(null, res[353].exec("The quick brown fox", 593));
-assertEquals("abcd:,abcd", res[354].exec("abcd:"), 594);
-assertEquals("abcd:,abcd", res[354].exec("abcd:"), 595);
-assertEquals("a:,a", res[354].exec("a:aaxyz"), 596);
-assertEquals("ab:,ab", res[354].exec("ab:ababxyz"), 597);
-assertEquals(null, res[354].exec("** Failers", 598));
-assertEquals("a:,a", res[354].exec("a:axyz"), 599);
-assertEquals("ab:,ab", res[354].exec("ab:abxyz"), 600);
-assertEquals(null, res[354].exec("abd", 601));
-assertEquals(null, res[354].exec("ce", 602));
-assertEquals(null, res[354].exec("abcabc1Xabc2XabcXabcabc", 603));
-assertEquals(null, res[354].exec("abcabc1Xabc2XabcXabcabc", 604));
-assertEquals(null, res[354].exec("abcabc1Xabc2XabcXabcabc", 605));
-assertEquals(null, res[354].exec("abcd", 606));
-assertEquals(null, res[354].exec("metcalfe 33", 607));
-assertEquals(null, res[356].exec("a\x0db", 608));
-assertEquals(null, res[356].exec("a\nb<cr>", 609));
-assertEquals("a\x85b", res[356].exec("a\x85b<anycrlf> "), 610);
-assertEquals(null, res[356].exec("** Failers", 611));
-assertEquals(null, res[356].exec("a\nb", 612));
-assertEquals(null, res[356].exec("a\nb<any>", 613));
-assertEquals(null, res[356].exec("a\x0db<cr>", 614));
-assertEquals(null, res[356].exec("a\x0db<any>", 615));
-assertEquals("a\x85b", res[356].exec("a\x85b<any> "), 616);
-assertEquals(null, res[356].exec("a\x0db<anycrlf>", 617));
-assertEquals("abc1", res[357].exec("abc1 \nabc2 \x0babc3xx \x0cabc4 \x0dabc5xx \x0d\nabc6 \x85abc7 JUNK"), 618);
-assertEquals("abc1", res[358].exec("abc1\n abc2\x0b abc3\x0c abc4\x0d abc5\x0d\n abc6\x85 abc7 abc9"), 619);
-assertEquals(null, res[361].exec("a\nb", 620));
-assertEquals(null, res[361].exec("a\x0db", 621));
-assertEquals(null, res[361].exec("a\x0d\nb", 622));
-assertEquals(null, res[361].exec("a\x0bb", 623));
-assertEquals(null, res[361].exec("a\x0cb", 624));
-assertEquals(null, res[361].exec("a\x85b", 625));
-assertEquals(null, res[361].exec("** Failers", 626));
-assertEquals(null, res[361].exec("a\n\x0db", 627));
-assertEquals("ab", res[362].exec("ab"), 628);
-assertEquals(null, res[362].exec("a\nb", 629));
-assertEquals(null, res[362].exec("a\x0db", 630));
-assertEquals(null, res[362].exec("a\x0d\nb", 631));
-assertEquals(null, res[362].exec("a\x0bb", 632));
-assertEquals(null, res[362].exec("a\x0cb", 633));
-assertEquals(null, res[362].exec("a\x85b", 634));
-assertEquals(null, res[362].exec("a\n\x0db", 635));
-assertEquals(null, res[362].exec("a\n\x0d\x85\x0cb", 636));
-assertEquals(null, res[363].exec("a\nb", 637));
-assertEquals(null, res[363].exec("a\x0db", 638));
-assertEquals(null, res[363].exec("a\x0d\nb", 639));
-assertEquals(null, res[363].exec("a\x0bb", 640));
-assertEquals(null, res[363].exec("a\x0cb", 641));
-assertEquals(null, res[363].exec("a\x85b", 642));
-assertEquals(null, res[363].exec("a\n\x0db", 643));
-assertEquals(null, res[363].exec("a\n\x0d\x85\x0cb", 644));
-assertEquals(null, res[363].exec("** Failers", 645));
-assertEquals(null, res[363].exec("ab", 646));
-assertEquals(null, res[364].exec("a\nb", 647));
-assertEquals(null, res[364].exec("a\n\x0db", 648));
-assertEquals(null, res[364].exec("a\n\x0d\x85b", 649));
-assertEquals(null, res[364].exec("a\x0d\n\x0d\nb", 650));
-assertEquals(null, res[364].exec("a\x0d\n\x0d\n\x0d\nb", 651));
-assertEquals(null, res[364].exec("a\n\x0d\n\x0db", 652));
-assertEquals(null, res[364].exec("a\n\n\x0d\nb", 653));
-assertEquals(null, res[364].exec("** Failers", 654));
-assertEquals(null, res[364].exec("a\n\n\n\x0db", 655));
-assertEquals(null, res[364].exec("a\x0d", 656));
-assertEquals("aRb", res[365].exec("aRb"), 657);
-assertEquals(null, res[365].exec("** Failers", 658));
-assertEquals(null, res[365].exec("a\nb", 659));
-assertEquals(null, res[365].exec("abcPXP123", 660));
-assertEquals(null, res[365].exec("abcPXP123", 661));
-assertEquals(null, res[365].exec("1.2.3.4", 662));
-assertEquals(null, res[365].exec("131.111.10.206", 663));
-assertEquals(null, res[365].exec("10.0.0.0", 664));
-assertEquals(null, res[365].exec("** Failers", 665));
-assertEquals(null, res[365].exec("10.6", 666));
-assertEquals(null, res[365].exec("455.3.4.5", 667));
-assertEquals(null, res[365].exec("1.2.3.4", 668));
-assertEquals(null, res[365].exec("131.111.10.206", 669));
-assertEquals(null, res[365].exec("10.0.0.0", 670));
-assertEquals(null, res[365].exec("** Failers", 671));
-assertEquals(null, res[365].exec("10.6", 672));
-assertEquals(null, res[365].exec("455.3.4.5", 673));
-assertEquals(null, res[365].exec("123axbaxbaxbx456", 674));
-assertEquals(null, res[365].exec("123axbaxbaxb456", 675));
-assertEquals(null, res[365].exec("123axbaxbaxbx456", 676));
-assertEquals(null, res[365].exec("123axbaxbaxbx456", 677));
-assertEquals(null, res[365].exec("123axbaxbaxbx456", 678));
-assertEquals(null, res[366].exec("ababababbbabZXXXX", 679));
-assertEquals(null, res[372].exec("a\x0db", 680));
-assertEquals(null, res[372].exec("*** Failers", 681));
-assertEquals(null, res[372].exec("a\nb", 682));
-assertEquals("afoo", res[373].exec("afoo"), 683);
-assertEquals(null, res[373].exec("** Failers", 684));
-assertEquals(null, res[373].exec("\x0d\nfoo", 685));
-assertEquals(null, res[373].exec("\nfoo", 686));
-assertEquals("afoo", res[374].exec("afoo"), 687);
-assertEquals(null, res[374].exec("\nfoo", 688));
-assertEquals(null, res[374].exec("** Failers", 689));
-assertEquals(null, res[374].exec("\x0d\nfoo", 690));
-assertEquals("afoo", res[375].exec("afoo"), 691);
-assertEquals(null, res[375].exec("** Failers", 692));
-assertEquals(null, res[375].exec("\nfoo", 693));
-assertEquals(null, res[375].exec("\x0d\nfoo", 694));
-assertEquals("afoo", res[376].exec("afoo"), 695);
-assertEquals(null, res[376].exec("\x0d\nfoo", 696));
-assertEquals(null, res[376].exec("\nfoo", 697));
-assertEquals("", res[377].exec("abc\x0d\x0dxyz"), 698);
-assertEquals("", res[377].exec("abc\n\x0dxyz "), 699);
-assertEquals(null, res[377].exec("** Failers ", 700));
-assertEquals("", res[377].exec("abc\x0d\nxyz"), 701);
-assertEquals("", res[377].exec("abc\x0d\n\x0d\n"), 702);
-assertEquals("", res[377].exec("abc\x0d\n\x0d\n"), 703);
-assertEquals("", res[377].exec("abc\x0d\n\x0d\n"), 704);
-assertEquals("abc1", res[378].exec("abc1\n abc2\x0b abc3\x0c abc4\x0d abc5\x0d\n abc6\x85 abc9"), 705);
-assertEquals("X", res[379].exec("XABC"), 706);
-assertEquals(null, res[379].exec("** Failers ", 707));
-assertEquals("X", res[379].exec("XABCB"), 708);
+assertNull(res[353].exec("(xy)x", 587));
+assertNull(res[353].exec("1221", 588));
+assertNull(res[353].exec("Satan, oscillate my metallic sonatas!", 589));
+assertNull(res[353].exec("A man, a plan, a canal: Panama!", 590));
+assertNull(res[353].exec("Able was I ere I saw Elba.", 591));
+assertNull(res[353].exec("*** Failers", 592));
+assertNull(res[353].exec("The quick brown fox", 593));
+assertToStringEquals("abcd:,abcd", res[354].exec("abcd:"), 594);
+assertToStringEquals("abcd:,abcd", res[354].exec("abcd:"), 595);
+assertToStringEquals("a:,a", res[354].exec("a:aaxyz"), 596);
+assertToStringEquals("ab:,ab", res[354].exec("ab:ababxyz"), 597);
+assertNull(res[354].exec("** Failers", 598));
+assertToStringEquals("a:,a", res[354].exec("a:axyz"), 599);
+assertToStringEquals("ab:,ab", res[354].exec("ab:abxyz"), 600);
+assertNull(res[354].exec("abd", 601));
+assertNull(res[354].exec("ce", 602));
+assertNull(res[354].exec("abcabc1Xabc2XabcXabcabc", 603));
+assertNull(res[354].exec("abcabc1Xabc2XabcXabcabc", 604));
+assertNull(res[354].exec("abcabc1Xabc2XabcXabcabc", 605));
+assertNull(res[354].exec("abcd", 606));
+assertNull(res[354].exec("metcalfe 33", 607));
+assertNull(res[356].exec("a\x0db", 608));
+assertNull(res[356].exec("a\nb<cr>", 609));
+assertToStringEquals("a\x85b", res[356].exec("a\x85b<anycrlf> "), 610);
+assertNull(res[356].exec("** Failers", 611));
+assertNull(res[356].exec("a\nb", 612));
+assertNull(res[356].exec("a\nb<any>", 613));
+assertNull(res[356].exec("a\x0db<cr>", 614));
+assertNull(res[356].exec("a\x0db<any>", 615));
+assertToStringEquals("a\x85b", res[356].exec("a\x85b<any> "), 616);
+assertNull(res[356].exec("a\x0db<anycrlf>", 617));
+assertToStringEquals("abc1", res[357].exec("abc1 \nabc2 \x0babc3xx \x0cabc4 \x0dabc5xx \x0d\nabc6 \x85abc7 JUNK"), 618);
+assertToStringEquals("abc1", res[358].exec("abc1\n abc2\x0b abc3\x0c abc4\x0d abc5\x0d\n abc6\x85 abc7 abc9"), 619);
+assertNull(res[361].exec("a\nb", 620));
+assertNull(res[361].exec("a\x0db", 621));
+assertNull(res[361].exec("a\x0d\nb", 622));
+assertNull(res[361].exec("a\x0bb", 623));
+assertNull(res[361].exec("a\x0cb", 624));
+assertNull(res[361].exec("a\x85b", 625));
+assertNull(res[361].exec("** Failers", 626));
+assertNull(res[361].exec("a\n\x0db", 627));
+assertToStringEquals("ab", res[362].exec("ab"), 628);
+assertNull(res[362].exec("a\nb", 629));
+assertNull(res[362].exec("a\x0db", 630));
+assertNull(res[362].exec("a\x0d\nb", 631));
+assertNull(res[362].exec("a\x0bb", 632));
+assertNull(res[362].exec("a\x0cb", 633));
+assertNull(res[362].exec("a\x85b", 634));
+assertNull(res[362].exec("a\n\x0db", 635));
+assertNull(res[362].exec("a\n\x0d\x85\x0cb", 636));
+assertNull(res[363].exec("a\nb", 637));
+assertNull(res[363].exec("a\x0db", 638));
+assertNull(res[363].exec("a\x0d\nb", 639));
+assertNull(res[363].exec("a\x0bb", 640));
+assertNull(res[363].exec("a\x0cb", 641));
+assertNull(res[363].exec("a\x85b", 642));
+assertNull(res[363].exec("a\n\x0db", 643));
+assertNull(res[363].exec("a\n\x0d\x85\x0cb", 644));
+assertNull(res[363].exec("** Failers", 645));
+assertNull(res[363].exec("ab", 646));
+assertNull(res[364].exec("a\nb", 647));
+assertNull(res[364].exec("a\n\x0db", 648));
+assertNull(res[364].exec("a\n\x0d\x85b", 649));
+assertNull(res[364].exec("a\x0d\n\x0d\nb", 650));
+assertNull(res[364].exec("a\x0d\n\x0d\n\x0d\nb", 651));
+assertNull(res[364].exec("a\n\x0d\n\x0db", 652));
+assertNull(res[364].exec("a\n\n\x0d\nb", 653));
+assertNull(res[364].exec("** Failers", 654));
+assertNull(res[364].exec("a\n\n\n\x0db", 655));
+assertNull(res[364].exec("a\x0d", 656));
+assertToStringEquals("aRb", res[365].exec("aRb"), 657);
+assertNull(res[365].exec("** Failers", 658));
+assertNull(res[365].exec("a\nb", 659));
+assertNull(res[365].exec("abcPXP123", 660));
+assertNull(res[365].exec("abcPXP123", 661));
+assertNull(res[365].exec("1.2.3.4", 662));
+assertNull(res[365].exec("131.111.10.206", 663));
+assertNull(res[365].exec("10.0.0.0", 664));
+assertNull(res[365].exec("** Failers", 665));
+assertNull(res[365].exec("10.6", 666));
+assertNull(res[365].exec("455.3.4.5", 667));
+assertNull(res[365].exec("1.2.3.4", 668));
+assertNull(res[365].exec("131.111.10.206", 669));
+assertNull(res[365].exec("10.0.0.0", 670));
+assertNull(res[365].exec("** Failers", 671));
+assertNull(res[365].exec("10.6", 672));
+assertNull(res[365].exec("455.3.4.5", 673));
+assertNull(res[365].exec("123axbaxbaxbx456", 674));
+assertNull(res[365].exec("123axbaxbaxb456", 675));
+assertNull(res[365].exec("123axbaxbaxbx456", 676));
+assertNull(res[365].exec("123axbaxbaxbx456", 677));
+assertNull(res[365].exec("123axbaxbaxbx456", 678));
+assertNull(res[366].exec("ababababbbabZXXXX", 679));
+assertNull(res[372].exec("a\x0db", 680));
+assertNull(res[372].exec("*** Failers", 681));
+assertNull(res[372].exec("a\nb", 682));
+assertToStringEquals("afoo", res[373].exec("afoo"), 683);
+assertNull(res[373].exec("** Failers", 684));
+assertNull(res[373].exec("\x0d\nfoo", 685));
+assertNull(res[373].exec("\nfoo", 686));
+assertToStringEquals("afoo", res[374].exec("afoo"), 687);
+assertNull(res[374].exec("\nfoo", 688));
+assertNull(res[374].exec("** Failers", 689));
+assertNull(res[374].exec("\x0d\nfoo", 690));
+assertToStringEquals("afoo", res[375].exec("afoo"), 691);
+assertNull(res[375].exec("** Failers", 692));
+assertNull(res[375].exec("\nfoo", 693));
+assertNull(res[375].exec("\x0d\nfoo", 694));
+assertToStringEquals("afoo", res[376].exec("afoo"), 695);
+assertNull(res[376].exec("\x0d\nfoo", 696));
+assertNull(res[376].exec("\nfoo", 697));
+assertToStringEquals("", res[377].exec("abc\x0d\x0dxyz"), 698);
+assertToStringEquals("", res[377].exec("abc\n\x0dxyz "), 699);
+assertNull(res[377].exec("** Failers ", 700));
+assertToStringEquals("", res[377].exec("abc\x0d\nxyz"), 701);
+assertToStringEquals("", res[377].exec("abc\x0d\n\x0d\n"), 702);
+assertToStringEquals("", res[377].exec("abc\x0d\n\x0d\n"), 703);
+assertToStringEquals("", res[377].exec("abc\x0d\n\x0d\n"), 704);
+assertToStringEquals("abc1", res[378].exec("abc1\n abc2\x0b abc3\x0c abc4\x0d abc5\x0d\n abc6\x85 abc9"), 705);
+assertToStringEquals("X", res[379].exec("XABC"), 706);
+assertNull(res[379].exec("** Failers ", 707));
+assertToStringEquals("X", res[379].exec("XABCB"), 708);
assertThrows("var re = /(ab|c)(?-1)/;", 709);
-assertEquals(null, res[379].exec("abc", 710));
-assertEquals(null, res[379].exec("xyabcabc", 711));
-assertEquals(null, res[379].exec("** Failers", 712));
-assertEquals(null, res[379].exec("xyabc ", 713));
+assertNull(res[379].exec("abc", 710));
+assertNull(res[379].exec("xyabcabc", 711));
+assertNull(res[379].exec("** Failers", 712));
+assertNull(res[379].exec("xyabc ", 713));
assertThrows("var re = /x(?-0)y/;", 714);
assertThrows("var re = /x(?-1)y/;", 715);
-assertEquals(null, res[379].exec("abcX", 716));
-assertEquals(null, res[379].exec("Y", 717));
-assertEquals(null, res[379].exec("** Failers", 718));
-assertEquals(null, res[379].exec("abcY ", 719));
-assertEquals(null, res[379].exec("YabcXabc", 720));
-assertEquals(null, res[379].exec("YabcXabcXabc", 721));
-assertEquals(null, res[379].exec("** Failers", 722));
-assertEquals("X", res[379].exec("XabcXabc "), 723);
-assertEquals(null, res[379].exec("Y!", 724));
-assertEquals(null, res[380].exec("foobar", 725));
-assertEquals(null, res[381].exec("foobar", 726));
-assertEquals("foobaz,foo,baz", res[381].exec("foobaz "), 727);
-assertEquals(null, res[382].exec("foobarbaz", 728));
-assertEquals(null, res[382].exec("tom-tom", 729));
-assertEquals(null, res[382].exec("bon-bon ", 730));
-assertEquals(null, res[382].exec("** Failers", 731));
-assertEquals(null, res[382].exec("tom-bon ", 732));
-assertEquals(null, res[382].exec("tom-tom", 733));
-assertEquals(null, res[382].exec("bon-bon ", 734));
+assertNull(res[379].exec("abcX", 716));
+assertNull(res[379].exec("Y", 717));
+assertNull(res[379].exec("** Failers", 718));
+assertNull(res[379].exec("abcY ", 719));
+assertNull(res[379].exec("YabcXabc", 720));
+assertNull(res[379].exec("YabcXabcXabc", 721));
+assertNull(res[379].exec("** Failers", 722));
+assertToStringEquals("X", res[379].exec("XabcXabc "), 723);
+assertNull(res[379].exec("Y!", 724));
+assertNull(res[380].exec("foobar", 725));
+assertNull(res[381].exec("foobar", 726));
+assertToStringEquals("foobaz,foo,baz", res[381].exec("foobaz "), 727);
+assertNull(res[382].exec("foobarbaz", 728));
+assertNull(res[382].exec("tom-tom", 729));
+assertNull(res[382].exec("bon-bon ", 730));
+assertNull(res[382].exec("** Failers", 731));
+assertNull(res[382].exec("tom-bon ", 732));
+assertNull(res[382].exec("tom-tom", 733));
+assertNull(res[382].exec("bon-bon ", 734));
assertThrows("var re = /(?|(abc)|(xyz))/;", 735);
assertThrows("var re = /(x)(?|(abc)|(xyz))(x)/;", 736);
-assertEquals(null, res[383].exec("xabcx", 737));
-assertEquals(null, res[383].exec("xxyzx ", 738));
+assertNull(res[383].exec("xabcx", 737));
+assertNull(res[383].exec("xxyzx ", 738));
assertThrows("var re = /(x)(?|(abc)(pqr)|(xyz))(x)/;", 739);
-assertEquals(null, res[383].exec("xabcpqrx", 740));
-assertEquals(null, res[383].exec("xxyzx ", 741));
+assertNull(res[383].exec("xabcpqrx", 740));
+assertNull(res[383].exec("xxyzx ", 741));
assertThrows("var re = /(?|(abc)|(xyz))\\1/;", 742);
-assertEquals(null, res[383].exec("abcabc", 743));
-assertEquals(null, res[383].exec("xyzxyz ", 744));
-assertEquals(null, res[383].exec("** Failers", 745));
-assertEquals(null, res[383].exec("abcxyz", 746));
-assertEquals(null, res[383].exec("xyzabc ", 747));
-assertEquals(null, res[383].exec("abcabc", 748));
-assertEquals(null, res[383].exec("xyzabc ", 749));
-assertEquals(null, res[383].exec("** Failers ", 750));
-assertEquals(null, res[383].exec("xyzxyz ", 751));
-assertEquals(null, res[384].exec("X X\n", 752));
-assertEquals(null, res[384].exec("X\x09X\x0b", 753));
-assertEquals(null, res[384].exec("** Failers", 754));
-assertEquals(null, res[384].exec("\xa0 X\n ", 755));
-assertEquals(null, res[385].exec("\x09 \xa0X\n\x0b\x0c\x0d\n", 756));
-assertEquals(null, res[385].exec("\x09 \xa0\n\x0b\x0c\x0d\n", 757));
-assertEquals(null, res[385].exec("\x09 \xa0\n\x0b\x0c", 758));
-assertEquals(null, res[385].exec("** Failers ", 759));
-assertEquals(null, res[385].exec("\x09 \xa0\n\x0b", 760));
-assertEquals(null, res[385].exec(" ", 761));
-assertEquals(null, res[386].exec("XY ABCDE", 762));
-assertEquals(null, res[386].exec("XY PQR ST ", 763));
-assertEquals(null, res[387].exec("XY AB PQRS", 764));
-assertEquals(null, res[388].exec(">XNNNYZ", 765));
-assertEquals(null, res[388].exec("> X NYQZ", 766));
-assertEquals(null, res[388].exec("** Failers", 767));
-assertEquals(null, res[388].exec(">XYZ ", 768));
-assertEquals(null, res[388].exec("> X NY Z", 769));
-assertEquals(null, res[389].exec(">XY\nZ\nA\x0bNN\x0c", 770));
-assertEquals(null, res[389].exec(">\n\x0dX\nY\n\x0bZZZ\nAAA\x0bNNN\x0c", 771));
-assertEquals(null, res[390].exec(">\x09<", 772));
-assertEquals(null, res[391].exec(">\x09 \xa0<", 773));
-assertEquals(null, res[396].exec("** Failers", 774));
-assertEquals(null, res[396].exec("XXXX", 775));
-assertEquals(null, res[397].exec("XXXX Y ", 776));
-assertEquals(null, res[419].exec("aaaaaa", 777));
-assertEquals(null, res[419].exec("aaabccc", 778));
-assertEquals(null, res[419].exec("aaabccc", 779));
-assertEquals(null, res[419].exec("aaabccc", 780));
-assertEquals(null, res[419].exec("aaabcccaaabccc", 781));
-assertEquals(null, res[419].exec("aaaxxxxxx", 782));
-assertEquals(null, res[419].exec("aaa++++++ ", 783));
-assertEquals(null, res[419].exec("bbbxxxxx", 784));
-assertEquals(null, res[419].exec("bbb+++++ ", 785));
-assertEquals(null, res[419].exec("cccxxxx", 786));
-assertEquals(null, res[419].exec("ccc++++ ", 787));
-assertEquals(null, res[419].exec("dddddddd ", 788));
-assertEquals(null, res[419].exec("aaaxxxxxx", 789));
-assertEquals(null, res[419].exec("aaa++++++ ", 790));
-assertEquals(null, res[419].exec("bbbxxxxx", 791));
-assertEquals(null, res[419].exec("bbb+++++ ", 792));
-assertEquals(null, res[419].exec("cccxxxx", 793));
-assertEquals(null, res[419].exec("ccc++++ ", 794));
-assertEquals(null, res[419].exec("dddddddd ", 795));
-assertEquals(null, res[419].exec("aaabccc", 796));
-assertEquals(null, res[419].exec("ABX", 797));
-assertEquals(null, res[419].exec("AADE", 798));
-assertEquals(null, res[419].exec("ACDE", 799));
-assertEquals(null, res[419].exec("** Failers", 800));
-assertEquals(null, res[419].exec("AD ", 801));
-assertEquals(null, res[419].exec(" ", 802));
-assertEquals(null, res[419].exec("aaaaaa", 803));
-assertEquals(null, res[419].exec("aaabccc", 804));
-assertEquals(null, res[419].exec("aaabccc", 805));
-assertEquals(null, res[419].exec("aaabccc", 806));
-assertEquals(null, res[419].exec("aaabcccaaabccc", 807));
-assertEquals(null, res[419].exec("aaabccc", 808));
-assertEquals(null, res[422].exec("\x0d\nA", 809));
-assertEquals("\nA", res[423].exec("\x0d\nA "), 810);
-assertEquals("\nA", res[424].exec("\x0d\nA "), 811);
-assertEquals("\nA,\n", res[425].exec("\x0d\nA "), 812);
-assertEquals(null, res[425].exec("a\nb", 813));
-assertEquals(null, res[425].exec("** Failers", 814));
-assertEquals(null, res[425].exec("a\x0db ", 815));
-assertEquals(null, res[425].exec("a\nb", 816));
-assertEquals(null, res[425].exec("** Failers", 817));
-assertEquals(null, res[425].exec("a\x0db ", 818));
-assertEquals(null, res[425].exec("a\x0db", 819));
-assertEquals(null, res[425].exec("** Failers", 820));
-assertEquals(null, res[425].exec("a\nb ", 821));
-assertEquals(null, res[425].exec("a\x0db", 822));
-assertEquals(null, res[425].exec("a\nb ", 823));
-assertEquals(null, res[425].exec("** Failers", 824));
-assertEquals(null, res[425].exec("a\x0d\nb ", 825));
-assertEquals(null, res[425].exec("** Failers", 826));
-assertEquals(null, res[425].exec("a\x0db", 827));
-assertEquals(null, res[425].exec("a\nb ", 828));
-assertEquals(null, res[425].exec("a\x0d\nb ", 829));
-assertEquals(null, res[425].exec("** Failers", 830));
-assertEquals(null, res[425].exec("a\x0db", 831));
-assertEquals(null, res[425].exec("a\nb ", 832));
-assertEquals(null, res[425].exec("a\x0d\nb ", 833));
-assertEquals(null, res[425].exec("a\x85b ", 834));
-assertEquals(null, res[426].exec("a\x0db", 835));
-assertEquals(null, res[426].exec("a\nb", 836));
-assertEquals(null, res[426].exec("a\x0d\nb", 837));
-assertEquals(null, res[426].exec("** Failers", 838));
-assertEquals(null, res[426].exec("a\x85b", 839));
-assertEquals(null, res[426].exec("a\x0bb ", 840));
-assertEquals(null, res[427].exec("a\x0db", 841));
-assertEquals(null, res[427].exec("a\nb", 842));
-assertEquals(null, res[427].exec("a\x0d\nb", 843));
-assertEquals(null, res[427].exec("a\x85b", 844));
-assertEquals(null, res[427].exec("a\x0bb ", 845));
-assertEquals(null, res[427].exec("** Failers ", 846));
-assertEquals(null, res[427].exec("a\x85b<bsr_anycrlf>", 847));
-assertEquals(null, res[427].exec("a\x0bb<bsr_anycrlf>", 848));
-assertEquals(null, res[428].exec("a\x0db", 849));
-assertEquals(null, res[428].exec("a\nb", 850));
-assertEquals(null, res[428].exec("a\x0d\nb", 851));
-assertEquals(null, res[428].exec("** Failers", 852));
-assertEquals(null, res[428].exec("a\x85b", 853));
-assertEquals(null, res[428].exec("a\x0bb ", 854));
-assertEquals(null, res[429].exec("a\x0db", 855));
-assertEquals(null, res[429].exec("a\nb", 856));
-assertEquals(null, res[429].exec("a\x0d\nb", 857));
-assertEquals(null, res[429].exec("a\x85b", 858));
-assertEquals(null, res[429].exec("a\x0bb ", 859));
-assertEquals(null, res[429].exec("** Failers ", 860));
-assertEquals(null, res[429].exec("a\x85b<bsr_anycrlf>", 861));
-assertEquals(null, res[429].exec("a\x0bb<bsr_anycrlf>", 862));
-assertEquals(null, res[430].exec("a\x0d\n\nb", 863));
-assertEquals(null, res[430].exec("a\n\x0d\x0db", 864));
-assertEquals(null, res[430].exec("a\x0d\n\x0d\n\x0d\n\x0d\nb", 865));
-assertEquals(null, res[430].exec("** Failers", 866));
-assertEquals(null, res[430].exec("a\x8585b", 867));
-assertEquals(null, res[430].exec("a\x0b\x00bb ", 868));
-assertEquals(null, res[431].exec("a\x0d\x0db", 869));
-assertEquals(null, res[431].exec("a\n\n\nb", 870));
-assertEquals(null, res[431].exec("a\x0d\n\n\x0d\x0db", 871));
-assertEquals(null, res[431].exec("a\x8585b", 872));
-assertEquals(null, res[431].exec("a\x0b\x00bb ", 873));
-assertEquals(null, res[431].exec("** Failers ", 874));
-assertEquals(null, res[431].exec("a\x0d\x0d\x0d\x0d\x0db ", 875));
-assertEquals(null, res[431].exec("a\x8585b<bsr_anycrlf>", 876));
-assertEquals(null, res[431].exec("a\x0b\x00bb<bsr_anycrlf>", 877));
-assertEquals(null, res[431].exec("a\nb", 878));
-assertEquals(null, res[431].exec("a\x0db ", 879));
-assertEquals(null, res[431].exec("a\x85b", 880));
-assertEquals(null, res[431].exec("a\nb", 881));
-assertEquals(null, res[431].exec("a\x0db ", 882));
-assertEquals(null, res[431].exec("a\x85b", 883));
+assertNull(res[383].exec("abcabc", 743));
+assertNull(res[383].exec("xyzxyz ", 744));
+assertNull(res[383].exec("** Failers", 745));
+assertNull(res[383].exec("abcxyz", 746));
+assertNull(res[383].exec("xyzabc ", 747));
+assertNull(res[383].exec("abcabc", 748));
+assertNull(res[383].exec("xyzabc ", 749));
+assertNull(res[383].exec("** Failers ", 750));
+assertNull(res[383].exec("xyzxyz ", 751));
+assertNull(res[384].exec("X X\n", 752));
+assertNull(res[384].exec("X\x09X\x0b", 753));
+assertNull(res[384].exec("** Failers", 754));
+assertNull(res[384].exec("\xa0 X\n ", 755));
+assertNull(res[385].exec("\x09 \xa0X\n\x0b\x0c\x0d\n", 756));
+assertNull(res[385].exec("\x09 \xa0\n\x0b\x0c\x0d\n", 757));
+assertNull(res[385].exec("\x09 \xa0\n\x0b\x0c", 758));
+assertNull(res[385].exec("** Failers ", 759));
+assertNull(res[385].exec("\x09 \xa0\n\x0b", 760));
+assertNull(res[385].exec(" ", 761));
+assertNull(res[386].exec("XY ABCDE", 762));
+assertNull(res[386].exec("XY PQR ST ", 763));
+assertNull(res[387].exec("XY AB PQRS", 764));
+assertNull(res[388].exec(">XNNNYZ", 765));
+assertNull(res[388].exec("> X NYQZ", 766));
+assertNull(res[388].exec("** Failers", 767));
+assertNull(res[388].exec(">XYZ ", 768));
+assertNull(res[388].exec("> X NY Z", 769));
+assertNull(res[389].exec(">XY\nZ\nA\x0bNN\x0c", 770));
+assertNull(res[389].exec(">\n\x0dX\nY\n\x0bZZZ\nAAA\x0bNNN\x0c", 771));
+assertNull(res[390].exec(">\x09<", 772));
+assertNull(res[391].exec(">\x09 \xa0<", 773));
+assertNull(res[396].exec("** Failers", 774));
+assertNull(res[396].exec("XXXX", 775));
+assertNull(res[397].exec("XXXX Y ", 776));
+assertNull(res[419].exec("aaaaaa", 777));
+assertNull(res[419].exec("aaabccc", 778));
+assertNull(res[419].exec("aaabccc", 779));
+assertNull(res[419].exec("aaabccc", 780));
+assertNull(res[419].exec("aaabcccaaabccc", 781));
+assertNull(res[419].exec("aaaxxxxxx", 782));
+assertNull(res[419].exec("aaa++++++ ", 783));
+assertNull(res[419].exec("bbbxxxxx", 784));
+assertNull(res[419].exec("bbb+++++ ", 785));
+assertNull(res[419].exec("cccxxxx", 786));
+assertNull(res[419].exec("ccc++++ ", 787));
+assertNull(res[419].exec("dddddddd ", 788));
+assertNull(res[419].exec("aaaxxxxxx", 789));
+assertNull(res[419].exec("aaa++++++ ", 790));
+assertNull(res[419].exec("bbbxxxxx", 791));
+assertNull(res[419].exec("bbb+++++ ", 792));
+assertNull(res[419].exec("cccxxxx", 793));
+assertNull(res[419].exec("ccc++++ ", 794));
+assertNull(res[419].exec("dddddddd ", 795));
+assertNull(res[419].exec("aaabccc", 796));
+assertNull(res[419].exec("ABX", 797));
+assertNull(res[419].exec("AADE", 798));
+assertNull(res[419].exec("ACDE", 799));
+assertNull(res[419].exec("** Failers", 800));
+assertNull(res[419].exec("AD ", 801));
+assertNull(res[419].exec(" ", 802));
+assertNull(res[419].exec("aaaaaa", 803));
+assertNull(res[419].exec("aaabccc", 804));
+assertNull(res[419].exec("aaabccc", 805));
+assertNull(res[419].exec("aaabccc", 806));
+assertNull(res[419].exec("aaabcccaaabccc", 807));
+assertNull(res[419].exec("aaabccc", 808));
+assertNull(res[422].exec("\x0d\nA", 809));
+assertToStringEquals("\nA", res[423].exec("\x0d\nA "), 810);
+assertToStringEquals("\nA", res[424].exec("\x0d\nA "), 811);
+assertToStringEquals("\nA,\n", res[425].exec("\x0d\nA "), 812);
+assertNull(res[425].exec("a\nb", 813));
+assertNull(res[425].exec("** Failers", 814));
+assertNull(res[425].exec("a\x0db ", 815));
+assertNull(res[425].exec("a\nb", 816));
+assertNull(res[425].exec("** Failers", 817));
+assertNull(res[425].exec("a\x0db ", 818));
+assertNull(res[425].exec("a\x0db", 819));
+assertNull(res[425].exec("** Failers", 820));
+assertNull(res[425].exec("a\nb ", 821));
+assertNull(res[425].exec("a\x0db", 822));
+assertNull(res[425].exec("a\nb ", 823));
+assertNull(res[425].exec("** Failers", 824));
+assertNull(res[425].exec("a\x0d\nb ", 825));
+assertNull(res[425].exec("** Failers", 826));
+assertNull(res[425].exec("a\x0db", 827));
+assertNull(res[425].exec("a\nb ", 828));
+assertNull(res[425].exec("a\x0d\nb ", 829));
+assertNull(res[425].exec("** Failers", 830));
+assertNull(res[425].exec("a\x0db", 831));
+assertNull(res[425].exec("a\nb ", 832));
+assertNull(res[425].exec("a\x0d\nb ", 833));
+assertNull(res[425].exec("a\x85b ", 834));
+assertNull(res[426].exec("a\x0db", 835));
+assertNull(res[426].exec("a\nb", 836));
+assertNull(res[426].exec("a\x0d\nb", 837));
+assertNull(res[426].exec("** Failers", 838));
+assertNull(res[426].exec("a\x85b", 839));
+assertNull(res[426].exec("a\x0bb ", 840));
+assertNull(res[427].exec("a\x0db", 841));
+assertNull(res[427].exec("a\nb", 842));
+assertNull(res[427].exec("a\x0d\nb", 843));
+assertNull(res[427].exec("a\x85b", 844));
+assertNull(res[427].exec("a\x0bb ", 845));
+assertNull(res[427].exec("** Failers ", 846));
+assertNull(res[427].exec("a\x85b<bsr_anycrlf>", 847));
+assertNull(res[427].exec("a\x0bb<bsr_anycrlf>", 848));
+assertNull(res[428].exec("a\x0db", 849));
+assertNull(res[428].exec("a\nb", 850));
+assertNull(res[428].exec("a\x0d\nb", 851));
+assertNull(res[428].exec("** Failers", 852));
+assertNull(res[428].exec("a\x85b", 853));
+assertNull(res[428].exec("a\x0bb ", 854));
+assertNull(res[429].exec("a\x0db", 855));
+assertNull(res[429].exec("a\nb", 856));
+assertNull(res[429].exec("a\x0d\nb", 857));
+assertNull(res[429].exec("a\x85b", 858));
+assertNull(res[429].exec("a\x0bb ", 859));
+assertNull(res[429].exec("** Failers ", 860));
+assertNull(res[429].exec("a\x85b<bsr_anycrlf>", 861));
+assertNull(res[429].exec("a\x0bb<bsr_anycrlf>", 862));
+assertNull(res[430].exec("a\x0d\n\nb", 863));
+assertNull(res[430].exec("a\n\x0d\x0db", 864));
+assertNull(res[430].exec("a\x0d\n\x0d\n\x0d\n\x0d\nb", 865));
+assertNull(res[430].exec("** Failers", 866));
+assertNull(res[430].exec("a\x8585b", 867));
+assertNull(res[430].exec("a\x0b\x00bb ", 868));
+assertNull(res[431].exec("a\x0d\x0db", 869));
+assertNull(res[431].exec("a\n\n\nb", 870));
+assertNull(res[431].exec("a\x0d\n\n\x0d\x0db", 871));
+assertNull(res[431].exec("a\x8585b", 872));
+assertNull(res[431].exec("a\x0b\x00bb ", 873));
+assertNull(res[431].exec("** Failers ", 874));
+assertNull(res[431].exec("a\x0d\x0d\x0d\x0d\x0db ", 875));
+assertNull(res[431].exec("a\x8585b<bsr_anycrlf>", 876));
+assertNull(res[431].exec("a\x0b\x00bb<bsr_anycrlf>", 877));
+assertNull(res[431].exec("a\nb", 878));
+assertNull(res[431].exec("a\x0db ", 879));
+assertNull(res[431].exec("a\x85b", 880));
+assertNull(res[431].exec("a\nb", 881));
+assertNull(res[431].exec("a\x0db ", 882));
+assertNull(res[431].exec("a\x85b", 883));
assertThrows("var re = /(?-+a)/;", 884);
-assertEquals(null, res[443].exec("aaaa", 885));
-assertEquals(null, res[443].exec("bacxxx", 886));
-assertEquals(null, res[443].exec("bbaccxxx ", 887));
-assertEquals(null, res[443].exec("bbbacccxx", 888));
-assertEquals(null, res[443].exec("aaaa", 889));
-assertEquals(null, res[443].exec("bacxxx", 890));
-assertEquals(null, res[443].exec("bbaccxxx ", 891));
-assertEquals(null, res[443].exec("bbbacccxx", 892));
-assertEquals("a,a", res[444].exec("aaaa"), 893);
-assertEquals(null, res[444].exec("bacxxx", 894));
-assertEquals(null, res[444].exec("bbaccxxx ", 895));
-assertEquals(null, res[444].exec("bbbacccxx", 896));
-assertEquals("a,a", res[445].exec("aaaa"), 897);
-assertEquals(null, res[445].exec("bacxxx", 898));
-assertEquals(null, res[445].exec("bbaccxxx ", 899));
-assertEquals(null, res[445].exec("bbbacccxx", 900));
-assertEquals("a,a", res[446].exec("aaaa"), 901);
-assertEquals(null, res[446].exec("bacxxx", 902));
-assertEquals(null, res[446].exec("bbaccxxx ", 903));
-assertEquals(null, res[446].exec("bbbacccxx", 904));
-assertEquals("a,a,a", res[447].exec("aaaa"), 905);
-assertEquals(null, res[447].exec("bacxxx", 906));
-assertEquals(null, res[447].exec("bbaccxxx ", 907));
-assertEquals(null, res[447].exec("bbbacccxx", 908));
-assertEquals(null, res[449].exec("bacxxx", 909));
-assertEquals(null, res[449].exec("XaaX", 910));
-assertEquals(null, res[449].exec("XAAX ", 911));
-assertEquals(null, res[449].exec("XaaX", 912));
-assertEquals(null, res[449].exec("** Failers ", 913));
-assertEquals(null, res[449].exec("XAAX ", 914));
-assertEquals(null, res[449].exec("XaaX", 915));
-assertEquals(null, res[449].exec("XAAX ", 916));
-assertEquals(null, res[449].exec("xzxx", 917));
-assertEquals(null, res[449].exec("yzyy ", 918));
-assertEquals(null, res[449].exec("** Failers", 919));
-assertEquals(null, res[449].exec("xxz ", 920));
-assertEquals("a,,,a", res[450].exec("cat"), 921);
-assertEquals("a,,,a", res[451].exec("cat"), 922);
-assertEquals("TA]", res[452].exec("The ACTA] comes "), 923);
-assertEquals("TA]", res[453].exec("The ACTA] comes "), 924);
-assertEquals(null, res[453].exec("abcbabc", 925));
-assertEquals(null, res[453].exec("abcbabc", 926));
-assertEquals(null, res[453].exec("abcbabc", 927));
-assertEquals(null, res[453].exec("** Failers ", 928));
-assertEquals(null, res[453].exec("abcXabc", 929));
-assertEquals(null, res[453].exec("abcXabc", 930));
-assertEquals(null, res[453].exec("** Failers ", 931));
-assertEquals(null, res[453].exec("abcbabc", 932));
-assertEquals(null, res[453].exec("xyzbabcxyz", 933));
-assertEquals(null, res[456].exec("** Failers", 934));
-assertEquals(null, res[456].exec("ab", 935));
-assertEquals(null, res[457].exec("** Failers", 936));
-assertEquals(null, res[457].exec("ab ", 937));
-assertEquals(null, res[457].exec("** Failers", 938));
-assertEquals(null, res[457].exec("ab ", 939));
-assertEquals("aXb", res[458].exec("aXb"), 940);
-assertEquals("a\nb", res[458].exec("a\nb "), 941);
-assertEquals(null, res[458].exec("** Failers", 942));
-assertEquals(null, res[458].exec("ab ", 943));
-assertEquals("aXb", res[459].exec("aXb"), 944);
-assertEquals("a\nX\nXb", res[459].exec("a\nX\nXb "), 945);
-assertEquals(null, res[459].exec("** Failers", 946));
-assertEquals(null, res[459].exec("ab ", 947));
-assertEquals("acb", res[463].exec("acb"), 948);
-assertEquals("ab", res[463].exec("ab"), 949);
-assertEquals(null, res[463].exec("ax{100}b ", 950));
-assertEquals(null, res[463].exec("*** Failers", 951));
-assertEquals(null, res[463].exec("a\nb ", 952));
-assertEquals(null, res[464].exec("ax{4000}xyb ", 953));
-assertEquals(null, res[464].exec("ax{4000}yb ", 954));
-assertEquals(null, res[464].exec("ax{4000}x{100}yb ", 955));
-assertEquals(null, res[464].exec("*** Failers", 956));
-assertEquals(null, res[464].exec("ax{4000}b ", 957));
-assertEquals(null, res[464].exec("ac\ncb ", 958));
-assertEquals("a\xc0,,\xc0", res[465].exec("a\xc0\x88b"), 959);
-assertEquals("ax,,x", res[466].exec("ax{100}b"), 960);
-assertEquals("a\xc0\x88b,\xc0\x88,b", res[467].exec("a\xc0\x88b"), 961);
-assertEquals("ax{100}b,x{100},b", res[468].exec("ax{100}b"), 962);
-assertEquals("a\xc0\x92,\xc0,\x92", res[469].exec("a\xc0\x92bcd"), 963);
-assertEquals("ax{,x,{", res[470].exec("ax{240}bcd"), 964);
-assertEquals("a\xc0\x92,\xc0,\x92", res[471].exec("a\xc0\x92bcd"), 965);
-assertEquals("ax{,x,{", res[472].exec("ax{240}bcd"), 966);
-assertEquals("a\xc0,,\xc0", res[473].exec("a\xc0\x92bcd"), 967);
-assertEquals("ax,,x", res[474].exec("ax{240}bcd"), 968);
-assertEquals(null, res[475].exec("ax{1234}xyb ", 969));
-assertEquals(null, res[475].exec("ax{1234}x{4321}yb ", 970));
-assertEquals(null, res[475].exec("ax{1234}x{4321}x{3412}b ", 971));
-assertEquals(null, res[475].exec("*** Failers", 972));
-assertEquals(null, res[475].exec("ax{1234}b ", 973));
-assertEquals(null, res[475].exec("ac\ncb ", 974));
-assertEquals("ax{1234}xyb,x{1234}xy", res[476].exec("ax{1234}xyb "), 975);
-assertEquals("ax{1234}x{4321}yb,x{1234}x{4321}y", res[476].exec("ax{1234}x{4321}yb "), 976);
-assertEquals("ax{1234}x{4321}x{3412}b,x{1234}x{4321}x{3412}", res[476].exec("ax{1234}x{4321}x{3412}b "), 977);
-assertEquals("axxxxbcdefghijb,xxxxbcdefghij", res[476].exec("axxxxbcdefghijb "), 978);
-assertEquals("ax{1234}x{4321}x{3412}x{3421}b,x{1234}x{4321}x{3412}x{3421}", res[476].exec("ax{1234}x{4321}x{3412}x{3421}b "), 979);
-assertEquals(null, res[476].exec("*** Failers", 980));
-assertEquals("ax{1234}b,x{1234}", res[476].exec("ax{1234}b "), 981);
-assertEquals("ax{1234}xyb,x{1234}xy", res[477].exec("ax{1234}xyb "), 982);
-assertEquals("ax{1234}x{4321}yb,x{1234}x{4321}y", res[477].exec("ax{1234}x{4321}yb "), 983);
-assertEquals("ax{1234}x{4321}x{3412}b,x{1234}x{4321}x{3412}", res[477].exec("ax{1234}x{4321}x{3412}b "), 984);
-assertEquals("axxxxb,xxxx", res[477].exec("axxxxbcdefghijb "), 985);
-assertEquals("ax{1234}x{4321}x{3412}x{3421}b,x{1234}x{4321}x{3412}x{3421}", res[477].exec("ax{1234}x{4321}x{3412}x{3421}b "), 986);
-assertEquals(null, res[477].exec("*** Failers", 987));
-assertEquals("ax{1234}b,x{1234}", res[477].exec("ax{1234}b "), 988);
-assertEquals(null, res[478].exec("ax{1234}xyb ", 989));
-assertEquals(null, res[478].exec("ax{1234}x{4321}yb ", 990));
-assertEquals(null, res[478].exec("ax{1234}x{4321}x{3412}b ", 991));
-assertEquals("axxxxb,xxxx", res[478].exec("axxxxbcdefghijb "), 992);
-assertEquals(null, res[478].exec("ax{1234}x{4321}x{3412}x{3421}b ", 993));
-assertEquals("axbxxb,xbxx", res[478].exec("axbxxbcdefghijb "), 994);
-assertEquals("axxxxxb,xxxxx", res[478].exec("axxxxxbcdefghijb "), 995);
-assertEquals(null, res[478].exec("*** Failers", 996));
-assertEquals(null, res[478].exec("ax{1234}b ", 997));
-assertEquals(null, res[478].exec("axxxxxxbcdefghijb ", 998));
-assertEquals(null, res[479].exec("ax{1234}xyb ", 999));
-assertEquals(null, res[479].exec("ax{1234}x{4321}yb ", 1000));
-assertEquals(null, res[479].exec("ax{1234}x{4321}x{3412}b ", 1001));
-assertEquals("axxxxb,xxxx", res[479].exec("axxxxbcdefghijb "), 1002);
-assertEquals(null, res[479].exec("ax{1234}x{4321}x{3412}x{3421}b ", 1003));
-assertEquals("axbxxb,xbxx", res[479].exec("axbxxbcdefghijb "), 1004);
-assertEquals("axxxxxb,xxxxx", res[479].exec("axxxxxbcdefghijb "), 1005);
-assertEquals(null, res[479].exec("*** Failers", 1006));
-assertEquals(null, res[479].exec("ax{1234}b ", 1007));
-assertEquals(null, res[479].exec("axxxxxxbcdefghijb ", 1008));
-assertEquals(null, res[479].exec("*** Failers", 1009));
-assertEquals(null, res[479].exec("x{100}", 1010));
-assertEquals(null, res[479].exec("aXbcd", 1011));
-assertEquals(null, res[479].exec("ax{100}bcd", 1012));
-assertEquals(null, res[479].exec("ax{100000}bcd", 1013));
-assertEquals(null, res[479].exec("x{100}x{100}x{100}b", 1014));
-assertEquals(null, res[479].exec("*** Failers ", 1015));
-assertEquals(null, res[479].exec("x{100}x{100}b", 1016));
-assertEquals(null, res[479].exec("x{ab} ", 1017));
-assertEquals(null, res[479].exec("\xc2\xab", 1018));
-assertEquals(null, res[479].exec("*** Failers ", 1019));
-assertEquals(null, res[479].exec("\x00{ab}", 1020));
-assertEquals(null, res[479].exec("WXYZ", 1021));
-assertEquals(null, res[479].exec("x{256}XYZ ", 1022));
-assertEquals(null, res[479].exec("*** Failers", 1023));
-assertEquals(null, res[479].exec("XYZ ", 1024));
-assertEquals(null, res[480].exec("Xx{1234}", 1025));
-assertEquals(null, res[481].exec("Xx{1234}YZ", 1026));
-assertEquals("X", res[482].exec("XYZabcdce"), 1027);
-assertEquals("X", res[483].exec("XYZabcde"), 1028);
-assertEquals(null, res[484].exec("Xabcdefg ", 1029));
-assertEquals(null, res[484].exec("Xx{1234} ", 1030));
-assertEquals(null, res[484].exec("Xx{1234}YZ", 1031));
-assertEquals(null, res[484].exec("Xx{1234}x{512} ", 1032));
-assertEquals(null, res[484].exec("Xx{1234}x{512}YZ", 1033));
-assertEquals(null, res[485].exec("Xabcdefg ", 1034));
-assertEquals(null, res[485].exec("Xx{1234} ", 1035));
-assertEquals(null, res[485].exec("Xx{1234}YZ", 1036));
-assertEquals(null, res[485].exec("Xx{1234}x{512} ", 1037));
-assertEquals("bcd", res[486].exec("bcd"), 1038);
-assertEquals("00}", res[486].exec("x{100}aYx{256}Z "), 1039);
-assertEquals("x{", res[487].exec("x{100}bc"), 1040);
-assertEquals("x{100}bcA", res[488].exec("x{100}bcAa"), 1041);
-assertEquals("x{", res[489].exec("x{100}bca"), 1042);
-assertEquals("bcd", res[490].exec("bcd"), 1043);
-assertEquals("00}", res[490].exec("x{100}aYx{256}Z "), 1044);
-assertEquals("x{", res[491].exec("x{100}bc"), 1045);
-assertEquals("x{100}bc", res[492].exec("x{100}bcAa"), 1046);
-assertEquals("x{", res[493].exec("x{100}bca"), 1047);
-assertEquals(null, res[493].exec("abcd", 1048));
-assertEquals(null, res[493].exec("abcd", 1049));
-assertEquals("x{", res[493].exec("x{100}x{100} "), 1050);
-assertEquals("x{", res[493].exec("x{100}x{100} "), 1051);
-assertEquals("x{", res[493].exec("x{100}x{100}x{100}x{100} "), 1052);
-assertEquals(null, res[493].exec("abce", 1053));
-assertEquals("x{", res[493].exec("x{100}x{100}x{100}x{100} "), 1054);
-assertEquals(null, res[493].exec("abcdx{100}x{100}x{100}x{100} ", 1055));
-assertEquals(null, res[493].exec("abcdx{100}x{100}x{100}x{100} ", 1056));
-assertEquals(null, res[493].exec("abcdx{100}x{100}x{100}x{100} ", 1057));
-assertEquals(null, res[493].exec("abcdx{100}x{100}x{100}XX", 1058));
-assertEquals(null, res[493].exec("abcdx{100}x{100}x{100}x{100}x{100}x{100}x{100}XX", 1059));
-assertEquals(null, res[493].exec("abcdx{100}x{100}x{100}x{100}x{100}x{100}x{100}XX", 1060));
-assertEquals("Xy", res[493].exec("Xyyyax{100}x{100}bXzzz"), 1061);
-assertEquals("X", res[496].exec("1X2"), 1062);
-assertEquals("x", res[496].exec("1x{100}2 "), 1063);
-assertEquals(">X", res[497].exec("> >X Y"), 1064);
-assertEquals(">x", res[497].exec("> >x{100} Y"), 1065);
-assertEquals("1", res[498].exec("x{100}3"), 1066);
-assertEquals(" ", res[499].exec("x{100} X"), 1067);
-assertEquals("abcd", res[500].exec("12abcd34"), 1068);
-assertEquals("*** Failers", res[500].exec("*** Failers"), 1069);
-assertEquals(" ", res[500].exec("1234 "), 1070);
-assertEquals("abc", res[501].exec("12abcd34"), 1071);
-assertEquals("ab", res[501].exec("12ab34"), 1072);
-assertEquals("***", res[501].exec("*** Failers "), 1073);
-assertEquals(null, res[501].exec("1234", 1074));
-assertEquals(" ", res[501].exec("12a34 "), 1075);
-assertEquals("ab", res[502].exec("12abcd34"), 1076);
-assertEquals("ab", res[502].exec("12ab34"), 1077);
-assertEquals("**", res[502].exec("*** Failers "), 1078);
-assertEquals(null, res[502].exec("1234", 1079));
-assertEquals(" ", res[502].exec("12a34 "), 1080);
-assertEquals("12", res[503].exec("12abcd34"), 1081);
-assertEquals(null, res[503].exec("*** Failers", 1082));
-assertEquals("12", res[504].exec("12abcd34"), 1083);
-assertEquals("123", res[504].exec("1234abcd"), 1084);
-assertEquals(null, res[504].exec("*** Failers ", 1085));
-assertEquals(null, res[504].exec("1.4 ", 1086));
-assertEquals("12", res[505].exec("12abcd34"), 1087);
-assertEquals("12", res[505].exec("1234abcd"), 1088);
-assertEquals(null, res[505].exec("*** Failers ", 1089));
-assertEquals(null, res[505].exec("1.4 ", 1090));
-assertEquals("12abcd34", res[506].exec("12abcd34"), 1091);
-assertEquals("***", res[506].exec("*** Failers"), 1092);
-assertEquals(null, res[506].exec(" ", 1093));
-assertEquals("12a", res[507].exec("12abcd34"), 1094);
-assertEquals("123", res[507].exec("1234abcd"), 1095);
-assertEquals("***", res[507].exec("*** Failers"), 1096);
-assertEquals(null, res[507].exec(" ", 1097));
-assertEquals("12", res[508].exec("12abcd34"), 1098);
-assertEquals("12", res[508].exec("1234abcd"), 1099);
-assertEquals("**", res[508].exec("*** Failers"), 1100);
-assertEquals(null, res[508].exec(" ", 1101));
-assertEquals("> <", res[509].exec("12> <34"), 1102);
-assertEquals(null, res[509].exec("*** Failers", 1103));
-assertEquals("> <", res[510].exec("ab> <cd"), 1104);
-assertEquals("> <", res[510].exec("ab> <ce"), 1105);
-assertEquals(null, res[510].exec("*** Failers", 1106));
-assertEquals(null, res[510].exec("ab> <cd ", 1107));
-assertEquals("> <", res[511].exec("ab> <cd"), 1108);
-assertEquals("> <", res[511].exec("ab> <ce"), 1109);
-assertEquals(null, res[511].exec("*** Failers", 1110));
-assertEquals(null, res[511].exec("ab> <cd ", 1111));
-assertEquals("12", res[512].exec("12 34"), 1112);
-assertEquals("Failers", res[512].exec("*** Failers"), 1113);
-assertEquals(null, res[512].exec("+++=*! ", 1114));
-assertEquals("ab", res[513].exec("ab cd"), 1115);
-assertEquals("abc", res[513].exec("abcd ce"), 1116);
-assertEquals("Fai", res[513].exec("*** Failers"), 1117);
-assertEquals(null, res[513].exec("a.b.c", 1118));
-assertEquals("ab", res[514].exec("ab cd"), 1119);
-assertEquals("ab", res[514].exec("abcd ce"), 1120);
-assertEquals("Fa", res[514].exec("*** Failers"), 1121);
-assertEquals(null, res[514].exec("a.b.c", 1122));
-assertEquals("====", res[515].exec("12====34"), 1123);
-assertEquals("*** ", res[515].exec("*** Failers"), 1124);
-assertEquals(" ", res[515].exec("abcd "), 1125);
-assertEquals("===", res[516].exec("ab====cd"), 1126);
-assertEquals("==", res[516].exec("ab==cd"), 1127);
-assertEquals("***", res[516].exec("*** Failers"), 1128);
-assertEquals(null, res[516].exec("a.b.c", 1129));
-assertEquals("==", res[517].exec("ab====cd"), 1130);
-assertEquals("==", res[517].exec("ab==cd"), 1131);
-assertEquals("**", res[517].exec("*** Failers"), 1132);
-assertEquals(null, res[517].exec("a.b.c", 1133));
-assertEquals(null, res[517].exec("x{100}", 1134));
-assertEquals(null, res[517].exec("Zx{100}", 1135));
-assertEquals(null, res[517].exec("x{100}Z", 1136));
-assertEquals("**", res[517].exec("*** Failers "), 1137);
-assertEquals(null, res[517].exec("Zx{100}", 1138));
-assertEquals(null, res[517].exec("x{100}", 1139));
-assertEquals(null, res[517].exec("x{100}Z", 1140));
-assertEquals("**", res[517].exec("*** Failers "), 1141);
-assertEquals(null, res[517].exec("abcx{200}X", 1142));
-assertEquals(null, res[517].exec("abcx{100}X ", 1143));
-assertEquals("**", res[517].exec("*** Failers"), 1144);
-assertEquals(" ", res[517].exec("X "), 1145);
-assertEquals(null, res[517].exec("abcx{200}X", 1146));
-assertEquals(null, res[517].exec("abcx{100}X ", 1147));
-assertEquals(null, res[517].exec("abQX ", 1148));
-assertEquals("**", res[517].exec("*** Failers"), 1149);
-assertEquals(" ", res[517].exec("X "), 1150);
-assertEquals(null, res[517].exec("abcx{100}x{200}x{100}X", 1151));
-assertEquals("**", res[517].exec("*** Failers"), 1152);
-assertEquals(null, res[517].exec("abcx{200}X", 1153));
-assertEquals(" ", res[517].exec("X "), 1154);
-assertEquals(null, res[517].exec("AX", 1155));
-assertEquals(null, res[517].exec("x{150}X", 1156));
-assertEquals(null, res[517].exec("x{500}X ", 1157));
-assertEquals("**", res[517].exec("*** Failers"), 1158);
-assertEquals(null, res[517].exec("x{100}X", 1159));
-assertEquals(" ", res[517].exec("x{200}X "), 1160);
-assertEquals(null, res[517].exec("AX", 1161));
-assertEquals(null, res[517].exec("x{150}X", 1162));
-assertEquals(null, res[517].exec("x{500}X ", 1163));
-assertEquals("**", res[517].exec("*** Failers"), 1164);
-assertEquals(null, res[517].exec("x{100}X", 1165));
-assertEquals(" ", res[517].exec("x{200}X "), 1166);
-assertEquals(null, res[517].exec("QX ", 1167));
-assertEquals(null, res[517].exec("AX", 1168));
-assertEquals(null, res[517].exec("x{500}X ", 1169));
-assertEquals("**", res[517].exec("*** Failers"), 1170);
-assertEquals(null, res[517].exec("x{100}X", 1171));
-assertEquals(null, res[517].exec("x{150}X", 1172));
-assertEquals(" ", res[517].exec("x{200}X "), 1173);
-assertEquals(null, res[518].exec("aXb", 1174));
-assertEquals(null, res[518].exec("a\nb", 1175));
-assertEquals(null, res[519].exec("aXb", 1176));
-assertEquals(null, res[519].exec("a\nb", 1177));
-assertEquals(null, res[519].exec("*** Failers ", 1178));
-assertEquals(null, res[519].exec("ax{100}b ", 1179));
-assertEquals(null, res[519].exec("z", 1180));
-assertEquals(null, res[519].exec("Z ", 1181));
-assertEquals(null, res[519].exec("x{100}", 1182));
-assertEquals(null, res[519].exec("*** Failers", 1183));
-assertEquals(null, res[519].exec("x{102}", 1184));
-assertEquals(null, res[519].exec("y ", 1185));
-assertEquals("\xff", res[520].exec(">\xff<"), 1186);
-assertEquals(null, res[521].exec(">x{ff}<", 1187));
-assertEquals("X", res[522].exec("XYZ"), 1188);
-assertEquals("X", res[523].exec("XYZ"), 1189);
-assertEquals("x", res[523].exec("x{123} "), 1190);
-assertEquals(",", res[528].exec("catac"), 1191);
-assertEquals(",", res[528].exec("ax{256}a "), 1192);
-assertEquals(",", res[528].exec("x{85}"), 1193);
-assertEquals(",", res[528].exec("\u1234 "), 1194);
-assertEquals(",", res[528].exec("\u1234 "), 1195);
-assertEquals(",", res[528].exec("abcdefg"), 1196);
-assertEquals(",", res[528].exec("ab"), 1197);
-assertEquals(",", res[528].exec("a "), 1198);
-assertEquals("Ax", res[529].exec("Ax{a3}BC"), 1199);
-assertEquals("Ax", res[530].exec("Ax{a3}BC"), 1200);
-assertEquals("}=", res[531].exec("+x{a3}== "), 1201);
-assertEquals("}=", res[532].exec("+x{a3}== "), 1202);
-assertEquals("x", res[533].exec("x{442}x{435}x{441}x{442}"), 1203);
-assertEquals("x", res[534].exec("x{442}x{435}x{441}x{442}"), 1204);
-assertEquals("x", res[535].exec("x{442}x{435}x{441}x{442}"), 1205);
-assertEquals("x", res[536].exec("x{442}x{435}x{441}x{442}"), 1206);
-assertEquals("{", res[537].exec("x{2442}x{2435}x{2441}x{2442}"), 1207);
-assertEquals("{", res[538].exec("x{2442}x{2435}x{2441}x{2442}"), 1208);
-assertEquals("abc\n\x0dx{442}x{435}x{441}x{442}xyz ", res[539].exec("abc\n\x0dx{442}x{435}x{441}x{442}xyz "), 1209);
-assertEquals("x{442}x{435}x{441}x{442}", res[539].exec("x{442}x{435}x{441}x{442}"), 1210);
-assertEquals("c d", res[540].exec("abc defx{442}x{443}xyz\npqr"), 1211);
-assertEquals("c d", res[541].exec("abc defx{442}x{443}xyz\npqr"), 1212);
-assertEquals(null, res[542].exec("+x{2442}", 1213));
-assertEquals(null, res[543].exec("+x{2442}", 1214));
-assertEquals(null, res[544].exec("Ax{442}", 1215));
-assertEquals(null, res[545].exec("Ax{442}", 1216));
-assertEquals(null, res[546].exec("Ax{442}", 1217));
-assertEquals(null, res[547].exec("Ax{442}", 1218));
-assertEquals(null, res[548].exec("\x19x{e01ff}", 1219));
-assertEquals(null, res[549].exec("Ax{422}", 1220));
-assertEquals(null, res[550].exec("x{19}x{e01ff}", 1221));
-assertEquals(null, res[551].exec("Ax{442}", 1222));
-assertEquals(null, res[552].exec("Ax{442}", 1223));
-assertEquals(null, res[553].exec("ax{442}", 1224));
-assertEquals(null, res[554].exec("+x{2442}", 1225));
-assertEquals(null, res[555].exec("Mx{442}", 1226));
-assertEquals("abc", res[556].exec("abc"), 1227);
-assertEquals("abc", res[557].exec("abc"), 1228);
-assertEquals("abc", res[558].exec("abc"), 1229);
-assertEquals("abc", res[559].exec("abc"), 1230);
-assertEquals(null, res[560].exec("x{100}ax{1234}bcd", 1231));
-assertEquals(null, res[562].exec("x{0041}x{2262}x{0391}x{002e}", 1232));
-assertEquals(null, res[562].exec("x{D55c}x{ad6d}x{C5B4} ", 1233));
-assertEquals(null, res[562].exec("x{65e5}x{672c}x{8a9e}", 1234));
-assertEquals("{861}X", res[563].exec("x{212ab}x{212ab}x{212ab}x{861}X"), 1235);
-assertEquals("x{2", res[564].exec("x{212ab}x{212ab}x{212ab}x{861}"), 1236);
-assertEquals("x{c", res[564].exec("x{c0}b"), 1237);
-assertEquals("ax{", res[564].exec("ax{c0}aaaa/ "), 1238);
-assertEquals("ax{", res[564].exec("ax{c0}aaaa/ "), 1239);
-assertEquals("ax{", res[564].exec("ax{c0}ax{c0}aaa/ "), 1240);
-assertEquals("ax{", res[564].exec("ax{c0}aaaa/ "), 1241);
-assertEquals("ax{", res[564].exec("ax{c0}ax{c0}aaa/ "), 1242);
-assertEquals("ax{", res[564].exec("ax{c0}aaaa/ "), 1243);
-assertEquals("ax{", res[564].exec("ax{c0}ax{c0}aaa/ "), 1244);
-assertEquals("Sho", res[564].exec("Should produce an error diagnostic"), 1245);
-assertEquals(null, res[565].exec("Xx{1234}", 1246));
-assertEquals(null, res[565].exec("X\nabc ", 1247));
-assertEquals("b", res[566].exec("bar"), 1248);
-assertEquals(null, res[566].exec("*** Failers", 1249));
-assertEquals(null, res[566].exec("c", 1250));
-assertEquals(null, res[566].exec("x{ff}", 1251));
-assertEquals(null, res[566].exec("x{100} ", 1252));
-assertEquals("c", res[567].exec("c"), 1253);
-assertEquals("x", res[567].exec("x{ff}"), 1254);
-assertEquals("x", res[567].exec("x{100} "), 1255);
-assertEquals("*", res[567].exec("*** Failers "), 1256);
-assertEquals(null, res[567].exec("aaa", 1257));
-assertEquals("x", res[568].exec("x{f1}"), 1258);
-assertEquals("x", res[568].exec("x{bf}"), 1259);
-assertEquals("x", res[568].exec("x{100}"), 1260);
-assertEquals("x", res[568].exec("x{1000} "), 1261);
-assertEquals("*", res[568].exec("*** Failers"), 1262);
-assertEquals("x", res[568].exec("x{c0} "), 1263);
-assertEquals("x", res[568].exec("x{f0} "), 1264);
-assertEquals("1", res[568].exec("1234"), 1265);
-assertEquals("\"", res[568].exec("\"1234\" "), 1266);
-assertEquals("x", res[568].exec("x{100}1234"), 1267);
-assertEquals("\"", res[568].exec("\"x{100}1234\" "), 1268);
-assertEquals("x", res[568].exec("x{100}x{100}12ab "), 1269);
-assertEquals("x", res[568].exec("x{100}x{100}\"12\" "), 1270);
-assertEquals("*", res[568].exec("*** Failers "), 1271);
-assertEquals("x", res[568].exec("x{100}x{100}abcd"), 1272);
-assertEquals("A", res[568].exec("A"), 1273);
-assertEquals("x", res[568].exec("x{100}"), 1274);
-assertEquals("Z", res[568].exec("Zx{100}"), 1275);
-assertEquals("x", res[568].exec("x{100}Z"), 1276);
-assertEquals("*", res[568].exec("*** Failers "), 1277);
-assertEquals("Z", res[568].exec("Zx{100}"), 1278);
-assertEquals("x", res[568].exec("x{100}"), 1279);
-assertEquals("x", res[568].exec("x{100}Z"), 1280);
-assertEquals("*", res[568].exec("*** Failers "), 1281);
-assertEquals("x", res[568].exec("x{100}"), 1282);
-assertEquals("x", res[568].exec("x{104}"), 1283);
-assertEquals("*", res[568].exec("*** Failers"), 1284);
-assertEquals("x", res[568].exec("x{105}"), 1285);
-assertEquals("x", res[568].exec("x{ff} "), 1286);
-assertEquals("x", res[568].exec("x{100}"), 1287);
-assertEquals("\u0100", res[568].exec("\u0100 "), 1288);
-assertEquals("\xff", res[569].exec(">\xff<"), 1289);
-assertEquals(null, res[570].exec(">x{ff}<", 1290));
-assertEquals("\xd6", res[572].exec("\xd6 # Matches without Study"), 1291);
-assertEquals("x", res[572].exec("x{d6}"), 1292);
-assertEquals("\xd6", res[572].exec("\xd6 <-- Same with Study"), 1293);
-assertEquals("x", res[572].exec("x{d6}"), 1294);
-assertEquals("\xd6", res[572].exec("\xd6 # Matches without Study"), 1295);
-assertEquals("x", res[572].exec("x{d6} "), 1296);
-assertEquals("\xd6", res[572].exec("\xd6 <-- Same with Study"), 1297);
-assertEquals("x", res[572].exec("x{d6} "), 1298);
-assertEquals("\ufffd", res[572].exec("\ufffd]"), 1299);
-assertEquals("\ufffd", res[572].exec("\ufffd"), 1300);
-assertEquals("\ufffd", res[572].exec("\ufffd\ufffd\ufffd"), 1301);
-assertEquals("\ufffd", res[572].exec("\ufffd\ufffd\ufffd?"), 1302);
-assertEquals(null, res[573].exec("\xc0\x80", 1303));
-assertEquals(null, res[573].exec("\xc1\x8f ", 1304));
-assertEquals(null, res[573].exec("\xe0\x9f\x80", 1305));
-assertEquals(null, res[573].exec("\xf0\x8f\x80\x80 ", 1306));
-assertEquals(null, res[573].exec("\xf8\x87\x80\x80\x80 ", 1307));
-assertEquals(null, res[573].exec("\xfc\x83\x80\x80\x80\x80", 1308));
-assertEquals(null, res[573].exec("\xfe\x80\x80\x80\x80\x80 ", 1309));
-assertEquals(null, res[573].exec("\xff\x80\x80\x80\x80\x80 ", 1310));
-assertEquals(null, res[573].exec("\xc3\x8f", 1311));
-assertEquals(null, res[573].exec("\xe0\xaf\x80", 1312));
-assertEquals(null, res[573].exec("\xe1\x80\x80", 1313));
-assertEquals(null, res[573].exec("\xf0\x9f\x80\x80 ", 1314));
-assertEquals(null, res[573].exec("\xf1\x8f\x80\x80 ", 1315));
-assertEquals(null, res[573].exec("\xf8\x88\x80\x80\x80 ", 1316));
-assertEquals(null, res[573].exec("\xf9\x87\x80\x80\x80 ", 1317));
-assertEquals(null, res[573].exec("\xfc\x84\x80\x80\x80\x80", 1318));
-assertEquals(null, res[573].exec("\xfd\x83\x80\x80\x80\x80", 1319));
-assertEquals(null, res[573].exec("?\xf8\x88\x80\x80\x80 ", 1320));
-assertEquals(null, res[573].exec("?\xf9\x87\x80\x80\x80 ", 1321));
-assertEquals(null, res[573].exec("?\xfc\x84\x80\x80\x80\x80", 1322));
-assertEquals(null, res[573].exec("?\xfd\x83\x80\x80\x80\x80", 1323));
-assertEquals(".", res[574].exec("A.B"), 1324);
-assertEquals("{", res[574].exec("Ax{100}B "), 1325);
-assertEquals("x", res[575].exec("x{100}X "), 1326);
-assertEquals("a", res[575].exec("ax{1234}b"), 1327);
-assertEquals(null, res[577].exec("AxxB ", 1328));
-assertEquals("abc1", res[578].exec("abc1 \nabc2 \x0babc3xx \x0cabc4 \x0dabc5xx \x0d\nabc6 x{0085}abc7 x{2028}abc8 x{2029}abc9 JUNK"), 1329);
-assertEquals("abc1", res[579].exec("abc1\n abc2\x0b abc3\x0c abc4\x0d abc5\x0d\n abc6x{0085} abc7x{2028} abc8x{2029} abc9"), 1330);
-assertEquals(null, res[580].exec("a\nb", 1331));
-assertEquals(null, res[580].exec("a\x0db", 1332));
-assertEquals(null, res[580].exec("a\x0d\nb", 1333));
-assertEquals(null, res[580].exec("a\x0bb", 1334));
-assertEquals(null, res[580].exec("a\x0cb", 1335));
-assertEquals(null, res[580].exec("ax{85}b ", 1336));
-assertEquals(null, res[580].exec("ax{2028}b ", 1337));
-assertEquals(null, res[580].exec("ax{2029}b ", 1338));
-assertEquals(null, res[580].exec("** Failers", 1339));
-assertEquals(null, res[580].exec("a\n\x0db ", 1340));
-assertEquals("ab", res[581].exec("ab"), 1341);
-assertEquals(null, res[581].exec("a\nb", 1342));
-assertEquals(null, res[581].exec("a\x0db", 1343));
-assertEquals(null, res[581].exec("a\x0d\nb", 1344));
-assertEquals(null, res[581].exec("a\x0bb", 1345));
-assertEquals(null, res[581].exec("a\x0cx{2028}x{2029}b", 1346));
-assertEquals(null, res[581].exec("ax{85}b ", 1347));
-assertEquals(null, res[581].exec("a\n\x0db ", 1348));
-assertEquals(null, res[581].exec("a\n\x0dx{85}\x0cb ", 1349));
-assertEquals(null, res[582].exec("a\nb", 1350));
-assertEquals(null, res[582].exec("a\x0db", 1351));
-assertEquals(null, res[582].exec("a\x0d\nb", 1352));
-assertEquals(null, res[582].exec("a\x0bb", 1353));
-assertEquals(null, res[582].exec("a\x0cx{2028}x{2029}b", 1354));
-assertEquals(null, res[582].exec("ax{85}b ", 1355));
-assertEquals(null, res[582].exec("a\n\x0db ", 1356));
-assertEquals(null, res[582].exec("a\n\x0dx{85}\x0cb ", 1357));
-assertEquals(null, res[582].exec("** Failers", 1358));
-assertEquals(null, res[582].exec("ab ", 1359));
-assertEquals(null, res[583].exec("a\nb", 1360));
-assertEquals(null, res[583].exec("a\n\x0db", 1361));
-assertEquals(null, res[583].exec("a\n\x0dx{85}b", 1362));
-assertEquals(null, res[583].exec("a\x0d\n\x0d\nb ", 1363));
-assertEquals(null, res[583].exec("a\x0d\n\x0d\n\x0d\nb ", 1364));
-assertEquals(null, res[583].exec("a\n\x0d\n\x0db", 1365));
-assertEquals(null, res[583].exec("a\n\n\x0d\nb ", 1366));
-assertEquals(null, res[583].exec("** Failers", 1367));
-assertEquals(null, res[583].exec("a\n\n\n\x0db", 1368));
-assertEquals(null, res[583].exec("a\x0d", 1369));
-assertEquals(null, res[584].exec("X X\n", 1370));
-assertEquals(null, res[584].exec("X\x09X\x0b", 1371));
-assertEquals(null, res[584].exec("** Failers", 1372));
-assertEquals(null, res[584].exec("x{a0} X\n ", 1373));
-assertEquals(null, res[585].exec("\x09 x{a0}X\n\x0b\x0c\x0d\n", 1374));
-assertEquals(null, res[585].exec("\x09 x{a0}\n\x0b\x0c\x0d\n", 1375));
-assertEquals(null, res[585].exec("\x09 x{a0}\n\x0b\x0c", 1376));
-assertEquals(null, res[585].exec("** Failers ", 1377));
-assertEquals(null, res[585].exec("\x09 x{a0}\n\x0b", 1378));
-assertEquals(null, res[585].exec(" ", 1379));
-assertEquals(null, res[586].exec("x{3001}x{3000}x{2030}x{2028}", 1380));
-assertEquals(null, res[586].exec("Xx{180e}Xx{85}", 1381));
-assertEquals(null, res[586].exec("** Failers", 1382));
-assertEquals(null, res[586].exec("x{2009} X\n ", 1383));
-assertEquals(null, res[587].exec("x{1680}x{180e}x{2007}Xx{2028}x{2029}\x0c\x0d\n", 1384));
-assertEquals(null, res[587].exec("\x09x{205f}x{a0}\nx{2029}\x0cx{2028}\n", 1385));
-assertEquals(null, res[587].exec("\x09 x{202f}\n\x0b\x0c", 1386));
-assertEquals(null, res[587].exec("** Failers ", 1387));
-assertEquals(null, res[587].exec("\x09x{200a}x{a0}x{2028}\x0b", 1388));
-assertEquals(null, res[587].exec(" ", 1389));
-assertEquals(null, res[588].exec(">x{1680}", 1390));
-assertEquals(null, res[589].exec(">x{1680}x{180e}x{2000}x{2003}x{200a}x{202f}x{205f}x{3000}<", 1391));
-assertEquals("x{1ec5} ", res[593].exec("x{1ec5} "), 1392);
-assertEquals(null, res[594].exec("x{0}x{d7ff}x{e000}x{10ffff}", 1393));
-assertEquals(null, res[594].exec("x{d800}", 1394));
-assertEquals(null, res[594].exec("x{d800}?", 1395));
-assertEquals(null, res[594].exec("x{da00}", 1396));
-assertEquals(null, res[594].exec("x{da00}?", 1397));
-assertEquals(null, res[594].exec("x{dfff}", 1398));
-assertEquals(null, res[594].exec("x{dfff}?", 1399));
-assertEquals(null, res[594].exec("x{110000} ", 1400));
-assertEquals(null, res[594].exec("x{110000}? ", 1401));
-assertEquals(null, res[594].exec("x{2000000} ", 1402));
-assertEquals(null, res[594].exec("x{2000000}? ", 1403));
-assertEquals(null, res[594].exec("x{7fffffff} ", 1404));
-assertEquals(null, res[594].exec("x{7fffffff}? ", 1405));
-assertEquals(null, res[595].exec("a\x0db", 1406));
-assertEquals(null, res[595].exec("a\nb", 1407));
-assertEquals(null, res[595].exec("a\x0d\nb", 1408));
-assertEquals(null, res[595].exec("** Failers", 1409));
-assertEquals(null, res[595].exec("ax{85}b", 1410));
-assertEquals(null, res[595].exec("a\x0bb ", 1411));
-assertEquals(null, res[596].exec("a\x0db", 1412));
-assertEquals(null, res[596].exec("a\nb", 1413));
-assertEquals(null, res[596].exec("a\x0d\nb", 1414));
-assertEquals(null, res[596].exec("ax{85}b", 1415));
-assertEquals(null, res[596].exec("a\x0bb ", 1416));
-assertEquals(null, res[596].exec("** Failers ", 1417));
-assertEquals(null, res[596].exec("ax{85}b<bsr_anycrlf>", 1418));
-assertEquals(null, res[596].exec("a\x0bb<bsr_anycrlf>", 1419));
-assertEquals(null, res[597].exec("a\x0db", 1420));
-assertEquals(null, res[597].exec("a\nb", 1421));
-assertEquals(null, res[597].exec("a\x0d\nb", 1422));
-assertEquals(null, res[597].exec("** Failers", 1423));
-assertEquals(null, res[597].exec("ax{85}b", 1424));
-assertEquals(null, res[597].exec("a\x0bb ", 1425));
-assertEquals(null, res[598].exec("a\x0db", 1426));
-assertEquals(null, res[598].exec("a\nb", 1427));
-assertEquals(null, res[598].exec("a\x0d\nb", 1428));
-assertEquals(null, res[598].exec("ax{85}b", 1429));
-assertEquals(null, res[598].exec("a\x0bb ", 1430));
-assertEquals(null, res[598].exec("** Failers ", 1431));
-assertEquals(null, res[598].exec("ax{85}b<bsr_anycrlf>", 1432));
-assertEquals(null, res[598].exec("a\x0bb<bsr_anycrlf>", 1433));
-assertEquals("QQQx{2029}ABCaXYZ=!bPQR", res[599].exec("QQQx{2029}ABCaXYZ=!bPQR"), 1434);
-assertEquals(null, res[599].exec("** Failers", 1435));
-assertEquals(null, res[599].exec("ax{2029}b", 1436));
-assertEquals(null, res[599].exec("a\xe2\x80\xa9b ", 1437));
-assertEquals(null, res[600].exec("ax{1234}b", 1438));
-assertEquals("a\nb", res[600].exec("a\nb "), 1439);
-assertEquals(null, res[600].exec("** Failers", 1440));
-assertEquals(null, res[600].exec("ab ", 1441));
-assertEquals("aXb", res[601].exec("aXb"), 1442);
-assertEquals("a\nX\nXx{1234}b", res[601].exec("a\nX\nXx{1234}b "), 1443);
-assertEquals(null, res[601].exec("** Failers", 1444));
-assertEquals(null, res[601].exec("ab ", 1445));
-assertEquals(null, res[601].exec("x{de}x{de}", 1446));
-assertEquals(null, res[601].exec("x{123} ", 1447));
-assertEquals("X", res[602].exec("Ax{1ec5}ABCXYZ"), 1448);
-assertEquals(null, res[604].exec("x{c0}x{30f}x{660}x{66c}x{f01}x{1680}<", 1449));
-assertEquals(null, res[604].exec("\npx{300}9!$ < ", 1450));
-assertEquals(null, res[604].exec("** Failers ", 1451));
-assertEquals(null, res[604].exec("apx{300}9!$ < ", 1452));
-assertEquals(null, res[605].exec("X", 1453));
-assertEquals(null, res[605].exec("** Failers ", 1454));
-assertEquals(null, res[605].exec("", 1455));
-assertEquals(null, res[606].exec("9", 1456));
-assertEquals(null, res[606].exec("** Failers ", 1457));
-assertEquals(null, res[606].exec("x{c0}", 1458));
-assertEquals(null, res[607].exec("X", 1459));
-assertEquals(null, res[607].exec("** Failers ", 1460));
-assertEquals(null, res[607].exec("x{30f}", 1461));
-assertEquals(null, res[608].exec("X", 1462));
-assertEquals(null, res[608].exec("** Failers ", 1463));
-assertEquals(null, res[608].exec("x{660}", 1464));
-assertEquals(null, res[609].exec("X", 1465));
-assertEquals(null, res[609].exec("** Failers ", 1466));
-assertEquals(null, res[609].exec("x{66c}", 1467));
-assertEquals(null, res[610].exec("X", 1468));
-assertEquals(null, res[610].exec("** Failers ", 1469));
-assertEquals(null, res[610].exec("x{f01}", 1470));
-assertEquals(null, res[611].exec("X", 1471));
-assertEquals(null, res[611].exec("** Failers ", 1472));
-assertEquals(null, res[611].exec("x{1680}", 1473));
-assertEquals(null, res[612].exec("x{017}", 1474));
-assertEquals(null, res[612].exec("x{09f} ", 1475));
-assertEquals(null, res[612].exec("** Failers", 1476));
-assertEquals(null, res[612].exec("x{0600} ", 1477));
-assertEquals(null, res[613].exec("x{601}", 1478));
-assertEquals(null, res[613].exec("** Failers", 1479));
-assertEquals(null, res[613].exec("x{09f} ", 1480));
-assertEquals(null, res[614].exec("x{e0000}", 1481));
-assertEquals(null, res[614].exec("** Failers", 1482));
-assertEquals(null, res[614].exec("x{09f} ", 1483));
-assertEquals(null, res[615].exec("x{f8ff}", 1484));
-assertEquals(null, res[615].exec("** Failers", 1485));
-assertEquals(null, res[615].exec("x{09f} ", 1486));
-assertEquals(null, res[616].exec("?x{dfff}", 1487));
-assertEquals(null, res[616].exec("** Failers", 1488));
-assertEquals(null, res[616].exec("x{09f} ", 1489));
-assertEquals(null, res[617].exec("a", 1490));
-assertEquals(null, res[617].exec("** Failers ", 1491));
-assertEquals(null, res[617].exec("Z", 1492));
-assertEquals(null, res[617].exec("x{e000} ", 1493));
-assertEquals(null, res[618].exec("x{2b0}", 1494));
-assertEquals(null, res[618].exec("** Failers", 1495));
-assertEquals(null, res[618].exec("a ", 1496));
-assertEquals(null, res[619].exec("x{1bb}", 1497));
-assertEquals(null, res[619].exec("x{3400}", 1498));
-assertEquals(null, res[619].exec("x{3401}", 1499));
-assertEquals(null, res[619].exec("x{4d00}", 1500));
-assertEquals(null, res[619].exec("x{4db4}", 1501));
-assertEquals(null, res[619].exec("x{4db5} ", 1502));
-assertEquals(null, res[619].exec("** Failers", 1503));
-assertEquals(null, res[619].exec("a ", 1504));
-assertEquals(null, res[619].exec("x{2b0}", 1505));
-assertEquals(null, res[619].exec("x{4db6} ", 1506));
-assertEquals(null, res[620].exec("x{1c5}", 1507));
-assertEquals(null, res[620].exec("** Failers", 1508));
-assertEquals(null, res[620].exec("a ", 1509));
-assertEquals(null, res[620].exec("x{2b0}", 1510));
-assertEquals(null, res[621].exec("A", 1511));
-assertEquals(null, res[621].exec("** Failers", 1512));
-assertEquals(null, res[621].exec("x{2b0}", 1513));
-assertEquals(null, res[622].exec("x{903}", 1514));
-assertEquals(null, res[622].exec("** Failers", 1515));
-assertEquals(null, res[622].exec("X", 1516));
-assertEquals(null, res[622].exec("x{300}", 1517));
-assertEquals(null, res[622].exec(" ", 1518));
-assertEquals(null, res[623].exec("x{488}", 1519));
-assertEquals(null, res[623].exec("** Failers", 1520));
-assertEquals(null, res[623].exec("X", 1521));
-assertEquals(null, res[623].exec("x{903}", 1522));
-assertEquals(null, res[623].exec("x{300}", 1523));
-assertEquals(null, res[624].exec("x{300}", 1524));
-assertEquals(null, res[624].exec("** Failers", 1525));
-assertEquals(null, res[624].exec("X", 1526));
-assertEquals(null, res[624].exec("x{903}", 1527));
-assertEquals(null, res[624].exec("0123456789x{660}x{661}x{662}x{663}x{664}x{665}x{666}x{667}x{668}x{669}x{66a}", 1528));
-assertEquals(null, res[624].exec("x{6f0}x{6f1}x{6f2}x{6f3}x{6f4}x{6f5}x{6f6}x{6f7}x{6f8}x{6f9}x{6fa}", 1529));
-assertEquals(null, res[624].exec("x{966}x{967}x{968}x{969}x{96a}x{96b}x{96c}x{96d}x{96e}x{96f}x{970}", 1530));
-assertEquals(null, res[624].exec("** Failers", 1531));
-assertEquals(null, res[624].exec("X", 1532));
-assertEquals(null, res[625].exec("x{16ee}", 1533));
-assertEquals(null, res[625].exec("** Failers", 1534));
-assertEquals(null, res[625].exec("X", 1535));
-assertEquals(null, res[625].exec("x{966}", 1536));
-assertEquals(null, res[626].exec("x{b2}", 1537));
-assertEquals(null, res[626].exec("x{b3}", 1538));
-assertEquals(null, res[626].exec("** Failers", 1539));
-assertEquals(null, res[626].exec("X", 1540));
-assertEquals(null, res[626].exec("x{16ee}", 1541));
-assertEquals(null, res[627].exec("_", 1542));
-assertEquals(null, res[627].exec("x{203f}", 1543));
-assertEquals(null, res[627].exec("** Failers", 1544));
-assertEquals(null, res[627].exec("X", 1545));
-assertEquals(null, res[627].exec("-", 1546));
-assertEquals(null, res[627].exec("x{58a}", 1547));
-assertEquals(null, res[628].exec("-", 1548));
-assertEquals(null, res[628].exec("x{58a}", 1549));
-assertEquals(null, res[628].exec("** Failers", 1550));
-assertEquals(null, res[628].exec("X", 1551));
-assertEquals(null, res[628].exec("x{203f}", 1552));
-assertEquals(null, res[629].exec(")", 1553));
-assertEquals(null, res[629].exec("]", 1554));
-assertEquals(null, res[629].exec("}", 1555));
-assertEquals(null, res[629].exec("x{f3b}", 1556));
-assertEquals(null, res[629].exec("** Failers", 1557));
-assertEquals(null, res[629].exec("X", 1558));
-assertEquals(null, res[629].exec("x{203f}", 1559));
-assertEquals(null, res[629].exec("(", 1560));
-assertEquals(null, res[629].exec("[", 1561));
-assertEquals(null, res[629].exec("{", 1562));
-assertEquals(null, res[629].exec("x{f3c}", 1563));
-assertEquals(null, res[630].exec("x{bb}", 1564));
-assertEquals(null, res[630].exec("x{2019}", 1565));
-assertEquals(null, res[630].exec("** Failers", 1566));
-assertEquals(null, res[630].exec("X", 1567));
-assertEquals(null, res[630].exec("x{203f}", 1568));
-assertEquals(null, res[631].exec("x{ab}", 1569));
-assertEquals(null, res[631].exec("x{2018}", 1570));
-assertEquals(null, res[631].exec("** Failers", 1571));
-assertEquals(null, res[631].exec("X", 1572));
-assertEquals(null, res[631].exec("x{203f}", 1573));
-assertEquals(null, res[632].exec("!", 1574));
-assertEquals(null, res[632].exec("x{37e}", 1575));
-assertEquals(null, res[632].exec("** Failers", 1576));
-assertEquals(null, res[632].exec("X", 1577));
-assertEquals(null, res[632].exec("x{203f}", 1578));
-assertEquals(null, res[633].exec("(", 1579));
-assertEquals(null, res[633].exec("[", 1580));
-assertEquals(null, res[633].exec("{", 1581));
-assertEquals(null, res[633].exec("x{f3c}", 1582));
-assertEquals(null, res[633].exec("** Failers", 1583));
-assertEquals(null, res[633].exec("X", 1584));
-assertEquals(null, res[633].exec(")", 1585));
-assertEquals(null, res[633].exec("]", 1586));
-assertEquals(null, res[633].exec("}", 1587));
-assertEquals(null, res[633].exec("x{f3b}", 1588));
-assertEquals(null, res[633].exec("$x{a2}x{a3}x{a4}x{a5}x{a6}", 1589));
-assertEquals(null, res[633].exec("x{9f2}", 1590));
-assertEquals(null, res[633].exec("** Failers", 1591));
-assertEquals(null, res[633].exec("X", 1592));
-assertEquals(null, res[633].exec("x{2c2}", 1593));
-assertEquals(null, res[634].exec("x{2c2}", 1594));
-assertEquals(null, res[634].exec("** Failers", 1595));
-assertEquals(null, res[634].exec("X", 1596));
-assertEquals(null, res[634].exec("x{9f2}", 1597));
-assertEquals(null, res[634].exec("+<|~x{ac}x{2044}", 1598));
-assertEquals(null, res[634].exec("** Failers", 1599));
-assertEquals(null, res[634].exec("X", 1600));
-assertEquals(null, res[634].exec("x{9f2}", 1601));
-assertEquals(null, res[635].exec("x{a6}", 1602));
-assertEquals(null, res[635].exec("x{482} ", 1603));
-assertEquals(null, res[635].exec("** Failers", 1604));
-assertEquals(null, res[635].exec("X", 1605));
-assertEquals(null, res[635].exec("x{9f2}", 1606));
-assertEquals(null, res[636].exec("x{2028}", 1607));
-assertEquals(null, res[636].exec("** Failers", 1608));
-assertEquals(null, res[636].exec("X", 1609));
-assertEquals(null, res[636].exec("x{2029}", 1610));
-assertEquals(null, res[637].exec("x{2029}", 1611));
-assertEquals(null, res[637].exec("** Failers", 1612));
-assertEquals(null, res[637].exec("X", 1613));
-assertEquals(null, res[637].exec("x{2028}", 1614));
-assertEquals(null, res[638].exec("\\ \\", 1615));
-assertEquals(null, res[638].exec("x{a0}", 1616));
-assertEquals(null, res[638].exec("x{1680}", 1617));
-assertEquals(null, res[638].exec("x{180e}", 1618));
-assertEquals(null, res[638].exec("x{2000}", 1619));
-assertEquals(null, res[638].exec("x{2001} ", 1620));
-assertEquals(null, res[638].exec("** Failers", 1621));
-assertEquals(null, res[638].exec("x{2028}", 1622));
-assertEquals(null, res[638].exec("x{200d} ", 1623));
-assertEquals(null, res[638].exec(" x{660}x{661}x{662}ABC", 1624));
-assertEquals(null, res[638].exec(" x{660}x{661}x{662}ABC", 1625));
-assertEquals(null, res[639].exec(" x{660}x{661}x{662}ABC", 1626));
-assertEquals(null, res[640].exec(" x{660}x{661}x{662}ABC", 1627));
-assertEquals(null, res[641].exec(" x{660}x{661}x{662}ABC", 1628));
-assertEquals(null, res[642].exec(" x{660}x{661}x{662}ABC", 1629));
-assertEquals(null, res[643].exec(" x{660}x{661}x{662}ABC", 1630));
-assertEquals(null, res[644].exec(" x{660}x{661}x{662}ABC", 1631));
-assertEquals(null, res[645].exec(" x{660}x{661}x{662}ABC", 1632));
-assertEquals(null, res[646].exec(" x{660}x{661}x{662}ABC", 1633));
-assertEquals(null, res[647].exec(" x{660}x{661}x{662}ABC", 1634));
-assertEquals(null, res[647].exec(" x{660}x{661}x{662}ABC", 1635));
-assertEquals(null, res[647].exec(" x{660}x{661}x{662}ABC", 1636));
-assertEquals(null, res[647].exec(" ** Failers", 1637));
-assertEquals(null, res[647].exec(" x{660}x{661}x{662}ABC", 1638));
-assertEquals(null, res[648].exec("A", 1639));
-assertEquals(null, res[648].exec("ax{10a0}B ", 1640));
-assertEquals(null, res[648].exec("** Failers ", 1641));
-assertEquals(null, res[648].exec("a", 1642));
-assertEquals(null, res[648].exec("x{1d00} ", 1643));
-assertEquals(null, res[649].exec("1234", 1644));
-assertEquals(null, res[649].exec("** Failers", 1645));
-assertEquals(null, res[649].exec("ABC ", 1646));
-assertEquals(null, res[650].exec("1234", 1647));
-assertEquals(null, res[650].exec("** Failers", 1648));
-assertEquals(null, res[650].exec("ABC ", 1649));
-assertEquals(null, res[650].exec("A2XYZ", 1650));
-assertEquals(null, res[650].exec("123A5XYZPQR", 1651));
-assertEquals(null, res[650].exec("ABAx{660}XYZpqr", 1652));
-assertEquals(null, res[650].exec("** Failers", 1653));
-assertEquals(null, res[650].exec("AXYZ", 1654));
-assertEquals(null, res[650].exec("XYZ ", 1655));
-assertEquals(null, res[650].exec("1XYZ", 1656));
-assertEquals(null, res[650].exec("AB=XYZ.. ", 1657));
-assertEquals(null, res[650].exec("XYZ ", 1658));
-assertEquals(null, res[650].exec("** Failers", 1659));
-assertEquals(null, res[650].exec("WXYZ ", 1660));
-assertEquals(null, res[655].exec("1234", 1661));
-assertEquals(null, res[655].exec("1234", 1662));
-assertEquals(null, res[655].exec("12-34", 1663));
-assertEquals("{", res[655].exec("12+x{661}-34 "), 1664);
-assertEquals(null, res[655].exec("** Failers", 1665));
-assertEquals("d", res[655].exec("abcd "), 1666);
-assertEquals("d", res[656].exec("abcd"), 1667);
-assertEquals(null, res[656].exec("** Failers", 1668));
-assertEquals(null, res[656].exec("1234", 1669));
-assertEquals(null, res[657].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 1670));
-assertEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[657].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 1671);
-assertEquals(" ", res[657].exec(" "), 1672);
-assertEquals(null, res[657].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 1673));
-assertEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[657].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 1674);
-assertEquals(null, res[658].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 1675));
-assertEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[658].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 1676);
-assertEquals(null, res[659].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 1677));
-assertEquals(null, res[659].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 1678));
-assertEquals(null, res[660].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 1679));
-assertEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[660].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 1680);
-assertEquals(null, res[661].exec("a", 1681));
-assertEquals(null, res[661].exec("A ", 1682));
-assertEquals(null, res[662].exec("a", 1683));
-assertEquals(null, res[662].exec("A ", 1684));
-assertEquals(null, res[663].exec("A", 1685));
-assertEquals(null, res[663].exec("aZ", 1686));
-assertEquals(null, res[663].exec("** Failers", 1687));
-assertEquals(null, res[663].exec("abc ", 1688));
-assertEquals(null, res[664].exec("A", 1689));
-assertEquals(null, res[664].exec("aZ", 1690));
-assertEquals(null, res[664].exec("** Failers", 1691));
-assertEquals(null, res[664].exec("abc ", 1692));
-assertEquals(null, res[665].exec("a", 1693));
-assertEquals(null, res[665].exec("Az", 1694));
-assertEquals(null, res[665].exec("** Failers", 1695));
-assertEquals(null, res[665].exec("ABC ", 1696));
-assertEquals(null, res[666].exec("a", 1697));
-assertEquals(null, res[666].exec("Az", 1698));
-assertEquals(null, res[666].exec("** Failers", 1699));
-assertEquals(null, res[666].exec("ABC ", 1700));
-assertEquals(null, res[666].exec("x{c0}", 1701));
-assertEquals(null, res[666].exec("x{e0} ", 1702));
-assertEquals(null, res[666].exec("x{c0}", 1703));
-assertEquals(null, res[666].exec("x{e0} ", 1704));
-assertEquals(null, res[666].exec("Ax{391}x{10427}x{ff3a}x{1fb0}", 1705));
-assertEquals(null, res[666].exec("** Failers", 1706));
-assertEquals(null, res[666].exec("ax{391}x{10427}x{ff3a}x{1fb0} ", 1707));
-assertEquals(null, res[666].exec("Ax{3b1}x{10427}x{ff3a}x{1fb0}", 1708));
-assertEquals(null, res[666].exec("Ax{391}x{1044F}x{ff3a}x{1fb0}", 1709));
-assertEquals(null, res[666].exec("Ax{391}x{10427}x{ff5a}x{1fb0}", 1710));
-assertEquals(null, res[666].exec("Ax{391}x{10427}x{ff3a}x{1fb8}", 1711));
-assertEquals(null, res[666].exec("Ax{391}x{10427}x{ff3a}x{1fb0}", 1712));
-assertEquals(null, res[666].exec("ax{391}x{10427}x{ff3a}x{1fb0} ", 1713));
-assertEquals(null, res[666].exec("Ax{3b1}x{10427}x{ff3a}x{1fb0}", 1714));
-assertEquals(null, res[666].exec("Ax{391}x{1044F}x{ff3a}x{1fb0}", 1715));
-assertEquals(null, res[666].exec("Ax{391}x{10427}x{ff5a}x{1fb0}", 1716));
-assertEquals(null, res[666].exec("Ax{391}x{10427}x{ff3a}x{1fb8}", 1717));
-assertEquals(null, res[666].exec("x{391}x{3b1}x{3b1}x{3b1}x{391}", 1718));
-assertEquals(null, res[666].exec("x{391}x{3b1}x{3b1}x{3b1}x{391}X", 1719));
-assertEquals(null, res[666].exec("x{391}x{3b1}x{3b1}x{3b1}x{391}X", 1720));
-assertEquals(null, res[666].exec("x{391}", 1721));
-assertEquals(null, res[666].exec("x{ff3a}", 1722));
-assertEquals(null, res[666].exec("x{3b1}", 1723));
-assertEquals(null, res[666].exec("x{ff5a} ", 1724));
-assertEquals(null, res[666].exec("x{c0}", 1725));
-assertEquals(null, res[666].exec("x{e0} ", 1726));
-assertEquals(null, res[666].exec("x{104}", 1727));
-assertEquals(null, res[666].exec("x{105}", 1728));
-assertEquals(null, res[666].exec("x{109} ", 1729));
-assertEquals(null, res[666].exec("** Failers", 1730));
-assertEquals(null, res[666].exec("x{100}", 1731));
-assertEquals(null, res[666].exec("x{10a} ", 1732));
-assertEquals(null, res[666].exec("Z", 1733));
-assertEquals(null, res[666].exec("z", 1734));
-assertEquals(null, res[666].exec("x{39c}", 1735));
-assertEquals(null, res[666].exec("x{178}", 1736));
-assertEquals(null, res[666].exec("|", 1737));
-assertEquals(null, res[666].exec("x{80}", 1738));
-assertEquals(null, res[666].exec("x{ff}", 1739));
-assertEquals(null, res[666].exec("x{100}", 1740));
-assertEquals(null, res[666].exec("x{101} ", 1741));
-assertEquals(null, res[666].exec("** Failers", 1742));
-assertEquals(null, res[666].exec("x{102}", 1743));
-assertEquals(null, res[666].exec("Y", 1744));
-assertEquals(null, res[666].exec("y ", 1745));
-assertEquals(null, res[667].exec("A", 1746));
-assertEquals(null, res[667].exec("Ax{300}BC ", 1747));
-assertEquals(null, res[667].exec("Ax{300}x{301}x{302}BC ", 1748));
-assertEquals(null, res[667].exec("*** Failers", 1749));
-assertEquals(null, res[667].exec("x{300} ", 1750));
-assertEquals("X", res[668].exec("X123"), 1751);
-assertEquals(null, res[668].exec("*** Failers", 1752));
-assertEquals(null, res[668].exec("AXYZ", 1753));
-assertEquals(null, res[669].exec("Ax{300}x{301}x{302}BCAx{300}x{301} ", 1754));
-assertEquals(null, res[669].exec("Ax{300}x{301}x{302}BCAx{300}x{301}C ", 1755));
-assertEquals(null, res[670].exec("Ax{300}x{301}x{302}BCAx{300}x{301} ", 1756));
-assertEquals(null, res[670].exec("Ax{300}x{301}x{302}BCAx{300}x{301}C ", 1757));
-assertEquals("A,,A", res[671].exec("Ax{300}x{301}x{302}BCAx{300}x{301} "), 1758);
-assertEquals("A,,A", res[671].exec("Ax{300}x{301}x{302}BCAx{300}x{301}C "), 1759);
-assertEquals("A,,A", res[672].exec("Ax{300}x{301}x{302}BCAx{300}x{301} "), 1760);
-assertEquals("A,,A", res[672].exec("Ax{300}x{301}x{302}BCAx{300}x{301}C "), 1761);
-assertEquals(null, res[673].exec("*** Failers", 1762));
-assertEquals(null, res[673].exec("Ax{300}x{301}x{302}", 1763));
-assertEquals(null, res[674].exec("Ax{300}x{301}Bx{300}X", 1764));
-assertEquals(null, res[674].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}", 1765));
-assertEquals(null, res[674].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}X", 1766));
-assertEquals(null, res[674].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}DAx{300}X", 1767));
-assertEquals(null, res[675].exec("Ax{300}x{301}Bx{300}X", 1768));
-assertEquals(null, res[675].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}", 1769));
-assertEquals(null, res[675].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}X", 1770));
-assertEquals(null, res[675].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}DAx{300}X", 1771));
-assertEquals(null, res[675].exec("x{2e81}x{3007}x{2f804}x{31a0}", 1772));
-assertEquals(null, res[675].exec("** Failers", 1773));
-assertEquals(null, res[675].exec("x{2e7f} ", 1774));
-assertEquals(null, res[675].exec("x{3105}", 1775));
-assertEquals(null, res[675].exec("** Failers", 1776));
-assertEquals(null, res[675].exec("x{30ff} ", 1777));
-assertEquals(null, res[676].exec("x{06e9}", 1778));
-assertEquals(null, res[676].exec("x{060b}", 1779));
-assertEquals(null, res[676].exec("** Failers", 1780));
-assertEquals(null, res[676].exec("Xx{06e9} ", 1781));
-assertEquals(null, res[677].exec("x{2f800}", 1782));
-assertEquals(null, res[677].exec("** Failers", 1783));
-assertEquals(null, res[677].exec("x{a014}", 1784));
-assertEquals(null, res[677].exec("x{a4c6} ", 1785));
-assertEquals(null, res[678].exec("AXYZ", 1786));
-assertEquals(null, res[678].exec("x{1234}XYZ ", 1787));
-assertEquals(null, res[678].exec("** Failers", 1788));
-assertEquals(null, res[678].exec("X ", 1789));
-assertEquals(null, res[679].exec("** Failers", 1790));
-assertEquals(null, res[679].exec("AX", 1791));
-assertEquals(null, res[680].exec("XYZ", 1792));
-assertEquals(null, res[680].exec("AXYZ", 1793));
-assertEquals(null, res[680].exec("x{1234}XYZ ", 1794));
-assertEquals(null, res[680].exec("** Failers", 1795));
-assertEquals(null, res[680].exec("ABXYZ ", 1796));
-assertEquals(null, res[681].exec("XYZ", 1797));
-assertEquals(null, res[681].exec("** Failers", 1798));
-assertEquals(null, res[681].exec("AXYZ", 1799));
-assertEquals(null, res[681].exec("x{1234}XYZ ", 1800));
-assertEquals(null, res[681].exec("ABXYZ ", 1801));
-assertEquals(null, res[681].exec("AXYZ", 1802));
-assertEquals(null, res[681].exec("x{1234}XYZ", 1803));
-assertEquals(null, res[681].exec("Ax{1234}XYZ", 1804));
-assertEquals(null, res[681].exec("** Failers", 1805));
-assertEquals(null, res[681].exec("XYZ", 1806));
-assertEquals(null, res[681].exec("** Failers", 1807));
-assertEquals(null, res[681].exec("AXYZ", 1808));
-assertEquals(null, res[681].exec("x{1234}XYZ", 1809));
-assertEquals(null, res[681].exec("Ax{1234}XYZ", 1810));
-assertEquals(null, res[681].exec("XYZ", 1811));
-assertEquals(null, res[682].exec("XYZ", 1812));
-assertEquals(null, res[682].exec("AXYZ", 1813));
-assertEquals(null, res[682].exec("x{1234}XYZ", 1814));
-assertEquals(null, res[682].exec("Ax{1234}XYZ", 1815));
-assertEquals(null, res[682].exec("** Failers", 1816));
-assertEquals(null, res[683].exec("XYZ", 1817));
-assertEquals(null, res[683].exec("** Failers", 1818));
-assertEquals(null, res[683].exec("AXYZ", 1819));
-assertEquals(null, res[683].exec("x{1234}XYZ", 1820));
-assertEquals(null, res[683].exec("Ax{1234}XYZ", 1821));
-assertEquals("AX", res[684].exec("AXYZ"), 1822);
-assertEquals(null, res[684].exec("x{1234}XYZ ", 1823));
-assertEquals(null, res[684].exec("** Failers", 1824));
-assertEquals(null, res[684].exec("X ", 1825));
-assertEquals(null, res[685].exec("** Failers", 1826));
-assertEquals("AX", res[685].exec("AX"), 1827);
-assertEquals("X", res[686].exec("XYZ"), 1828);
-assertEquals("AX", res[686].exec("AXYZ"), 1829);
-assertEquals(null, res[686].exec("x{1234}XYZ ", 1830));
-assertEquals(null, res[686].exec("** Failers", 1831));
-assertEquals(null, res[686].exec("ABXYZ ", 1832));
-assertEquals("X", res[687].exec("XYZ"), 1833);
-assertEquals(null, res[687].exec("** Failers", 1834));
-assertEquals("AX", res[687].exec("AXYZ"), 1835);
-assertEquals(null, res[687].exec("x{1234}XYZ ", 1836));
-assertEquals(null, res[687].exec("ABXYZ ", 1837));
-assertEquals("AX", res[688].exec("AXYZ"), 1838);
-assertEquals(null, res[688].exec("x{1234}XYZ", 1839));
-assertEquals(null, res[688].exec("Ax{1234}XYZ", 1840));
-assertEquals(null, res[688].exec("** Failers", 1841));
-assertEquals(null, res[688].exec("XYZ", 1842));
-assertEquals(null, res[689].exec("** Failers", 1843));
-assertEquals("AX", res[689].exec("AXYZ"), 1844);
-assertEquals(null, res[689].exec("x{1234}XYZ", 1845));
-assertEquals(null, res[689].exec("Ax{1234}XYZ", 1846));
-assertEquals(null, res[689].exec("XYZ", 1847));
-assertEquals("X", res[690].exec("XYZ"), 1848);
-assertEquals("AX", res[690].exec("AXYZ"), 1849);
-assertEquals(null, res[690].exec("x{1234}XYZ", 1850));
-assertEquals(null, res[690].exec("Ax{1234}XYZ", 1851));
-assertEquals(null, res[690].exec("** Failers", 1852));
-assertEquals("X", res[691].exec("XYZ"), 1853);
-assertEquals(null, res[691].exec("** Failers", 1854));
-assertEquals("AX", res[691].exec("AXYZ"), 1855);
-assertEquals(null, res[691].exec("x{1234}XYZ", 1856));
-assertEquals(null, res[691].exec("Ax{1234}XYZ", 1857));
-assertEquals(null, res[692].exec("abcdefgh", 1858));
-assertEquals(null, res[692].exec("x{1234}\n\x0dx{3456}xyz ", 1859));
-assertEquals(null, res[693].exec("abcdefgh", 1860));
-assertEquals(null, res[693].exec("x{1234}\n\x0dx{3456}xyz ", 1861));
-assertEquals(null, res[694].exec("** Failers", 1862));
-assertEquals(null, res[694].exec("abcdefgh", 1863));
-assertEquals(null, res[694].exec("x{1234}\n\x0dx{3456}xyz ", 1864));
-assertEquals(null, res[695].exec(" AXY", 1865));
-assertEquals(null, res[695].exec(" aXY", 1866));
-assertEquals(null, res[695].exec(" x{1c5}XY", 1867));
-assertEquals(null, res[695].exec(" ** Failers", 1868));
-assertEquals(null, res[695].exec(" x{1bb}XY", 1869));
-assertEquals(null, res[695].exec(" x{2b0}XY", 1870));
-assertEquals(null, res[695].exec(" !XY ", 1871));
-assertEquals(null, res[696].exec(" AXY", 1872));
-assertEquals(null, res[696].exec(" aXY", 1873));
-assertEquals(null, res[696].exec(" x{1c5}XY", 1874));
-assertEquals(null, res[696].exec(" ** Failers", 1875));
-assertEquals(null, res[696].exec(" x{1bb}XY", 1876));
-assertEquals(null, res[696].exec(" x{2b0}XY", 1877));
-assertEquals(null, res[696].exec(" !XY ", 1878));
-assertEquals(null, res[696].exec(" AXY", 1879));
-assertEquals(null, res[696].exec(" aXY", 1880));
-assertEquals(null, res[696].exec(" AbcdeXyz ", 1881));
-assertEquals(null, res[696].exec(" x{1c5}AbXY", 1882));
-assertEquals(null, res[696].exec(" abcDEXypqreXlmn ", 1883));
-assertEquals(null, res[696].exec(" ** Failers", 1884));
-assertEquals(null, res[696].exec(" x{1bb}XY", 1885));
-assertEquals(null, res[696].exec(" x{2b0}XY", 1886));
-assertEquals(null, res[696].exec(" !XY ", 1887));
-assertEquals(null, res[697].exec(" AXY", 1888));
-assertEquals(null, res[697].exec(" aXY", 1889));
-assertEquals(null, res[697].exec(" AbcdeXyz ", 1890));
-assertEquals(null, res[697].exec(" x{1c5}AbXY", 1891));
-assertEquals(null, res[697].exec(" abcDEXypqreXlmn ", 1892));
-assertEquals(null, res[697].exec(" ** Failers", 1893));
-assertEquals(null, res[697].exec(" x{1bb}XY", 1894));
-assertEquals(null, res[697].exec(" x{2b0}XY", 1895));
-assertEquals(null, res[697].exec(" !XY ", 1896));
-assertEquals(null, res[697].exec(" AXY", 1897));
-assertEquals(null, res[697].exec(" aXY", 1898));
-assertEquals(null, res[697].exec(" AbcdeXyz ", 1899));
-assertEquals(null, res[697].exec(" x{1c5}AbXY", 1900));
-assertEquals(null, res[697].exec(" abcDEXypqreXlmn ", 1901));
-assertEquals(null, res[697].exec(" ** Failers", 1902));
-assertEquals(null, res[697].exec(" x{1bb}XY", 1903));
-assertEquals(null, res[697].exec(" x{2b0}XY", 1904));
-assertEquals(null, res[697].exec(" !XY ", 1905));
-assertEquals(null, res[698].exec(" AXY", 1906));
-assertEquals(null, res[698].exec(" aXY", 1907));
-assertEquals(null, res[698].exec(" AbcdeXyz ", 1908));
-assertEquals(null, res[698].exec(" x{1c5}AbXY", 1909));
-assertEquals(null, res[698].exec(" abcDEXypqreXlmn ", 1910));
-assertEquals(null, res[698].exec(" ** Failers", 1911));
-assertEquals(null, res[698].exec(" x{1bb}XY", 1912));
-assertEquals(null, res[698].exec(" x{2b0}XY", 1913));
-assertEquals(null, res[698].exec(" !XY ", 1914));
-assertEquals(null, res[699].exec(" !XY", 1915));
-assertEquals(null, res[699].exec(" x{1bb}XY", 1916));
-assertEquals(null, res[699].exec(" x{2b0}XY", 1917));
-assertEquals(null, res[699].exec(" ** Failers", 1918));
-assertEquals(null, res[699].exec(" x{1c5}XY", 1919));
-assertEquals(null, res[699].exec(" AXY ", 1920));
-assertEquals(null, res[700].exec(" !XY", 1921));
-assertEquals(null, res[700].exec(" x{1bb}XY", 1922));
-assertEquals(null, res[700].exec(" x{2b0}XY", 1923));
-assertEquals(null, res[700].exec(" ** Failers", 1924));
-assertEquals(null, res[700].exec(" x{1c5}XY", 1925));
-assertEquals(null, res[700].exec(" AXY ", 1926));
-assertEquals(null, res[701].exec("\xa0!", 1927));
-assertEquals(null, res[701].exec("AabcabcYZ ", 1928));
-assertEquals("L=abcX,L=abc,abc", res[702].exec("L=abcX"), 1929);
-assertEquals(null, res[702].exec("x{c0}", 1930));
-assertEquals(null, res[702].exec("x{e0} ", 1931));
-assertEquals(null, res[702].exec("x{c0}", 1932));
-assertEquals(null, res[702].exec("x{e0} ", 1933));
-assertEquals(null, res[703].exec("x{1b00}x{12000}x{7c0}x{a840}x{10900}", 1934));
-assertEquals(null, res[706].exec("123abcdefg", 1935));
-assertEquals(null, res[706].exec("123abc\xc4\xc5zz", 1936));
-assertEquals(null, res[710].exec("A\x80", 1937));
-assertEquals(null, res[725].exec("x{60e} ", 1938));
-assertEquals(null, res[725].exec("x{656} ", 1939));
-assertEquals(null, res[725].exec("x{657} ", 1940));
-assertEquals(null, res[725].exec("x{658} ", 1941));
-assertEquals(null, res[725].exec("x{659} ", 1942));
-assertEquals(null, res[725].exec("x{65a} ", 1943));
-assertEquals(null, res[725].exec("x{65b} ", 1944));
-assertEquals(null, res[725].exec("x{65c} ", 1945));
-assertEquals(null, res[725].exec("x{65d} ", 1946));
-assertEquals(null, res[725].exec("x{65e} ", 1947));
-assertEquals(null, res[725].exec("x{66a} ", 1948));
-assertEquals(null, res[725].exec("x{6e9} ", 1949));
-assertEquals(null, res[725].exec("x{6ef}", 1950));
-assertEquals(null, res[725].exec("x{6fa} ", 1951));
-assertEquals(null, res[725].exec("** Failers", 1952));
-assertEquals(null, res[725].exec("x{600}", 1953));
-assertEquals(null, res[725].exec("x{650}", 1954));
-assertEquals(null, res[725].exec("x{651} ", 1955));
-assertEquals(null, res[725].exec("x{652} ", 1956));
-assertEquals(null, res[725].exec("x{653} ", 1957));
-assertEquals(null, res[725].exec("x{654} ", 1958));
-assertEquals(null, res[725].exec("x{655} ", 1959));
-assertEquals(null, res[725].exec("x{65f} ", 1960));
-assertEquals(null, res[726].exec("x{1d2b} ", 1961));
-assertEquals(null, res[727].exec("x{589}", 1962));
-assertEquals(null, res[727].exec("x{60c}", 1963));
-assertEquals(null, res[727].exec("x{61f} ", 1964));
-assertEquals(null, res[727].exec("x{964}", 1965));
-assertEquals(null, res[727].exec("x{965} ", 1966));
-assertEquals(null, res[727].exec("x{970} ", 1967));
-assertEquals(null, res[728].exec("x{64b}", 1968));
-assertEquals(null, res[728].exec("x{654}", 1969));
-assertEquals(null, res[728].exec("x{655}", 1970));
-assertEquals(null, res[728].exec("x{200c} ", 1971));
-assertEquals(null, res[728].exec("** Failers", 1972));
-assertEquals(null, res[728].exec("x{64a}", 1973));
-assertEquals(null, res[728].exec("x{656} ", 1974));
-assertEquals(null, res[729].exec("x{10450}", 1975));
-assertEquals(null, res[729].exec("x{1047f}", 1976));
-assertEquals(null, res[730].exec("x{10400}", 1977));
-assertEquals(null, res[730].exec("x{1044f}", 1978));
-assertEquals(null, res[731].exec("x{10480}", 1979));
-assertEquals(null, res[731].exec("x{1049d}", 1980));
-assertEquals(null, res[731].exec("x{104a0}", 1981));
-assertEquals(null, res[731].exec("x{104a9}", 1982));
-assertEquals(null, res[731].exec("** Failers", 1983));
-assertEquals(null, res[731].exec("x{1049e}", 1984));
-assertEquals(null, res[731].exec("x{1049f}", 1985));
-assertEquals(null, res[731].exec("x{104aa} ", 1986));
-assertEquals(null, res[731].exec("\xe2\x80\xa8\xe2\x80\xa8", 1987));
-assertEquals(null, res[731].exec("x{2028}x{2028}x{2028}", 1988));
-assertEquals(null, res[732].exec("x{c0}x{e0}x{116}x{117}", 1989));
-assertEquals(null, res[732].exec("x{c0}x{e0}x{116}x{117}", 1990));
-assertEquals(null, res[733].exec("x{102A4}x{AA52}x{A91D}x{1C46}x{10283}x{1092E}x{1C6B}x{A93B}x{A8BF}x{1BA0}x{A50A}====", 1991));
-assertEquals(null, res[733].exec("x{a77d}x{1d79}", 1992));
-assertEquals(null, res[733].exec("x{1d79}x{a77d} ", 1993));
-assertEquals(null, res[733].exec("x{a77d}x{1d79}", 1994));
-assertEquals(null, res[733].exec("** Failers ", 1995));
-assertEquals(null, res[733].exec("x{1d79}x{a77d} ", 1996));
-assertEquals("AA,A", res[734].exec("AA"), 1997);
-assertEquals("Aa,A", res[734].exec("Aa"), 1998);
-assertEquals("aa,a", res[734].exec("aa"), 1999);
-assertEquals("aA,a", res[734].exec("aA"), 2000);
-assertEquals(null, res[734].exec("x{de}x{de}", 2001));
-assertEquals(null, res[734].exec("x{de}x{fe}", 2002));
-assertEquals(null, res[734].exec("x{fe}x{fe}", 2003));
-assertEquals(null, res[734].exec("x{fe}x{de}", 2004));
-assertEquals(null, res[734].exec("x{10a}x{10a}", 2005));
-assertEquals(null, res[734].exec("x{10a}x{10b}", 2006));
-assertEquals(null, res[734].exec("x{10b}x{10b}", 2007));
-assertEquals(null, res[734].exec("x{10b}x{10a}", 2008));
-assertEquals("abc", res[736].exec("abc"), 2009);
-assertEquals("abc", res[737].exec("abc"), 2010);
-assertEquals("abbbbc", res[737].exec("abbbbc"), 2011);
-assertEquals("ac", res[737].exec("ac"), 2012);
-assertEquals("abc", res[738].exec("abc"), 2013);
-assertEquals("abbbbbbc", res[738].exec("abbbbbbc"), 2014);
-assertEquals(null, res[738].exec("*** Failers ", 2015));
-assertEquals(null, res[738].exec("ac", 2016));
-assertEquals(null, res[738].exec("ab", 2017));
-assertEquals("a", res[739].exec("a"), 2018);
-assertEquals("aaaaaaaaaaaaaaaaa", res[739].exec("aaaaaaaaaaaaaaaaa"), 2019);
-assertEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[739].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa "), 2020);
-assertEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[739].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaF "), 2021);
-assertEquals("a,a", res[740].exec("a"), 2022);
-assertEquals("a,a", res[740].exec("abcd"), 2023);
-assertEquals("a,a", res[740].exec("african"), 2024);
-assertEquals("abc", res[741].exec("abcdef"), 2025);
-assertEquals(null, res[741].exec("*** Failers", 2026));
-assertEquals(null, res[741].exec("xyzabc", 2027));
-assertEquals(null, res[741].exec("xyz\nabc ", 2028));
-assertEquals("abc", res[742].exec("abcdef"), 2029);
-assertEquals("abc", res[742].exec("xyz\nabc "), 2030);
-assertEquals(null, res[742].exec("*** Failers", 2031));
-assertEquals(null, res[742].exec("xyzabc", 2032));
-assertEquals(null, res[743].exec("abcdef", 2033));
-assertEquals(null, res[743].exec("*** Failers", 2034));
-assertEquals(null, res[743].exec("xyzabc", 2035));
-assertEquals(null, res[743].exec("xyz\nabc ", 2036));
-assertEquals(null, res[744].exec("abcdef", 2037));
-assertEquals(null, res[744].exec("*** Failers", 2038));
-assertEquals(null, res[744].exec("xyzabc", 2039));
-assertEquals(null, res[744].exec("xyz\nabc ", 2040));
-assertEquals(null, res[745].exec("abcdef", 2041));
-assertEquals(null, res[745].exec("xyzabc>3", 2042));
-assertEquals(null, res[745].exec("*** Failers", 2043));
-assertEquals(null, res[745].exec("xyzabc ", 2044));
-assertEquals(null, res[745].exec("xyzabc>2 ", 2045));
-assertEquals("x9yzz", res[746].exec("x9yzz"), 2046);
-assertEquals("x0y+z", res[746].exec("x0y+z"), 2047);
-assertEquals(null, res[746].exec("*** Failers", 2048));
-assertEquals(null, res[746].exec("xyz", 2049));
-assertEquals(null, res[746].exec("xxy0z ", 2050));
-assertEquals("x yzz", res[747].exec("x yzz"), 2051);
-assertEquals("x y+z", res[747].exec("x y+z"), 2052);
-assertEquals(null, res[747].exec("*** Failers", 2053));
-assertEquals(null, res[747].exec("xyz", 2054));
-assertEquals(null, res[747].exec("xxyyz", 2055));
-assertEquals("xxy+z", res[748].exec("xxy+z"), 2056);
-assertEquals(null, res[748].exec("*** Failers", 2057));
-assertEquals(null, res[748].exec("xxy0z", 2058));
-assertEquals(null, res[748].exec("x+y+z ", 2059));
-assertEquals("x+y", res[749].exec("x+y"), 2060);
-assertEquals("x-y", res[749].exec("x-y"), 2061);
-assertEquals(null, res[749].exec("*** Failers", 2062));
-assertEquals(null, res[749].exec("x\ny", 2063));
-assertEquals("x+y", res[750].exec("x+y"), 2064);
-assertEquals("x-y", res[750].exec("x-y"), 2065);
-assertEquals(null, res[750].exec("x\ny", 2066));
-assertEquals(null, res[750].exec("a+bc+dp+q", 2067));
-assertEquals(null, res[750].exec("a+bc\ndp+q", 2068));
-assertEquals(null, res[750].exec("x\nyp+q ", 2069));
-assertEquals(null, res[750].exec("*** Failers ", 2070));
-assertEquals(null, res[750].exec("a\nbc\ndp+q", 2071));
-assertEquals(null, res[750].exec("a+bc\ndp\nq", 2072));
-assertEquals(null, res[750].exec("x\nyp\nq ", 2073));
-assertEquals(null, res[751].exec("ba0", 2074));
-assertEquals(null, res[751].exec("*** Failers", 2075));
-assertEquals(null, res[751].exec("ba0\n", 2076));
-assertEquals(null, res[751].exec("ba0\ncd ", 2077));
-assertEquals(null, res[752].exec("ba0", 2078));
-assertEquals(null, res[752].exec("*** Failers", 2079));
-assertEquals(null, res[752].exec("ba0\n", 2080));
-assertEquals(null, res[752].exec("ba0\ncd ", 2081));
-assertEquals(null, res[753].exec("ba0", 2082));
-assertEquals(null, res[753].exec("ba0\n", 2083));
-assertEquals(null, res[753].exec("*** Failers", 2084));
-assertEquals(null, res[753].exec("ba0\ncd ", 2085));
-assertEquals(null, res[754].exec("ba0", 2086));
-assertEquals(null, res[754].exec("ba0\n", 2087));
-assertEquals(null, res[754].exec("*** Failers", 2088));
-assertEquals(null, res[754].exec("ba0\ncd ", 2089));
-assertEquals("a0", res[755].exec("ba0"), 2090);
-assertEquals(null, res[755].exec("ba0\n", 2091));
-assertEquals(null, res[755].exec("*** Failers", 2092));
-assertEquals(null, res[755].exec("ba0\ncd ", 2093));
-assertEquals("a0", res[756].exec("ba0"), 2094);
-assertEquals("a0", res[756].exec("ba0\n"), 2095);
-assertEquals("a0", res[756].exec("ba0\ncd "), 2096);
-assertEquals(null, res[756].exec("*** Failers", 2097));
-assertEquals("abc", res[757].exec("abc"), 2098);
-assertEquals("aBc", res[757].exec("aBc"), 2099);
-assertEquals("ABC", res[757].exec("ABC"), 2100);
-assertEquals("b", res[758].exec("abcd"), 2101);
-assertEquals("abz", res[759].exec("abz"), 2102);
-assertEquals("abb", res[759].exec("abbz"), 2103);
-assertEquals("az", res[759].exec("azz "), 2104);
-assertEquals("yz", res[760].exec("ayzq"), 2105);
-assertEquals("xyz", res[760].exec("axyzq"), 2106);
-assertEquals("xxyz", res[760].exec("axxyz"), 2107);
-assertEquals("xxxyz", res[760].exec("axxxyzq"), 2108);
-assertEquals("xxxyz", res[760].exec("axxxxyzq"), 2109);
-assertEquals(null, res[760].exec("*** Failers", 2110));
-assertEquals(null, res[760].exec("ax", 2111));
-assertEquals(null, res[760].exec("axx ", 2112));
-assertEquals(null, res[760].exec(" ", 2113));
-assertEquals("xxxyz", res[761].exec("axxxyzq"), 2114);
-assertEquals("xxxyz", res[761].exec("axxxxyzq"), 2115);
-assertEquals(null, res[761].exec("*** Failers", 2116));
-assertEquals(null, res[761].exec("ax", 2117));
-assertEquals(null, res[761].exec("axx ", 2118));
-assertEquals(null, res[761].exec("ayzq", 2119));
-assertEquals(null, res[761].exec("axyzq", 2120));
-assertEquals(null, res[761].exec("axxyz", 2121));
-assertEquals(null, res[761].exec(" ", 2122));
-assertEquals("xxyz", res[762].exec("axxyz"), 2123);
-assertEquals("xxxyz", res[762].exec("axxxyzq"), 2124);
-assertEquals("xxxyz", res[762].exec("axxxxyzq"), 2125);
-assertEquals(null, res[762].exec("*** Failers", 2126));
-assertEquals(null, res[762].exec("ax", 2127));
-assertEquals(null, res[762].exec("axx ", 2128));
-assertEquals(null, res[762].exec("ayzq", 2129));
-assertEquals(null, res[762].exec("axyzq", 2130));
-assertEquals(null, res[762].exec(" ", 2131));
-assertEquals("b", res[763].exec("bac"), 2132);
-assertEquals("bcdef", res[763].exec("bcdefax"), 2133);
-assertEquals("*** F", res[763].exec("*** Failers"), 2134);
-assertEquals(" ", res[763].exec("aaaaa "), 2135);
-assertEquals("b", res[764].exec("bac"), 2136);
-assertEquals("bcdef", res[764].exec("bcdefax"), 2137);
-assertEquals("*** F", res[764].exec("*** Failers"), 2138);
-assertEquals("", res[764].exec("aaaaa "), 2139);
-assertEquals("xyz", res[765].exec("xyz"), 2140);
-assertEquals("wxyz", res[765].exec("awxyza"), 2141);
-assertEquals("bcdef", res[765].exec("abcdefa"), 2142);
-assertEquals("bcdef", res[765].exec("abcdefghijk"), 2143);
-assertEquals("*** F", res[765].exec("*** Failers"), 2144);
-assertEquals(null, res[765].exec("axya", 2145));
-assertEquals(null, res[765].exec("axa", 2146));
-assertEquals(" ", res[765].exec("aaaaa "), 2147);
-assertEquals("1234", res[766].exec("1234b567"), 2148);
-assertEquals("", res[766].exec("xyz"), 2149);
-assertEquals("a", res[767].exec("a1234b567"), 2150);
-assertEquals("xyz", res[767].exec("xyz"), 2151);
-assertEquals(" ", res[767].exec(" "), 2152);
-assertEquals("1234", res[768].exec("ab1234c56"), 2153);
-assertEquals(null, res[768].exec("*** Failers", 2154));
-assertEquals(null, res[768].exec("xyz", 2155));
-assertEquals("ab", res[769].exec("ab123c56"), 2156);
-assertEquals("*** Failers", res[769].exec("*** Failers"), 2157);
-assertEquals(null, res[769].exec("789", 2158));
-assertEquals("5A", res[770].exec("045ABC"), 2159);
-assertEquals("A", res[770].exec("ABC"), 2160);
-assertEquals(null, res[770].exec("*** Failers", 2161));
-assertEquals(null, res[770].exec("XYZ", 2162));
-assertEquals("A", res[771].exec("ABC"), 2163);
-assertEquals("BA", res[771].exec("BAC"), 2164);
-assertEquals("A", res[771].exec("9ABC "), 2165);
-assertEquals(null, res[771].exec("*** Failers", 2166));
-assertEquals("aaaa", res[772].exec("aaaa"), 2167);
-assertEquals("xyz", res[773].exec("xyz"), 2168);
-assertEquals("ggggggggxyz", res[773].exec("ggggggggxyz"), 2169);
-assertEquals("abcdxyz", res[774].exec("abcdxyz"), 2170);
-assertEquals("axyz", res[774].exec("axyz"), 2171);
-assertEquals(null, res[774].exec("*** Failers", 2172));
-assertEquals(null, res[774].exec("xyz", 2173));
-assertEquals("xyz", res[775].exec("xyz"), 2174);
-assertEquals("cxyz", res[775].exec("cxyz "), 2175);
-assertEquals("12X", res[776].exec("12X"), 2176);
-assertEquals("123X", res[776].exec("123X"), 2177);
-assertEquals(null, res[776].exec("*** Failers", 2178));
-assertEquals(null, res[776].exec("X", 2179));
-assertEquals(null, res[776].exec("1X", 2180));
-assertEquals(null, res[776].exec("1234X ", 2181));
-assertEquals("a4", res[777].exec("a45"), 2182);
-assertEquals("b9", res[777].exec("b93"), 2183);
-assertEquals("c9", res[777].exec("c99z"), 2184);
-assertEquals("d0", res[777].exec("d04"), 2185);
-assertEquals(null, res[777].exec("*** Failers", 2186));
-assertEquals(null, res[777].exec("e45", 2187));
-assertEquals(null, res[777].exec("abcd ", 2188));
-assertEquals(null, res[777].exec("abcd1234", 2189));
-assertEquals(null, res[777].exec("1234 ", 2190));
-assertEquals("a4", res[778].exec("a45"), 2191);
-assertEquals("b9", res[778].exec("b93"), 2192);
-assertEquals("c9", res[778].exec("c99z"), 2193);
-assertEquals("d0", res[778].exec("d04"), 2194);
-assertEquals("abcd1", res[778].exec("abcd1234"), 2195);
-assertEquals("1", res[778].exec("1234 "), 2196);
-assertEquals(null, res[778].exec("*** Failers", 2197));
-assertEquals(null, res[778].exec("e45", 2198));
-assertEquals(null, res[778].exec("abcd ", 2199));
-assertEquals("a4", res[779].exec("a45"), 2200);
-assertEquals("b9", res[779].exec("b93"), 2201);
-assertEquals("c9", res[779].exec("c99z"), 2202);
-assertEquals("d0", res[779].exec("d04"), 2203);
-assertEquals("abcd1", res[779].exec("abcd1234"), 2204);
-assertEquals(null, res[779].exec("*** Failers", 2205));
-assertEquals(null, res[779].exec("1234 ", 2206));
-assertEquals(null, res[779].exec("e45", 2207));
-assertEquals(null, res[779].exec("abcd ", 2208));
-assertEquals("aX", res[780].exec("aX"), 2209);
-assertEquals("aaX", res[780].exec("aaX "), 2210);
-assertEquals("a4", res[781].exec("a45"), 2211);
-assertEquals("b9", res[781].exec("b93"), 2212);
-assertEquals("c9", res[781].exec("c99z"), 2213);
-assertEquals("d0", res[781].exec("d04"), 2214);
-assertEquals("1", res[781].exec("1234 "), 2215);
-assertEquals(null, res[781].exec("*** Failers", 2216));
-assertEquals(null, res[781].exec("abcd1234", 2217));
-assertEquals(null, res[781].exec("e45", 2218));
-assertEquals("ab4", res[782].exec("ab45"), 2219);
-assertEquals("bcd9", res[782].exec("bcd93"), 2220);
-assertEquals(null, res[782].exec("*** Failers", 2221));
-assertEquals(null, res[782].exec("1234 ", 2222));
-assertEquals(null, res[782].exec("a36 ", 2223));
-assertEquals(null, res[782].exec("abcd1234", 2224));
-assertEquals(null, res[782].exec("ee45", 2225));
-assertEquals("abc4,abc", res[783].exec("abc45"), 2226);
-assertEquals("abcabcabc4,abc", res[783].exec("abcabcabc45"), 2227);
-assertEquals("4,", res[783].exec("42xyz "), 2228);
-assertEquals(null, res[783].exec("*** Failers", 2229));
-assertEquals("abc4,abc", res[784].exec("abc45"), 2230);
-assertEquals("abcabcabc4,abc", res[784].exec("abcabcabc45"), 2231);
-assertEquals(null, res[784].exec("*** Failers", 2232));
-assertEquals(null, res[784].exec("42xyz ", 2233));
-assertEquals("abc4,abc", res[785].exec("abc45"), 2234);
-assertEquals("4,", res[785].exec("42xyz "), 2235);
-assertEquals(null, res[785].exec("*** Failers", 2236));
-assertEquals(null, res[785].exec("abcabcabc45", 2237));
-assertEquals("abcabc4,abc", res[786].exec("abcabc45"), 2238);
-assertEquals("abcabcabc4,abc", res[786].exec("abcabcabc45"), 2239);
-assertEquals(null, res[786].exec("*** Failers", 2240));
-assertEquals(null, res[786].exec("abcabcabcabc45", 2241));
-assertEquals(null, res[786].exec("abc45", 2242));
-assertEquals(null, res[786].exec("42xyz ", 2243));
-assertEquals(null, res[786].exec("1abc2abc3456", 2244));
-assertEquals(null, res[786].exec("1abc2xyz3456 ", 2245));
-assertEquals("ab=ab,ab,ab", res[787].exec("ab=ab"), 2246);
-assertEquals("ab=ab,ab,ab", res[787].exec("ab=ab"), 2247);
-assertEquals(null, res[787].exec("abc", 2248));
-assertEquals(null, res[787].exec("a(b)c", 2249));
-assertEquals(null, res[787].exec("a(b(c))d ", 2250));
-assertEquals(null, res[787].exec("*** Failers)", 2251));
-assertEquals(null, res[787].exec("a(b(c)d ", 2252));
-assertEquals(null, res[787].exec(">abc>123<xyz<", 2253));
-assertEquals(null, res[787].exec(">abc>1(2)3<xyz<", 2254));
-assertEquals(null, res[787].exec(">abc>(1(2)3)<xyz<", 2255));
-assertEquals(null, res[787].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9876", 2256));
-assertEquals(null, res[787].exec("*** Failers ", 2257));
-assertEquals(null, res[787].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 2258));
-assertEquals(null, res[787].exec("<>", 2259));
-assertEquals(null, res[787].exec("<abcd>", 2260));
-assertEquals(null, res[787].exec("<abc <123> hij>", 2261));
-assertEquals(null, res[787].exec("<abc <def> hij>", 2262));
-assertEquals(null, res[787].exec("<abc<>def> ", 2263));
-assertEquals(null, res[787].exec("<abc<> ", 2264));
-assertEquals(null, res[787].exec("*** Failers", 2265));
-assertEquals(null, res[787].exec("<abc", 2266));
-assertEquals(null, res[787].exec("abc: ", 2267));
-assertEquals(null, res[787].exec("12 ", 2268));
-assertEquals(null, res[787].exec("*** Failers ", 2269));
-assertEquals(null, res[787].exec("123 ", 2270));
-assertEquals(null, res[787].exec("xyz ", 2271));
-assertEquals(null, res[787].exec(" ", 2272));
-assertEquals(null, res[787].exec("abc: ", 2273));
-assertEquals(null, res[787].exec("12 ", 2274));
-assertEquals(null, res[787].exec("*** Failers", 2275));
-assertEquals(null, res[787].exec("123", 2276));
-assertEquals(null, res[787].exec("xyz ", 2277));
-assertEquals(null, res[788].exec("abcde: ", 2278));
-assertEquals(null, res[788].exec("*** Failers ", 2279));
-assertEquals(null, res[788].exec("abc.. ", 2280));
-assertEquals(null, res[788].exec("123 ", 2281));
-assertEquals(null, res[788].exec("vwxyz ", 2282));
-assertEquals(null, res[788].exec(" ", 2283));
-assertEquals(null, res[789].exec("12 ", 2284));
-assertEquals(null, res[789].exec("*** Failers", 2285));
-assertEquals(null, res[789].exec("abcde:", 2286));
-assertEquals(null, res[789].exec("abc.. ", 2287));
-assertEquals(null, res[789].exec("123", 2288));
-assertEquals(null, res[789].exec("vwxyz ", 2289));
-assertEquals(null, res[789].exec("abc12345", 2290));
-assertEquals(null, res[789].exec("wxy123z", 2291));
-assertEquals(null, res[789].exec("*** Failers", 2292));
-assertEquals(null, res[789].exec("123abc", 2293));
-assertEquals(null, res[789].exec("123abc", 2294));
-assertEquals(null, res[789].exec("mno123456 ", 2295));
-assertEquals(null, res[789].exec("*** Failers", 2296));
-assertEquals(null, res[789].exec("abc12345", 2297));
-assertEquals(null, res[789].exec("wxy123z", 2298));
-assertEquals(null, res[789].exec("abcxyz", 2299));
-assertEquals(null, res[789].exec("123abcxyz999 ", 2300));
-assertEquals("abc", res[791].exec("abcdef"), 2301);
-assertEquals(null, res[791].exec("*** Failers", 2302));
-assertEquals("abc", res[791].exec("abcdefB "), 2303);
-assertEquals(",", res[792].exec("bcd"), 2304);
-assertEquals("aaa,aaa", res[792].exec("aaabcd"), 2305);
-assertEquals(",", res[792].exec("xyz"), 2306);
-assertEquals(",", res[792].exec("xyzN "), 2307);
-assertEquals(",", res[792].exec("*** Failers"), 2308);
-assertEquals(",", res[792].exec("bcdN "), 2309);
-assertEquals("xyz", res[793].exec("xyz"), 2310);
-assertEquals(null, res[793].exec("xyz\n", 2311));
-assertEquals(null, res[793].exec("*** Failers", 2312));
-assertEquals(null, res[793].exec("xyzZ", 2313));
-assertEquals(null, res[793].exec("xyz\nZ ", 2314));
-assertEquals("xyz", res[794].exec("xyz"), 2315);
-assertEquals("xyz", res[794].exec("xyz\n "), 2316);
-assertEquals("xyz", res[794].exec("abcxyz\npqr "), 2317);
-assertEquals("xyz", res[794].exec("abcxyz\npqrZ "), 2318);
-assertEquals("xyz", res[794].exec("xyz\nZ "), 2319);
-assertEquals(null, res[794].exec("*** Failers", 2320));
-assertEquals(null, res[794].exec("xyzZ", 2321));
-assertEquals(null, res[795].exec("abcdef", 2322));
-assertEquals(null, res[795].exec("defabcxyz>3 ", 2323));
-assertEquals(null, res[795].exec("*** Failers ", 2324));
-assertEquals(null, res[795].exec("defabcxyz", 2325));
-assertEquals(null, res[796].exec("abP", 2326));
-assertEquals(null, res[796].exec("abcdeP", 2327));
-assertEquals("abcdef", res[796].exec("abcdefP"), 2328);
-assertEquals(null, res[796].exec("*** Failers", 2329));
-assertEquals(null, res[796].exec("abxP ", 2330));
-assertEquals(null, res[797].exec("aP", 2331));
-assertEquals(null, res[797].exec("aaP", 2332));
-assertEquals(null, res[797].exec("aa2P ", 2333));
-assertEquals(null, res[797].exec("aaaP", 2334));
-assertEquals(null, res[797].exec("aaa23P ", 2335));
-assertEquals(null, res[797].exec("aaaa12345P", 2336));
-assertEquals("aa0z", res[797].exec("aa0zP"), 2337);
-assertEquals("aaaa4444444444444z", res[797].exec("aaaa4444444444444zP "), 2338);
-assertEquals(null, res[797].exec("*** Failers", 2339));
-assertEquals(null, res[797].exec("azP ", 2340));
-assertEquals(null, res[797].exec("aaaaaP ", 2341));
-assertEquals(null, res[797].exec("a56P ", 2342));
-assertEquals(null, res[799].exec("adfadadaklhlkalkajhlkjahdfasdfasdfladsfjkjPZ", 2343));
-assertEquals(null, res[799].exec("lkjhlkjhlkjhlkjhabbbbbbcdaefabbbbbbbefaPBZ", 2344));
-assertEquals(null, res[799].exec("cdabbbbbbbbPRBZ", 2345));
-assertEquals(null, res[799].exec("efabbbbbbbbbbbbbbbbPRBZ", 2346));
-assertEquals(null, res[799].exec("bbbbbbbbbbbbcdXyasdfadfPRBZ ", 2347));
-assertEquals(null, res[799].exec("abc", 2348));
-assertEquals(null, res[799].exec("** Failers", 2349));
-assertEquals(null, res[799].exec("def ", 2350));
-assertEquals("the quick brown fox", res[800].exec("the quick brown fox"), 2351);
-assertEquals(null, res[800].exec("The quick brown FOX", 2352));
-assertEquals("the quick brown fox", res[800].exec("What do you know about the quick brown fox?"), 2353);
-assertEquals(null, res[800].exec("What do you know about THE QUICK BROWN FOX?", 2354));
-assertEquals("the quick brown fox", res[801].exec("the quick brown fox"), 2355);
-assertEquals("The quick brown FOX", res[801].exec("The quick brown FOX"), 2356);
-assertEquals("the quick brown fox", res[801].exec("What do you know about the quick brown fox?"), 2357);
-assertEquals("THE QUICK BROWN FOX", res[801].exec("What do you know about THE QUICK BROWN FOX?"), 2358);
-assertEquals("abcd\x09\n\x0d\x0cae9;$\\?caxyz", res[802].exec("abcd\x09\n\x0d\x0cae9;$\\?caxyz"), 2359);
-assertEquals("abxyzpqrrrabbxyyyypqAzz", res[803].exec("abxyzpqrrrabbxyyyypqAzz"), 2360);
-assertEquals("abxyzpqrrrabbxyyyypqAzz", res[803].exec("abxyzpqrrrabbxyyyypqAzz"), 2361);
-assertEquals("aabxyzpqrrrabbxyyyypqAzz", res[803].exec("aabxyzpqrrrabbxyyyypqAzz"), 2362);
-assertEquals("aaabxyzpqrrrabbxyyyypqAzz", res[803].exec("aaabxyzpqrrrabbxyyyypqAzz"), 2363);
-assertEquals("aaaabxyzpqrrrabbxyyyypqAzz", res[803].exec("aaaabxyzpqrrrabbxyyyypqAzz"), 2364);
-assertEquals("abcxyzpqrrrabbxyyyypqAzz", res[803].exec("abcxyzpqrrrabbxyyyypqAzz"), 2365);
-assertEquals("aabcxyzpqrrrabbxyyyypqAzz", res[803].exec("aabcxyzpqrrrabbxyyyypqAzz"), 2366);
-assertEquals("aaabcxyzpqrrrabbxyyyypAzz", res[803].exec("aaabcxyzpqrrrabbxyyyypAzz"), 2367);
-assertEquals("aaabcxyzpqrrrabbxyyyypqAzz", res[803].exec("aaabcxyzpqrrrabbxyyyypqAzz"), 2368);
-assertEquals("aaabcxyzpqrrrabbxyyyypqqAzz", res[803].exec("aaabcxyzpqrrrabbxyyyypqqAzz"), 2369);
-assertEquals("aaabcxyzpqrrrabbxyyyypqqqAzz", res[803].exec("aaabcxyzpqrrrabbxyyyypqqqAzz"), 2370);
-assertEquals("aaabcxyzpqrrrabbxyyyypqqqqAzz", res[803].exec("aaabcxyzpqrrrabbxyyyypqqqqAzz"), 2371);
-assertEquals("aaabcxyzpqrrrabbxyyyypqqqqqAzz", res[803].exec("aaabcxyzpqrrrabbxyyyypqqqqqAzz"), 2372);
-assertEquals("aaabcxyzpqrrrabbxyyyypqqqqqqAzz", res[803].exec("aaabcxyzpqrrrabbxyyyypqqqqqqAzz"), 2373);
-assertEquals("aaaabcxyzpqrrrabbxyyyypqAzz", res[803].exec("aaaabcxyzpqrrrabbxyyyypqAzz"), 2374);
-assertEquals("abxyzzpqrrrabbxyyyypqAzz", res[803].exec("abxyzzpqrrrabbxyyyypqAzz"), 2375);
-assertEquals("aabxyzzzpqrrrabbxyyyypqAzz", res[803].exec("aabxyzzzpqrrrabbxyyyypqAzz"), 2376);
-assertEquals("aaabxyzzzzpqrrrabbxyyyypqAzz", res[803].exec("aaabxyzzzzpqrrrabbxyyyypqAzz"), 2377);
-assertEquals("aaaabxyzzzzpqrrrabbxyyyypqAzz", res[803].exec("aaaabxyzzzzpqrrrabbxyyyypqAzz"), 2378);
-assertEquals("abcxyzzpqrrrabbxyyyypqAzz", res[803].exec("abcxyzzpqrrrabbxyyyypqAzz"), 2379);
-assertEquals("aabcxyzzzpqrrrabbxyyyypqAzz", res[803].exec("aabcxyzzzpqrrrabbxyyyypqAzz"), 2380);
-assertEquals("aaabcxyzzzzpqrrrabbxyyyypqAzz", res[803].exec("aaabcxyzzzzpqrrrabbxyyyypqAzz"), 2381);
-assertEquals("aaaabcxyzzzzpqrrrabbxyyyypqAzz", res[803].exec("aaaabcxyzzzzpqrrrabbxyyyypqAzz"), 2382);
-assertEquals("aaaabcxyzzzzpqrrrabbbxyyyypqAzz", res[803].exec("aaaabcxyzzzzpqrrrabbbxyyyypqAzz"), 2383);
-assertEquals("aaaabcxyzzzzpqrrrabbbxyyyyypqAzz", res[803].exec("aaaabcxyzzzzpqrrrabbbxyyyyypqAzz"), 2384);
-assertEquals("aaabcxyzpqrrrabbxyyyypABzz", res[803].exec("aaabcxyzpqrrrabbxyyyypABzz"), 2385);
-assertEquals("aaabcxyzpqrrrabbxyyyypABBzz", res[803].exec("aaabcxyzpqrrrabbxyyyypABBzz"), 2386);
-assertEquals("aaabxyzpqrrrabbxyyyypqAzz", res[803].exec(">>>aaabxyzpqrrrabbxyyyypqAzz"), 2387);
-assertEquals("aaaabxyzpqrrrabbxyyyypqAzz", res[803].exec(">aaaabxyzpqrrrabbxyyyypqAzz"), 2388);
-assertEquals("abcxyzpqrrrabbxyyyypqAzz", res[803].exec(">>>>abcxyzpqrrrabbxyyyypqAzz"), 2389);
-assertEquals(null, res[803].exec("*** Failers", 2390));
-assertEquals(null, res[803].exec("abxyzpqrrabbxyyyypqAzz", 2391));
-assertEquals(null, res[803].exec("abxyzpqrrrrabbxyyyypqAzz", 2392));
-assertEquals(null, res[803].exec("abxyzpqrrrabxyyyypqAzz", 2393));
-assertEquals(null, res[803].exec("aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz", 2394));
-assertEquals(null, res[803].exec("aaaabcxyzzzzpqrrrabbbxyyypqAzz", 2395));
-assertEquals(null, res[803].exec("aaabcxyzpqrrrabbxyyyypqqqqqqqAzz", 2396));
-assertEquals("abczz,abc", res[804].exec("abczz"), 2397);
-assertEquals("abcabczz,abc", res[804].exec("abcabczz"), 2398);
-assertEquals(null, res[804].exec("*** Failers", 2399));
-assertEquals(null, res[804].exec("zz", 2400));
-assertEquals(null, res[804].exec("abcabcabczz", 2401));
-assertEquals(null, res[804].exec(">>abczz", 2402));
-assertEquals("bc,b", res[805].exec("bc"), 2403);
-assertEquals("bbc,b", res[805].exec("bbc"), 2404);
-assertEquals("bbbc,bb", res[805].exec("bbbc"), 2405);
-assertEquals("bac,a", res[805].exec("bac"), 2406);
-assertEquals("bbac,a", res[805].exec("bbac"), 2407);
-assertEquals("aac,a", res[805].exec("aac"), 2408);
-assertEquals("abbbbbbbbbbbc,bbbbbbbbbbb", res[805].exec("abbbbbbbbbbbc"), 2409);
-assertEquals("bbbbbbbbbbbac,a", res[805].exec("bbbbbbbbbbbac"), 2410);
-assertEquals(null, res[805].exec("*** Failers", 2411));
-assertEquals(null, res[805].exec("aaac", 2412));
-assertEquals(null, res[805].exec("abbbbbbbbbbbac", 2413));
-assertEquals("bc,b", res[806].exec("bc"), 2414);
-assertEquals("bbc,bb", res[806].exec("bbc"), 2415);
-assertEquals("bbbc,bbb", res[806].exec("bbbc"), 2416);
-assertEquals("bac,a", res[806].exec("bac"), 2417);
-assertEquals("bbac,a", res[806].exec("bbac"), 2418);
-assertEquals("aac,a", res[806].exec("aac"), 2419);
-assertEquals("abbbbbbbbbbbc,bbbbbbbbbbb", res[806].exec("abbbbbbbbbbbc"), 2420);
-assertEquals("bbbbbbbbbbbac,a", res[806].exec("bbbbbbbbbbbac"), 2421);
-assertEquals(null, res[806].exec("*** Failers", 2422));
-assertEquals(null, res[806].exec("aaac", 2423));
-assertEquals(null, res[806].exec("abbbbbbbbbbbac", 2424));
-assertEquals("bbc,bb", res[806].exec("bbc"), 2425);
-assertEquals("babc,ba", res[807].exec("babc"), 2426);
-assertEquals("bbabc,ba", res[807].exec("bbabc"), 2427);
-assertEquals("bababc,ba", res[807].exec("bababc"), 2428);
-assertEquals(null, res[807].exec("*** Failers", 2429));
-assertEquals(null, res[807].exec("bababbc", 2430));
-assertEquals(null, res[807].exec("babababc", 2431));
-assertEquals("babc,ba", res[808].exec("babc"), 2432);
-assertEquals("bbabc,ba", res[808].exec("bbabc"), 2433);
-assertEquals("bababc,ba", res[808].exec("bababc"), 2434);
-assertEquals(null, res[808].exec("*** Failers", 2435));
-assertEquals(null, res[808].exec("bababbc", 2436));
-assertEquals(null, res[808].exec("babababc", 2437));
+assertNull(res[443].exec("aaaa", 885));
+assertNull(res[443].exec("bacxxx", 886));
+assertNull(res[443].exec("bbaccxxx ", 887));
+assertNull(res[443].exec("bbbacccxx", 888));
+assertNull(res[443].exec("aaaa", 889));
+assertNull(res[443].exec("bacxxx", 890));
+assertNull(res[443].exec("bbaccxxx ", 891));
+assertNull(res[443].exec("bbbacccxx", 892));
+assertToStringEquals("a,a", res[444].exec("aaaa"), 893);
+assertNull(res[444].exec("bacxxx", 894));
+assertNull(res[444].exec("bbaccxxx ", 895));
+assertNull(res[444].exec("bbbacccxx", 896));
+assertToStringEquals("a,a", res[445].exec("aaaa"), 897);
+assertNull(res[445].exec("bacxxx", 898));
+assertNull(res[445].exec("bbaccxxx ", 899));
+assertNull(res[445].exec("bbbacccxx", 900));
+assertToStringEquals("a,a", res[446].exec("aaaa"), 901);
+assertNull(res[446].exec("bacxxx", 902));
+assertNull(res[446].exec("bbaccxxx ", 903));
+assertNull(res[446].exec("bbbacccxx", 904));
+assertToStringEquals("a,a,a", res[447].exec("aaaa"), 905);
+assertNull(res[447].exec("bacxxx", 906));
+assertNull(res[447].exec("bbaccxxx ", 907));
+assertNull(res[447].exec("bbbacccxx", 908));
+assertNull(res[449].exec("bacxxx", 909));
+assertNull(res[449].exec("XaaX", 910));
+assertNull(res[449].exec("XAAX ", 911));
+assertNull(res[449].exec("XaaX", 912));
+assertNull(res[449].exec("** Failers ", 913));
+assertNull(res[449].exec("XAAX ", 914));
+assertNull(res[449].exec("XaaX", 915));
+assertNull(res[449].exec("XAAX ", 916));
+assertNull(res[449].exec("xzxx", 917));
+assertNull(res[449].exec("yzyy ", 918));
+assertNull(res[449].exec("** Failers", 919));
+assertNull(res[449].exec("xxz ", 920));
+assertToStringEquals("a,,,a", res[450].exec("cat"), 921);
+assertToStringEquals("a,,,a", res[451].exec("cat"), 922);
+assertToStringEquals("TA]", res[452].exec("The ACTA] comes "), 923);
+assertToStringEquals("TA]", res[453].exec("The ACTA] comes "), 924);
+assertNull(res[453].exec("abcbabc", 925));
+assertNull(res[453].exec("abcbabc", 926));
+assertNull(res[453].exec("abcbabc", 927));
+assertNull(res[453].exec("** Failers ", 928));
+assertNull(res[453].exec("abcXabc", 929));
+assertNull(res[453].exec("abcXabc", 930));
+assertNull(res[453].exec("** Failers ", 931));
+assertNull(res[453].exec("abcbabc", 932));
+assertNull(res[453].exec("xyzbabcxyz", 933));
+assertNull(res[456].exec("** Failers", 934));
+assertNull(res[456].exec("ab", 935));
+assertNull(res[457].exec("** Failers", 936));
+assertNull(res[457].exec("ab ", 937));
+assertNull(res[457].exec("** Failers", 938));
+assertNull(res[457].exec("ab ", 939));
+assertToStringEquals("aXb", res[458].exec("aXb"), 940);
+assertToStringEquals("a\nb", res[458].exec("a\nb "), 941);
+assertNull(res[458].exec("** Failers", 942));
+assertNull(res[458].exec("ab ", 943));
+assertToStringEquals("aXb", res[459].exec("aXb"), 944);
+assertToStringEquals("a\nX\nXb", res[459].exec("a\nX\nXb "), 945);
+assertNull(res[459].exec("** Failers", 946));
+assertNull(res[459].exec("ab ", 947));
+assertToStringEquals("acb", res[463].exec("acb"), 948);
+assertToStringEquals("ab", res[463].exec("ab"), 949);
+assertNull(res[463].exec("ax{100}b ", 950));
+assertNull(res[463].exec("*** Failers", 951));
+assertNull(res[463].exec("a\nb ", 952));
+assertNull(res[464].exec("ax{4000}xyb ", 953));
+assertNull(res[464].exec("ax{4000}yb ", 954));
+assertNull(res[464].exec("ax{4000}x{100}yb ", 955));
+assertNull(res[464].exec("*** Failers", 956));
+assertNull(res[464].exec("ax{4000}b ", 957));
+assertNull(res[464].exec("ac\ncb ", 958));
+assertToStringEquals("a\xc0,,\xc0", res[465].exec("a\xc0\x88b"), 959);
+assertToStringEquals("ax,,x", res[466].exec("ax{100}b"), 960);
+assertToStringEquals("a\xc0\x88b,\xc0\x88,b", res[467].exec("a\xc0\x88b"), 961);
+assertToStringEquals("ax{100}b,x{100},b", res[468].exec("ax{100}b"), 962);
+assertToStringEquals("a\xc0\x92,\xc0,\x92", res[469].exec("a\xc0\x92bcd"), 963);
+assertToStringEquals("ax{,x,{", res[470].exec("ax{240}bcd"), 964);
+assertToStringEquals("a\xc0\x92,\xc0,\x92", res[471].exec("a\xc0\x92bcd"), 965);
+assertToStringEquals("ax{,x,{", res[472].exec("ax{240}bcd"), 966);
+assertToStringEquals("a\xc0,,\xc0", res[473].exec("a\xc0\x92bcd"), 967);
+assertToStringEquals("ax,,x", res[474].exec("ax{240}bcd"), 968);
+assertNull(res[475].exec("ax{1234}xyb ", 969));
+assertNull(res[475].exec("ax{1234}x{4321}yb ", 970));
+assertNull(res[475].exec("ax{1234}x{4321}x{3412}b ", 971));
+assertNull(res[475].exec("*** Failers", 972));
+assertNull(res[475].exec("ax{1234}b ", 973));
+assertNull(res[475].exec("ac\ncb ", 974));
+assertToStringEquals("ax{1234}xyb,x{1234}xy", res[476].exec("ax{1234}xyb "), 975);
+assertToStringEquals("ax{1234}x{4321}yb,x{1234}x{4321}y", res[476].exec("ax{1234}x{4321}yb "), 976);
+assertToStringEquals("ax{1234}x{4321}x{3412}b,x{1234}x{4321}x{3412}", res[476].exec("ax{1234}x{4321}x{3412}b "), 977);
+assertToStringEquals("axxxxbcdefghijb,xxxxbcdefghij", res[476].exec("axxxxbcdefghijb "), 978);
+assertToStringEquals("ax{1234}x{4321}x{3412}x{3421}b,x{1234}x{4321}x{3412}x{3421}", res[476].exec("ax{1234}x{4321}x{3412}x{3421}b "), 979);
+assertNull(res[476].exec("*** Failers", 980));
+assertToStringEquals("ax{1234}b,x{1234}", res[476].exec("ax{1234}b "), 981);
+assertToStringEquals("ax{1234}xyb,x{1234}xy", res[477].exec("ax{1234}xyb "), 982);
+assertToStringEquals("ax{1234}x{4321}yb,x{1234}x{4321}y", res[477].exec("ax{1234}x{4321}yb "), 983);
+assertToStringEquals("ax{1234}x{4321}x{3412}b,x{1234}x{4321}x{3412}", res[477].exec("ax{1234}x{4321}x{3412}b "), 984);
+assertToStringEquals("axxxxb,xxxx", res[477].exec("axxxxbcdefghijb "), 985);
+assertToStringEquals("ax{1234}x{4321}x{3412}x{3421}b,x{1234}x{4321}x{3412}x{3421}", res[477].exec("ax{1234}x{4321}x{3412}x{3421}b "), 986);
+assertNull(res[477].exec("*** Failers", 987));
+assertToStringEquals("ax{1234}b,x{1234}", res[477].exec("ax{1234}b "), 988);
+assertNull(res[478].exec("ax{1234}xyb ", 989));
+assertNull(res[478].exec("ax{1234}x{4321}yb ", 990));
+assertNull(res[478].exec("ax{1234}x{4321}x{3412}b ", 991));
+assertToStringEquals("axxxxb,xxxx", res[478].exec("axxxxbcdefghijb "), 992);
+assertNull(res[478].exec("ax{1234}x{4321}x{3412}x{3421}b ", 993));
+assertToStringEquals("axbxxb,xbxx", res[478].exec("axbxxbcdefghijb "), 994);
+assertToStringEquals("axxxxxb,xxxxx", res[478].exec("axxxxxbcdefghijb "), 995);
+assertNull(res[478].exec("*** Failers", 996));
+assertNull(res[478].exec("ax{1234}b ", 997));
+assertNull(res[478].exec("axxxxxxbcdefghijb ", 998));
+assertNull(res[479].exec("ax{1234}xyb ", 999));
+assertNull(res[479].exec("ax{1234}x{4321}yb ", 1000));
+assertNull(res[479].exec("ax{1234}x{4321}x{3412}b ", 1001));
+assertToStringEquals("axxxxb,xxxx", res[479].exec("axxxxbcdefghijb "), 1002);
+assertNull(res[479].exec("ax{1234}x{4321}x{3412}x{3421}b ", 1003));
+assertToStringEquals("axbxxb,xbxx", res[479].exec("axbxxbcdefghijb "), 1004);
+assertToStringEquals("axxxxxb,xxxxx", res[479].exec("axxxxxbcdefghijb "), 1005);
+assertNull(res[479].exec("*** Failers", 1006));
+assertNull(res[479].exec("ax{1234}b ", 1007));
+assertNull(res[479].exec("axxxxxxbcdefghijb ", 1008));
+assertNull(res[479].exec("*** Failers", 1009));
+assertNull(res[479].exec("x{100}", 1010));
+assertNull(res[479].exec("aXbcd", 1011));
+assertNull(res[479].exec("ax{100}bcd", 1012));
+assertNull(res[479].exec("ax{100000}bcd", 1013));
+assertNull(res[479].exec("x{100}x{100}x{100}b", 1014));
+assertNull(res[479].exec("*** Failers ", 1015));
+assertNull(res[479].exec("x{100}x{100}b", 1016));
+assertNull(res[479].exec("x{ab} ", 1017));
+assertNull(res[479].exec("\xc2\xab", 1018));
+assertNull(res[479].exec("*** Failers ", 1019));
+assertNull(res[479].exec("\x00{ab}", 1020));
+assertNull(res[479].exec("WXYZ", 1021));
+assertNull(res[479].exec("x{256}XYZ ", 1022));
+assertNull(res[479].exec("*** Failers", 1023));
+assertNull(res[479].exec("XYZ ", 1024));
+assertNull(res[480].exec("Xx{1234}", 1025));
+assertNull(res[481].exec("Xx{1234}YZ", 1026));
+assertToStringEquals("X", res[482].exec("XYZabcdce"), 1027);
+assertToStringEquals("X", res[483].exec("XYZabcde"), 1028);
+assertNull(res[484].exec("Xabcdefg ", 1029));
+assertNull(res[484].exec("Xx{1234} ", 1030));
+assertNull(res[484].exec("Xx{1234}YZ", 1031));
+assertNull(res[484].exec("Xx{1234}x{512} ", 1032));
+assertNull(res[484].exec("Xx{1234}x{512}YZ", 1033));
+assertNull(res[485].exec("Xabcdefg ", 1034));
+assertNull(res[485].exec("Xx{1234} ", 1035));
+assertNull(res[485].exec("Xx{1234}YZ", 1036));
+assertNull(res[485].exec("Xx{1234}x{512} ", 1037));
+assertToStringEquals("bcd", res[486].exec("bcd"), 1038);
+assertToStringEquals("00}", res[486].exec("x{100}aYx{256}Z "), 1039);
+assertToStringEquals("x{", res[487].exec("x{100}bc"), 1040);
+assertToStringEquals("x{100}bcA", res[488].exec("x{100}bcAa"), 1041);
+assertToStringEquals("x{", res[489].exec("x{100}bca"), 1042);
+assertToStringEquals("bcd", res[490].exec("bcd"), 1043);
+assertToStringEquals("00}", res[490].exec("x{100}aYx{256}Z "), 1044);
+assertToStringEquals("x{", res[491].exec("x{100}bc"), 1045);
+assertToStringEquals("x{100}bc", res[492].exec("x{100}bcAa"), 1046);
+assertToStringEquals("x{", res[493].exec("x{100}bca"), 1047);
+assertNull(res[493].exec("abcd", 1048));
+assertNull(res[493].exec("abcd", 1049));
+assertToStringEquals("x{", res[493].exec("x{100}x{100} "), 1050);
+assertToStringEquals("x{", res[493].exec("x{100}x{100} "), 1051);
+assertToStringEquals("x{", res[493].exec("x{100}x{100}x{100}x{100} "), 1052);
+assertNull(res[493].exec("abce", 1053));
+assertToStringEquals("x{", res[493].exec("x{100}x{100}x{100}x{100} "), 1054);
+assertNull(res[493].exec("abcdx{100}x{100}x{100}x{100} ", 1055));
+assertNull(res[493].exec("abcdx{100}x{100}x{100}x{100} ", 1056));
+assertNull(res[493].exec("abcdx{100}x{100}x{100}x{100} ", 1057));
+assertNull(res[493].exec("abcdx{100}x{100}x{100}XX", 1058));
+assertNull(res[493].exec("abcdx{100}x{100}x{100}x{100}x{100}x{100}x{100}XX", 1059));
+assertNull(res[493].exec("abcdx{100}x{100}x{100}x{100}x{100}x{100}x{100}XX", 1060));
+assertToStringEquals("Xy", res[493].exec("Xyyyax{100}x{100}bXzzz"), 1061);
+assertToStringEquals("X", res[496].exec("1X2"), 1062);
+assertToStringEquals("x", res[496].exec("1x{100}2 "), 1063);
+assertToStringEquals(">X", res[497].exec("> >X Y"), 1064);
+assertToStringEquals(">x", res[497].exec("> >x{100} Y"), 1065);
+assertToStringEquals("1", res[498].exec("x{100}3"), 1066);
+assertToStringEquals(" ", res[499].exec("x{100} X"), 1067);
+assertToStringEquals("abcd", res[500].exec("12abcd34"), 1068);
+assertToStringEquals("*** Failers", res[500].exec("*** Failers"), 1069);
+assertToStringEquals(" ", res[500].exec("1234 "), 1070);
+assertToStringEquals("abc", res[501].exec("12abcd34"), 1071);
+assertToStringEquals("ab", res[501].exec("12ab34"), 1072);
+assertToStringEquals("***", res[501].exec("*** Failers "), 1073);
+assertNull(res[501].exec("1234", 1074));
+assertToStringEquals(" ", res[501].exec("12a34 "), 1075);
+assertToStringEquals("ab", res[502].exec("12abcd34"), 1076);
+assertToStringEquals("ab", res[502].exec("12ab34"), 1077);
+assertToStringEquals("**", res[502].exec("*** Failers "), 1078);
+assertNull(res[502].exec("1234", 1079));
+assertToStringEquals(" ", res[502].exec("12a34 "), 1080);
+assertToStringEquals("12", res[503].exec("12abcd34"), 1081);
+assertNull(res[503].exec("*** Failers", 1082));
+assertToStringEquals("12", res[504].exec("12abcd34"), 1083);
+assertToStringEquals("123", res[504].exec("1234abcd"), 1084);
+assertNull(res[504].exec("*** Failers ", 1085));
+assertNull(res[504].exec("1.4 ", 1086));
+assertToStringEquals("12", res[505].exec("12abcd34"), 1087);
+assertToStringEquals("12", res[505].exec("1234abcd"), 1088);
+assertNull(res[505].exec("*** Failers ", 1089));
+assertNull(res[505].exec("1.4 ", 1090));
+assertToStringEquals("12abcd34", res[506].exec("12abcd34"), 1091);
+assertToStringEquals("***", res[506].exec("*** Failers"), 1092);
+assertNull(res[506].exec(" ", 1093));
+assertToStringEquals("12a", res[507].exec("12abcd34"), 1094);
+assertToStringEquals("123", res[507].exec("1234abcd"), 1095);
+assertToStringEquals("***", res[507].exec("*** Failers"), 1096);
+assertNull(res[507].exec(" ", 1097));
+assertToStringEquals("12", res[508].exec("12abcd34"), 1098);
+assertToStringEquals("12", res[508].exec("1234abcd"), 1099);
+assertToStringEquals("**", res[508].exec("*** Failers"), 1100);
+assertNull(res[508].exec(" ", 1101));
+assertToStringEquals("> <", res[509].exec("12> <34"), 1102);
+assertNull(res[509].exec("*** Failers", 1103));
+assertToStringEquals("> <", res[510].exec("ab> <cd"), 1104);
+assertToStringEquals("> <", res[510].exec("ab> <ce"), 1105);
+assertNull(res[510].exec("*** Failers", 1106));
+assertNull(res[510].exec("ab> <cd ", 1107));
+assertToStringEquals("> <", res[511].exec("ab> <cd"), 1108);
+assertToStringEquals("> <", res[511].exec("ab> <ce"), 1109);
+assertNull(res[511].exec("*** Failers", 1110));
+assertNull(res[511].exec("ab> <cd ", 1111));
+assertToStringEquals("12", res[512].exec("12 34"), 1112);
+assertToStringEquals("Failers", res[512].exec("*** Failers"), 1113);
+assertNull(res[512].exec("+++=*! ", 1114));
+assertToStringEquals("ab", res[513].exec("ab cd"), 1115);
+assertToStringEquals("abc", res[513].exec("abcd ce"), 1116);
+assertToStringEquals("Fai", res[513].exec("*** Failers"), 1117);
+assertNull(res[513].exec("a.b.c", 1118));
+assertToStringEquals("ab", res[514].exec("ab cd"), 1119);
+assertToStringEquals("ab", res[514].exec("abcd ce"), 1120);
+assertToStringEquals("Fa", res[514].exec("*** Failers"), 1121);
+assertNull(res[514].exec("a.b.c", 1122));
+assertToStringEquals("====", res[515].exec("12====34"), 1123);
+assertToStringEquals("*** ", res[515].exec("*** Failers"), 1124);
+assertToStringEquals(" ", res[515].exec("abcd "), 1125);
+assertToStringEquals("===", res[516].exec("ab====cd"), 1126);
+assertToStringEquals("==", res[516].exec("ab==cd"), 1127);
+assertToStringEquals("***", res[516].exec("*** Failers"), 1128);
+assertNull(res[516].exec("a.b.c", 1129));
+assertToStringEquals("==", res[517].exec("ab====cd"), 1130);
+assertToStringEquals("==", res[517].exec("ab==cd"), 1131);
+assertToStringEquals("**", res[517].exec("*** Failers"), 1132);
+assertNull(res[517].exec("a.b.c", 1133));
+assertNull(res[517].exec("x{100}", 1134));
+assertNull(res[517].exec("Zx{100}", 1135));
+assertNull(res[517].exec("x{100}Z", 1136));
+assertToStringEquals("**", res[517].exec("*** Failers "), 1137);
+assertNull(res[517].exec("Zx{100}", 1138));
+assertNull(res[517].exec("x{100}", 1139));
+assertNull(res[517].exec("x{100}Z", 1140));
+assertToStringEquals("**", res[517].exec("*** Failers "), 1141);
+assertNull(res[517].exec("abcx{200}X", 1142));
+assertNull(res[517].exec("abcx{100}X ", 1143));
+assertToStringEquals("**", res[517].exec("*** Failers"), 1144);
+assertToStringEquals(" ", res[517].exec("X "), 1145);
+assertNull(res[517].exec("abcx{200}X", 1146));
+assertNull(res[517].exec("abcx{100}X ", 1147));
+assertNull(res[517].exec("abQX ", 1148));
+assertToStringEquals("**", res[517].exec("*** Failers"), 1149);
+assertToStringEquals(" ", res[517].exec("X "), 1150);
+assertNull(res[517].exec("abcx{100}x{200}x{100}X", 1151));
+assertToStringEquals("**", res[517].exec("*** Failers"), 1152);
+assertNull(res[517].exec("abcx{200}X", 1153));
+assertToStringEquals(" ", res[517].exec("X "), 1154);
+assertNull(res[517].exec("AX", 1155));
+assertNull(res[517].exec("x{150}X", 1156));
+assertNull(res[517].exec("x{500}X ", 1157));
+assertToStringEquals("**", res[517].exec("*** Failers"), 1158);
+assertNull(res[517].exec("x{100}X", 1159));
+assertToStringEquals(" ", res[517].exec("x{200}X "), 1160);
+assertNull(res[517].exec("AX", 1161));
+assertNull(res[517].exec("x{150}X", 1162));
+assertNull(res[517].exec("x{500}X ", 1163));
+assertToStringEquals("**", res[517].exec("*** Failers"), 1164);
+assertNull(res[517].exec("x{100}X", 1165));
+assertToStringEquals(" ", res[517].exec("x{200}X "), 1166);
+assertNull(res[517].exec("QX ", 1167));
+assertNull(res[517].exec("AX", 1168));
+assertNull(res[517].exec("x{500}X ", 1169));
+assertToStringEquals("**", res[517].exec("*** Failers"), 1170);
+assertNull(res[517].exec("x{100}X", 1171));
+assertNull(res[517].exec("x{150}X", 1172));
+assertToStringEquals(" ", res[517].exec("x{200}X "), 1173);
+assertNull(res[518].exec("aXb", 1174));
+assertNull(res[518].exec("a\nb", 1175));
+assertNull(res[519].exec("aXb", 1176));
+assertNull(res[519].exec("a\nb", 1177));
+assertNull(res[519].exec("*** Failers ", 1178));
+assertNull(res[519].exec("ax{100}b ", 1179));
+assertNull(res[519].exec("z", 1180));
+assertNull(res[519].exec("Z ", 1181));
+assertNull(res[519].exec("x{100}", 1182));
+assertNull(res[519].exec("*** Failers", 1183));
+assertNull(res[519].exec("x{102}", 1184));
+assertNull(res[519].exec("y ", 1185));
+assertToStringEquals("\xff", res[520].exec(">\xff<"), 1186);
+assertNull(res[521].exec(">x{ff}<", 1187));
+assertToStringEquals("X", res[522].exec("XYZ"), 1188);
+assertToStringEquals("X", res[523].exec("XYZ"), 1189);
+assertToStringEquals("x", res[523].exec("x{123} "), 1190);
+assertToStringEquals(",", res[528].exec("catac"), 1191);
+assertToStringEquals(",", res[528].exec("ax{256}a "), 1192);
+assertToStringEquals(",", res[528].exec("x{85}"), 1193);
+assertToStringEquals(",", res[528].exec("\u1234 "), 1194);
+assertToStringEquals(",", res[528].exec("\u1234 "), 1195);
+assertToStringEquals(",", res[528].exec("abcdefg"), 1196);
+assertToStringEquals(",", res[528].exec("ab"), 1197);
+assertToStringEquals(",", res[528].exec("a "), 1198);
+assertToStringEquals("Ax", res[529].exec("Ax{a3}BC"), 1199);
+assertToStringEquals("Ax", res[530].exec("Ax{a3}BC"), 1200);
+assertToStringEquals("}=", res[531].exec("+x{a3}== "), 1201);
+assertToStringEquals("}=", res[532].exec("+x{a3}== "), 1202);
+assertToStringEquals("x", res[533].exec("x{442}x{435}x{441}x{442}"), 1203);
+assertToStringEquals("x", res[534].exec("x{442}x{435}x{441}x{442}"), 1204);
+assertToStringEquals("x", res[535].exec("x{442}x{435}x{441}x{442}"), 1205);
+assertToStringEquals("x", res[536].exec("x{442}x{435}x{441}x{442}"), 1206);
+assertToStringEquals("{", res[537].exec("x{2442}x{2435}x{2441}x{2442}"), 1207);
+assertToStringEquals("{", res[538].exec("x{2442}x{2435}x{2441}x{2442}"), 1208);
+assertToStringEquals("abc\n\x0dx{442}x{435}x{441}x{442}xyz ", res[539].exec("abc\n\x0dx{442}x{435}x{441}x{442}xyz "), 1209);
+assertToStringEquals("x{442}x{435}x{441}x{442}", res[539].exec("x{442}x{435}x{441}x{442}"), 1210);
+assertToStringEquals("c d", res[540].exec("abc defx{442}x{443}xyz\npqr"), 1211);
+assertToStringEquals("c d", res[541].exec("abc defx{442}x{443}xyz\npqr"), 1212);
+assertNull(res[542].exec("+x{2442}", 1213));
+assertNull(res[543].exec("+x{2442}", 1214));
+assertNull(res[544].exec("Ax{442}", 1215));
+assertNull(res[545].exec("Ax{442}", 1216));
+assertNull(res[546].exec("Ax{442}", 1217));
+assertNull(res[547].exec("Ax{442}", 1218));
+assertNull(res[548].exec("\x19x{e01ff}", 1219));
+assertNull(res[549].exec("Ax{422}", 1220));
+assertNull(res[550].exec("x{19}x{e01ff}", 1221));
+assertNull(res[551].exec("Ax{442}", 1222));
+assertNull(res[552].exec("Ax{442}", 1223));
+assertNull(res[553].exec("ax{442}", 1224));
+assertNull(res[554].exec("+x{2442}", 1225));
+assertNull(res[555].exec("Mx{442}", 1226));
+assertToStringEquals("abc", res[556].exec("abc"), 1227);
+assertToStringEquals("abc", res[557].exec("abc"), 1228);
+assertToStringEquals("abc", res[558].exec("abc"), 1229);
+assertToStringEquals("abc", res[559].exec("abc"), 1230);
+assertNull(res[560].exec("x{100}ax{1234}bcd", 1231));
+assertNull(res[562].exec("x{0041}x{2262}x{0391}x{002e}", 1232));
+assertNull(res[562].exec("x{D55c}x{ad6d}x{C5B4} ", 1233));
+assertNull(res[562].exec("x{65e5}x{672c}x{8a9e}", 1234));
+assertToStringEquals("{861}X", res[563].exec("x{212ab}x{212ab}x{212ab}x{861}X"), 1235);
+assertToStringEquals("x{2", res[564].exec("x{212ab}x{212ab}x{212ab}x{861}"), 1236);
+assertToStringEquals("x{c", res[564].exec("x{c0}b"), 1237);
+assertToStringEquals("ax{", res[564].exec("ax{c0}aaaa/ "), 1238);
+assertToStringEquals("ax{", res[564].exec("ax{c0}aaaa/ "), 1239);
+assertToStringEquals("ax{", res[564].exec("ax{c0}ax{c0}aaa/ "), 1240);
+assertToStringEquals("ax{", res[564].exec("ax{c0}aaaa/ "), 1241);
+assertToStringEquals("ax{", res[564].exec("ax{c0}ax{c0}aaa/ "), 1242);
+assertToStringEquals("ax{", res[564].exec("ax{c0}aaaa/ "), 1243);
+assertToStringEquals("ax{", res[564].exec("ax{c0}ax{c0}aaa/ "), 1244);
+assertToStringEquals("Sho", res[564].exec("Should produce an error diagnostic"), 1245);
+assertNull(res[565].exec("Xx{1234}", 1246));
+assertNull(res[565].exec("X\nabc ", 1247));
+assertToStringEquals("b", res[566].exec("bar"), 1248);
+assertNull(res[566].exec("*** Failers", 1249));
+assertNull(res[566].exec("c", 1250));
+assertNull(res[566].exec("x{ff}", 1251));
+assertNull(res[566].exec("x{100} ", 1252));
+assertToStringEquals("c", res[567].exec("c"), 1253);
+assertToStringEquals("x", res[567].exec("x{ff}"), 1254);
+assertToStringEquals("x", res[567].exec("x{100} "), 1255);
+assertToStringEquals("*", res[567].exec("*** Failers "), 1256);
+assertNull(res[567].exec("aaa", 1257));
+assertToStringEquals("x", res[568].exec("x{f1}"), 1258);
+assertToStringEquals("x", res[568].exec("x{bf}"), 1259);
+assertToStringEquals("x", res[568].exec("x{100}"), 1260);
+assertToStringEquals("x", res[568].exec("x{1000} "), 1261);
+assertToStringEquals("*", res[568].exec("*** Failers"), 1262);
+assertToStringEquals("x", res[568].exec("x{c0} "), 1263);
+assertToStringEquals("x", res[568].exec("x{f0} "), 1264);
+assertToStringEquals("1", res[568].exec("1234"), 1265);
+assertToStringEquals("\"", res[568].exec("\"1234\" "), 1266);
+assertToStringEquals("x", res[568].exec("x{100}1234"), 1267);
+assertToStringEquals("\"", res[568].exec("\"x{100}1234\" "), 1268);
+assertToStringEquals("x", res[568].exec("x{100}x{100}12ab "), 1269);
+assertToStringEquals("x", res[568].exec("x{100}x{100}\"12\" "), 1270);
+assertToStringEquals("*", res[568].exec("*** Failers "), 1271);
+assertToStringEquals("x", res[568].exec("x{100}x{100}abcd"), 1272);
+assertToStringEquals("A", res[568].exec("A"), 1273);
+assertToStringEquals("x", res[568].exec("x{100}"), 1274);
+assertToStringEquals("Z", res[568].exec("Zx{100}"), 1275);
+assertToStringEquals("x", res[568].exec("x{100}Z"), 1276);
+assertToStringEquals("*", res[568].exec("*** Failers "), 1277);
+assertToStringEquals("Z", res[568].exec("Zx{100}"), 1278);
+assertToStringEquals("x", res[568].exec("x{100}"), 1279);
+assertToStringEquals("x", res[568].exec("x{100}Z"), 1280);
+assertToStringEquals("*", res[568].exec("*** Failers "), 1281);
+assertToStringEquals("x", res[568].exec("x{100}"), 1282);
+assertToStringEquals("x", res[568].exec("x{104}"), 1283);
+assertToStringEquals("*", res[568].exec("*** Failers"), 1284);
+assertToStringEquals("x", res[568].exec("x{105}"), 1285);
+assertToStringEquals("x", res[568].exec("x{ff} "), 1286);
+assertToStringEquals("x", res[568].exec("x{100}"), 1287);
+assertToStringEquals("\u0100", res[568].exec("\u0100 "), 1288);
+assertToStringEquals("\xff", res[569].exec(">\xff<"), 1289);
+assertNull(res[570].exec(">x{ff}<", 1290));
+assertToStringEquals("\xd6", res[572].exec("\xd6 # Matches without Study"), 1291);
+assertToStringEquals("x", res[572].exec("x{d6}"), 1292);
+assertToStringEquals("\xd6", res[572].exec("\xd6 <-- Same with Study"), 1293);
+assertToStringEquals("x", res[572].exec("x{d6}"), 1294);
+assertToStringEquals("\xd6", res[572].exec("\xd6 # Matches without Study"), 1295);
+assertToStringEquals("x", res[572].exec("x{d6} "), 1296);
+assertToStringEquals("\xd6", res[572].exec("\xd6 <-- Same with Study"), 1297);
+assertToStringEquals("x", res[572].exec("x{d6} "), 1298);
+assertToStringEquals("\ufffd", res[572].exec("\ufffd]"), 1299);
+assertToStringEquals("\ufffd", res[572].exec("\ufffd"), 1300);
+assertToStringEquals("\ufffd", res[572].exec("\ufffd\ufffd\ufffd"), 1301);
+assertToStringEquals("\ufffd", res[572].exec("\ufffd\ufffd\ufffd?"), 1302);
+assertNull(res[573].exec("\xc0\x80", 1303));
+assertNull(res[573].exec("\xc1\x8f ", 1304));
+assertNull(res[573].exec("\xe0\x9f\x80", 1305));
+assertNull(res[573].exec("\xf0\x8f\x80\x80 ", 1306));
+assertNull(res[573].exec("\xf8\x87\x80\x80\x80 ", 1307));
+assertNull(res[573].exec("\xfc\x83\x80\x80\x80\x80", 1308));
+assertNull(res[573].exec("\xfe\x80\x80\x80\x80\x80 ", 1309));
+assertNull(res[573].exec("\xff\x80\x80\x80\x80\x80 ", 1310));
+assertNull(res[573].exec("\xc3\x8f", 1311));
+assertNull(res[573].exec("\xe0\xaf\x80", 1312));
+assertNull(res[573].exec("\xe1\x80\x80", 1313));
+assertNull(res[573].exec("\xf0\x9f\x80\x80 ", 1314));
+assertNull(res[573].exec("\xf1\x8f\x80\x80 ", 1315));
+assertNull(res[573].exec("\xf8\x88\x80\x80\x80 ", 1316));
+assertNull(res[573].exec("\xf9\x87\x80\x80\x80 ", 1317));
+assertNull(res[573].exec("\xfc\x84\x80\x80\x80\x80", 1318));
+assertNull(res[573].exec("\xfd\x83\x80\x80\x80\x80", 1319));
+assertNull(res[573].exec("?\xf8\x88\x80\x80\x80 ", 1320));
+assertNull(res[573].exec("?\xf9\x87\x80\x80\x80 ", 1321));
+assertNull(res[573].exec("?\xfc\x84\x80\x80\x80\x80", 1322));
+assertNull(res[573].exec("?\xfd\x83\x80\x80\x80\x80", 1323));
+assertToStringEquals(".", res[574].exec("A.B"), 1324);
+assertToStringEquals("{", res[574].exec("Ax{100}B "), 1325);
+assertToStringEquals("x", res[575].exec("x{100}X "), 1326);
+assertToStringEquals("a", res[575].exec("ax{1234}b"), 1327);
+assertNull(res[577].exec("AxxB ", 1328));
+assertToStringEquals("abc1", res[578].exec("abc1 \nabc2 \x0babc3xx \x0cabc4 \x0dabc5xx \x0d\nabc6 x{0085}abc7 x{2028}abc8 x{2029}abc9 JUNK"), 1329);
+assertToStringEquals("abc1", res[579].exec("abc1\n abc2\x0b abc3\x0c abc4\x0d abc5\x0d\n abc6x{0085} abc7x{2028} abc8x{2029} abc9"), 1330);
+assertNull(res[580].exec("a\nb", 1331));
+assertNull(res[580].exec("a\x0db", 1332));
+assertNull(res[580].exec("a\x0d\nb", 1333));
+assertNull(res[580].exec("a\x0bb", 1334));
+assertNull(res[580].exec("a\x0cb", 1335));
+assertNull(res[580].exec("ax{85}b ", 1336));
+assertNull(res[580].exec("ax{2028}b ", 1337));
+assertNull(res[580].exec("ax{2029}b ", 1338));
+assertNull(res[580].exec("** Failers", 1339));
+assertNull(res[580].exec("a\n\x0db ", 1340));
+assertToStringEquals("ab", res[581].exec("ab"), 1341);
+assertNull(res[581].exec("a\nb", 1342));
+assertNull(res[581].exec("a\x0db", 1343));
+assertNull(res[581].exec("a\x0d\nb", 1344));
+assertNull(res[581].exec("a\x0bb", 1345));
+assertNull(res[581].exec("a\x0cx{2028}x{2029}b", 1346));
+assertNull(res[581].exec("ax{85}b ", 1347));
+assertNull(res[581].exec("a\n\x0db ", 1348));
+assertNull(res[581].exec("a\n\x0dx{85}\x0cb ", 1349));
+assertNull(res[582].exec("a\nb", 1350));
+assertNull(res[582].exec("a\x0db", 1351));
+assertNull(res[582].exec("a\x0d\nb", 1352));
+assertNull(res[582].exec("a\x0bb", 1353));
+assertNull(res[582].exec("a\x0cx{2028}x{2029}b", 1354));
+assertNull(res[582].exec("ax{85}b ", 1355));
+assertNull(res[582].exec("a\n\x0db ", 1356));
+assertNull(res[582].exec("a\n\x0dx{85}\x0cb ", 1357));
+assertNull(res[582].exec("** Failers", 1358));
+assertNull(res[582].exec("ab ", 1359));
+assertNull(res[583].exec("a\nb", 1360));
+assertNull(res[583].exec("a\n\x0db", 1361));
+assertNull(res[583].exec("a\n\x0dx{85}b", 1362));
+assertNull(res[583].exec("a\x0d\n\x0d\nb ", 1363));
+assertNull(res[583].exec("a\x0d\n\x0d\n\x0d\nb ", 1364));
+assertNull(res[583].exec("a\n\x0d\n\x0db", 1365));
+assertNull(res[583].exec("a\n\n\x0d\nb ", 1366));
+assertNull(res[583].exec("** Failers", 1367));
+assertNull(res[583].exec("a\n\n\n\x0db", 1368));
+assertNull(res[583].exec("a\x0d", 1369));
+assertNull(res[584].exec("X X\n", 1370));
+assertNull(res[584].exec("X\x09X\x0b", 1371));
+assertNull(res[584].exec("** Failers", 1372));
+assertNull(res[584].exec("x{a0} X\n ", 1373));
+assertNull(res[585].exec("\x09 x{a0}X\n\x0b\x0c\x0d\n", 1374));
+assertNull(res[585].exec("\x09 x{a0}\n\x0b\x0c\x0d\n", 1375));
+assertNull(res[585].exec("\x09 x{a0}\n\x0b\x0c", 1376));
+assertNull(res[585].exec("** Failers ", 1377));
+assertNull(res[585].exec("\x09 x{a0}\n\x0b", 1378));
+assertNull(res[585].exec(" ", 1379));
+assertNull(res[586].exec("x{3001}x{3000}x{2030}x{2028}", 1380));
+assertNull(res[586].exec("Xx{180e}Xx{85}", 1381));
+assertNull(res[586].exec("** Failers", 1382));
+assertNull(res[586].exec("x{2009} X\n ", 1383));
+assertNull(res[587].exec("x{1680}x{180e}x{2007}Xx{2028}x{2029}\x0c\x0d\n", 1384));
+assertNull(res[587].exec("\x09x{205f}x{a0}\nx{2029}\x0cx{2028}\n", 1385));
+assertNull(res[587].exec("\x09 x{202f}\n\x0b\x0c", 1386));
+assertNull(res[587].exec("** Failers ", 1387));
+assertNull(res[587].exec("\x09x{200a}x{a0}x{2028}\x0b", 1388));
+assertNull(res[587].exec(" ", 1389));
+assertNull(res[588].exec(">x{1680}", 1390));
+assertNull(res[589].exec(">x{1680}x{180e}x{2000}x{2003}x{200a}x{202f}x{205f}x{3000}<", 1391));
+assertToStringEquals("x{1ec5} ", res[593].exec("x{1ec5} "), 1392);
+assertNull(res[594].exec("x{0}x{d7ff}x{e000}x{10ffff}", 1393));
+assertNull(res[594].exec("x{d800}", 1394));
+assertNull(res[594].exec("x{d800}?", 1395));
+assertNull(res[594].exec("x{da00}", 1396));
+assertNull(res[594].exec("x{da00}?", 1397));
+assertNull(res[594].exec("x{dfff}", 1398));
+assertNull(res[594].exec("x{dfff}?", 1399));
+assertNull(res[594].exec("x{110000} ", 1400));
+assertNull(res[594].exec("x{110000}? ", 1401));
+assertNull(res[594].exec("x{2000000} ", 1402));
+assertNull(res[594].exec("x{2000000}? ", 1403));
+assertNull(res[594].exec("x{7fffffff} ", 1404));
+assertNull(res[594].exec("x{7fffffff}? ", 1405));
+assertNull(res[595].exec("a\x0db", 1406));
+assertNull(res[595].exec("a\nb", 1407));
+assertNull(res[595].exec("a\x0d\nb", 1408));
+assertNull(res[595].exec("** Failers", 1409));
+assertNull(res[595].exec("ax{85}b", 1410));
+assertNull(res[595].exec("a\x0bb ", 1411));
+assertNull(res[596].exec("a\x0db", 1412));
+assertNull(res[596].exec("a\nb", 1413));
+assertNull(res[596].exec("a\x0d\nb", 1414));
+assertNull(res[596].exec("ax{85}b", 1415));
+assertNull(res[596].exec("a\x0bb ", 1416));
+assertNull(res[596].exec("** Failers ", 1417));
+assertNull(res[596].exec("ax{85}b<bsr_anycrlf>", 1418));
+assertNull(res[596].exec("a\x0bb<bsr_anycrlf>", 1419));
+assertNull(res[597].exec("a\x0db", 1420));
+assertNull(res[597].exec("a\nb", 1421));
+assertNull(res[597].exec("a\x0d\nb", 1422));
+assertNull(res[597].exec("** Failers", 1423));
+assertNull(res[597].exec("ax{85}b", 1424));
+assertNull(res[597].exec("a\x0bb ", 1425));
+assertNull(res[598].exec("a\x0db", 1426));
+assertNull(res[598].exec("a\nb", 1427));
+assertNull(res[598].exec("a\x0d\nb", 1428));
+assertNull(res[598].exec("ax{85}b", 1429));
+assertNull(res[598].exec("a\x0bb ", 1430));
+assertNull(res[598].exec("** Failers ", 1431));
+assertNull(res[598].exec("ax{85}b<bsr_anycrlf>", 1432));
+assertNull(res[598].exec("a\x0bb<bsr_anycrlf>", 1433));
+assertToStringEquals("QQQx{2029}ABCaXYZ=!bPQR", res[599].exec("QQQx{2029}ABCaXYZ=!bPQR"), 1434);
+assertNull(res[599].exec("** Failers", 1435));
+assertNull(res[599].exec("ax{2029}b", 1436));
+assertNull(res[599].exec("a\xe2\x80\xa9b ", 1437));
+assertNull(res[600].exec("ax{1234}b", 1438));
+assertToStringEquals("a\nb", res[600].exec("a\nb "), 1439);
+assertNull(res[600].exec("** Failers", 1440));
+assertNull(res[600].exec("ab ", 1441));
+assertToStringEquals("aXb", res[601].exec("aXb"), 1442);
+assertToStringEquals("a\nX\nXx{1234}b", res[601].exec("a\nX\nXx{1234}b "), 1443);
+assertNull(res[601].exec("** Failers", 1444));
+assertNull(res[601].exec("ab ", 1445));
+assertNull(res[601].exec("x{de}x{de}", 1446));
+assertNull(res[601].exec("x{123} ", 1447));
+assertToStringEquals("X", res[602].exec("Ax{1ec5}ABCXYZ"), 1448);
+assertNull(res[604].exec("x{c0}x{30f}x{660}x{66c}x{f01}x{1680}<", 1449));
+assertNull(res[604].exec("\npx{300}9!$ < ", 1450));
+assertNull(res[604].exec("** Failers ", 1451));
+assertNull(res[604].exec("apx{300}9!$ < ", 1452));
+assertNull(res[605].exec("X", 1453));
+assertNull(res[605].exec("** Failers ", 1454));
+assertNull(res[605].exec("", 1455));
+assertNull(res[606].exec("9", 1456));
+assertNull(res[606].exec("** Failers ", 1457));
+assertNull(res[606].exec("x{c0}", 1458));
+assertNull(res[607].exec("X", 1459));
+assertNull(res[607].exec("** Failers ", 1460));
+assertNull(res[607].exec("x{30f}", 1461));
+assertNull(res[608].exec("X", 1462));
+assertNull(res[608].exec("** Failers ", 1463));
+assertNull(res[608].exec("x{660}", 1464));
+assertNull(res[609].exec("X", 1465));
+assertNull(res[609].exec("** Failers ", 1466));
+assertNull(res[609].exec("x{66c}", 1467));
+assertNull(res[610].exec("X", 1468));
+assertNull(res[610].exec("** Failers ", 1469));
+assertNull(res[610].exec("x{f01}", 1470));
+assertNull(res[611].exec("X", 1471));
+assertNull(res[611].exec("** Failers ", 1472));
+assertNull(res[611].exec("x{1680}", 1473));
+assertNull(res[612].exec("x{017}", 1474));
+assertNull(res[612].exec("x{09f} ", 1475));
+assertNull(res[612].exec("** Failers", 1476));
+assertNull(res[612].exec("x{0600} ", 1477));
+assertNull(res[613].exec("x{601}", 1478));
+assertNull(res[613].exec("** Failers", 1479));
+assertNull(res[613].exec("x{09f} ", 1480));
+assertNull(res[614].exec("x{e0000}", 1481));
+assertNull(res[614].exec("** Failers", 1482));
+assertNull(res[614].exec("x{09f} ", 1483));
+assertNull(res[615].exec("x{f8ff}", 1484));
+assertNull(res[615].exec("** Failers", 1485));
+assertNull(res[615].exec("x{09f} ", 1486));
+assertNull(res[616].exec("?x{dfff}", 1487));
+assertNull(res[616].exec("** Failers", 1488));
+assertNull(res[616].exec("x{09f} ", 1489));
+assertNull(res[617].exec("a", 1490));
+assertNull(res[617].exec("** Failers ", 1491));
+assertNull(res[617].exec("Z", 1492));
+assertNull(res[617].exec("x{e000} ", 1493));
+assertNull(res[618].exec("x{2b0}", 1494));
+assertNull(res[618].exec("** Failers", 1495));
+assertNull(res[618].exec("a ", 1496));
+assertNull(res[619].exec("x{1bb}", 1497));
+assertNull(res[619].exec("x{3400}", 1498));
+assertNull(res[619].exec("x{3401}", 1499));
+assertNull(res[619].exec("x{4d00}", 1500));
+assertNull(res[619].exec("x{4db4}", 1501));
+assertNull(res[619].exec("x{4db5} ", 1502));
+assertNull(res[619].exec("** Failers", 1503));
+assertNull(res[619].exec("a ", 1504));
+assertNull(res[619].exec("x{2b0}", 1505));
+assertNull(res[619].exec("x{4db6} ", 1506));
+assertNull(res[620].exec("x{1c5}", 1507));
+assertNull(res[620].exec("** Failers", 1508));
+assertNull(res[620].exec("a ", 1509));
+assertNull(res[620].exec("x{2b0}", 1510));
+assertNull(res[621].exec("A", 1511));
+assertNull(res[621].exec("** Failers", 1512));
+assertNull(res[621].exec("x{2b0}", 1513));
+assertNull(res[622].exec("x{903}", 1514));
+assertNull(res[622].exec("** Failers", 1515));
+assertNull(res[622].exec("X", 1516));
+assertNull(res[622].exec("x{300}", 1517));
+assertNull(res[622].exec(" ", 1518));
+assertNull(res[623].exec("x{488}", 1519));
+assertNull(res[623].exec("** Failers", 1520));
+assertNull(res[623].exec("X", 1521));
+assertNull(res[623].exec("x{903}", 1522));
+assertNull(res[623].exec("x{300}", 1523));
+assertNull(res[624].exec("x{300}", 1524));
+assertNull(res[624].exec("** Failers", 1525));
+assertNull(res[624].exec("X", 1526));
+assertNull(res[624].exec("x{903}", 1527));
+assertNull(res[624].exec("0123456789x{660}x{661}x{662}x{663}x{664}x{665}x{666}x{667}x{668}x{669}x{66a}", 1528));
+assertNull(res[624].exec("x{6f0}x{6f1}x{6f2}x{6f3}x{6f4}x{6f5}x{6f6}x{6f7}x{6f8}x{6f9}x{6fa}", 1529));
+assertNull(res[624].exec("x{966}x{967}x{968}x{969}x{96a}x{96b}x{96c}x{96d}x{96e}x{96f}x{970}", 1530));
+assertNull(res[624].exec("** Failers", 1531));
+assertNull(res[624].exec("X", 1532));
+assertNull(res[625].exec("x{16ee}", 1533));
+assertNull(res[625].exec("** Failers", 1534));
+assertNull(res[625].exec("X", 1535));
+assertNull(res[625].exec("x{966}", 1536));
+assertNull(res[626].exec("x{b2}", 1537));
+assertNull(res[626].exec("x{b3}", 1538));
+assertNull(res[626].exec("** Failers", 1539));
+assertNull(res[626].exec("X", 1540));
+assertNull(res[626].exec("x{16ee}", 1541));
+assertNull(res[627].exec("_", 1542));
+assertNull(res[627].exec("x{203f}", 1543));
+assertNull(res[627].exec("** Failers", 1544));
+assertNull(res[627].exec("X", 1545));
+assertNull(res[627].exec("-", 1546));
+assertNull(res[627].exec("x{58a}", 1547));
+assertNull(res[628].exec("-", 1548));
+assertNull(res[628].exec("x{58a}", 1549));
+assertNull(res[628].exec("** Failers", 1550));
+assertNull(res[628].exec("X", 1551));
+assertNull(res[628].exec("x{203f}", 1552));
+assertNull(res[629].exec(")", 1553));
+assertNull(res[629].exec("]", 1554));
+assertNull(res[629].exec("}", 1555));
+assertNull(res[629].exec("x{f3b}", 1556));
+assertNull(res[629].exec("** Failers", 1557));
+assertNull(res[629].exec("X", 1558));
+assertNull(res[629].exec("x{203f}", 1559));
+assertNull(res[629].exec("(", 1560));
+assertNull(res[629].exec("[", 1561));
+assertNull(res[629].exec("{", 1562));
+assertNull(res[629].exec("x{f3c}", 1563));
+assertNull(res[630].exec("x{bb}", 1564));
+assertNull(res[630].exec("x{2019}", 1565));
+assertNull(res[630].exec("** Failers", 1566));
+assertNull(res[630].exec("X", 1567));
+assertNull(res[630].exec("x{203f}", 1568));
+assertNull(res[631].exec("x{ab}", 1569));
+assertNull(res[631].exec("x{2018}", 1570));
+assertNull(res[631].exec("** Failers", 1571));
+assertNull(res[631].exec("X", 1572));
+assertNull(res[631].exec("x{203f}", 1573));
+assertNull(res[632].exec("!", 1574));
+assertNull(res[632].exec("x{37e}", 1575));
+assertNull(res[632].exec("** Failers", 1576));
+assertNull(res[632].exec("X", 1577));
+assertNull(res[632].exec("x{203f}", 1578));
+assertNull(res[633].exec("(", 1579));
+assertNull(res[633].exec("[", 1580));
+assertNull(res[633].exec("{", 1581));
+assertNull(res[633].exec("x{f3c}", 1582));
+assertNull(res[633].exec("** Failers", 1583));
+assertNull(res[633].exec("X", 1584));
+assertNull(res[633].exec(")", 1585));
+assertNull(res[633].exec("]", 1586));
+assertNull(res[633].exec("}", 1587));
+assertNull(res[633].exec("x{f3b}", 1588));
+assertNull(res[633].exec("$x{a2}x{a3}x{a4}x{a5}x{a6}", 1589));
+assertNull(res[633].exec("x{9f2}", 1590));
+assertNull(res[633].exec("** Failers", 1591));
+assertNull(res[633].exec("X", 1592));
+assertNull(res[633].exec("x{2c2}", 1593));
+assertNull(res[634].exec("x{2c2}", 1594));
+assertNull(res[634].exec("** Failers", 1595));
+assertNull(res[634].exec("X", 1596));
+assertNull(res[634].exec("x{9f2}", 1597));
+assertNull(res[634].exec("+<|~x{ac}x{2044}", 1598));
+assertNull(res[634].exec("** Failers", 1599));
+assertNull(res[634].exec("X", 1600));
+assertNull(res[634].exec("x{9f2}", 1601));
+assertNull(res[635].exec("x{a6}", 1602));
+assertNull(res[635].exec("x{482} ", 1603));
+assertNull(res[635].exec("** Failers", 1604));
+assertNull(res[635].exec("X", 1605));
+assertNull(res[635].exec("x{9f2}", 1606));
+assertNull(res[636].exec("x{2028}", 1607));
+assertNull(res[636].exec("** Failers", 1608));
+assertNull(res[636].exec("X", 1609));
+assertNull(res[636].exec("x{2029}", 1610));
+assertNull(res[637].exec("x{2029}", 1611));
+assertNull(res[637].exec("** Failers", 1612));
+assertNull(res[637].exec("X", 1613));
+assertNull(res[637].exec("x{2028}", 1614));
+assertNull(res[638].exec("\\ \\", 1615));
+assertNull(res[638].exec("x{a0}", 1616));
+assertNull(res[638].exec("x{1680}", 1617));
+assertNull(res[638].exec("x{180e}", 1618));
+assertNull(res[638].exec("x{2000}", 1619));
+assertNull(res[638].exec("x{2001} ", 1620));
+assertNull(res[638].exec("** Failers", 1621));
+assertNull(res[638].exec("x{2028}", 1622));
+assertNull(res[638].exec("x{200d} ", 1623));
+assertNull(res[638].exec(" x{660}x{661}x{662}ABC", 1624));
+assertNull(res[638].exec(" x{660}x{661}x{662}ABC", 1625));
+assertNull(res[639].exec(" x{660}x{661}x{662}ABC", 1626));
+assertNull(res[640].exec(" x{660}x{661}x{662}ABC", 1627));
+assertNull(res[641].exec(" x{660}x{661}x{662}ABC", 1628));
+assertNull(res[642].exec(" x{660}x{661}x{662}ABC", 1629));
+assertNull(res[643].exec(" x{660}x{661}x{662}ABC", 1630));
+assertNull(res[644].exec(" x{660}x{661}x{662}ABC", 1631));
+assertNull(res[645].exec(" x{660}x{661}x{662}ABC", 1632));
+assertNull(res[646].exec(" x{660}x{661}x{662}ABC", 1633));
+assertNull(res[647].exec(" x{660}x{661}x{662}ABC", 1634));
+assertNull(res[647].exec(" x{660}x{661}x{662}ABC", 1635));
+assertNull(res[647].exec(" x{660}x{661}x{662}ABC", 1636));
+assertNull(res[647].exec(" ** Failers", 1637));
+assertNull(res[647].exec(" x{660}x{661}x{662}ABC", 1638));
+assertNull(res[648].exec("A", 1639));
+assertNull(res[648].exec("ax{10a0}B ", 1640));
+assertNull(res[648].exec("** Failers ", 1641));
+assertNull(res[648].exec("a", 1642));
+assertNull(res[648].exec("x{1d00} ", 1643));
+assertNull(res[649].exec("1234", 1644));
+assertNull(res[649].exec("** Failers", 1645));
+assertNull(res[649].exec("ABC ", 1646));
+assertNull(res[650].exec("1234", 1647));
+assertNull(res[650].exec("** Failers", 1648));
+assertNull(res[650].exec("ABC ", 1649));
+assertNull(res[650].exec("A2XYZ", 1650));
+assertNull(res[650].exec("123A5XYZPQR", 1651));
+assertNull(res[650].exec("ABAx{660}XYZpqr", 1652));
+assertNull(res[650].exec("** Failers", 1653));
+assertNull(res[650].exec("AXYZ", 1654));
+assertNull(res[650].exec("XYZ ", 1655));
+assertNull(res[650].exec("1XYZ", 1656));
+assertNull(res[650].exec("AB=XYZ.. ", 1657));
+assertNull(res[650].exec("XYZ ", 1658));
+assertNull(res[650].exec("** Failers", 1659));
+assertNull(res[650].exec("WXYZ ", 1660));
+assertNull(res[655].exec("1234", 1661));
+assertNull(res[655].exec("1234", 1662));
+assertNull(res[655].exec("12-34", 1663));
+assertToStringEquals("{", res[655].exec("12+x{661}-34 "), 1664);
+assertNull(res[655].exec("** Failers", 1665));
+assertToStringEquals("d", res[655].exec("abcd "), 1666);
+assertToStringEquals("d", res[656].exec("abcd"), 1667);
+assertNull(res[656].exec("** Failers", 1668));
+assertNull(res[656].exec("1234", 1669));
+assertNull(res[657].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 1670));
+assertToStringEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[657].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 1671);
+assertToStringEquals(" ", res[657].exec(" "), 1672);
+assertNull(res[657].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 1673));
+assertToStringEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[657].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 1674);
+assertNull(res[658].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 1675));
+assertToStringEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[658].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 1676);
+assertNull(res[659].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 1677));
+assertNull(res[659].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 1678));
+assertNull(res[660].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 1679));
+assertToStringEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[660].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 1680);
+assertNull(res[661].exec("a", 1681));
+assertNull(res[661].exec("A ", 1682));
+assertNull(res[662].exec("a", 1683));
+assertNull(res[662].exec("A ", 1684));
+assertNull(res[663].exec("A", 1685));
+assertNull(res[663].exec("aZ", 1686));
+assertNull(res[663].exec("** Failers", 1687));
+assertNull(res[663].exec("abc ", 1688));
+assertNull(res[664].exec("A", 1689));
+assertNull(res[664].exec("aZ", 1690));
+assertNull(res[664].exec("** Failers", 1691));
+assertNull(res[664].exec("abc ", 1692));
+assertNull(res[665].exec("a", 1693));
+assertNull(res[665].exec("Az", 1694));
+assertNull(res[665].exec("** Failers", 1695));
+assertNull(res[665].exec("ABC ", 1696));
+assertNull(res[666].exec("a", 1697));
+assertNull(res[666].exec("Az", 1698));
+assertNull(res[666].exec("** Failers", 1699));
+assertNull(res[666].exec("ABC ", 1700));
+assertNull(res[666].exec("x{c0}", 1701));
+assertNull(res[666].exec("x{e0} ", 1702));
+assertNull(res[666].exec("x{c0}", 1703));
+assertNull(res[666].exec("x{e0} ", 1704));
+assertNull(res[666].exec("Ax{391}x{10427}x{ff3a}x{1fb0}", 1705));
+assertNull(res[666].exec("** Failers", 1706));
+assertNull(res[666].exec("ax{391}x{10427}x{ff3a}x{1fb0} ", 1707));
+assertNull(res[666].exec("Ax{3b1}x{10427}x{ff3a}x{1fb0}", 1708));
+assertNull(res[666].exec("Ax{391}x{1044F}x{ff3a}x{1fb0}", 1709));
+assertNull(res[666].exec("Ax{391}x{10427}x{ff5a}x{1fb0}", 1710));
+assertNull(res[666].exec("Ax{391}x{10427}x{ff3a}x{1fb8}", 1711));
+assertNull(res[666].exec("Ax{391}x{10427}x{ff3a}x{1fb0}", 1712));
+assertNull(res[666].exec("ax{391}x{10427}x{ff3a}x{1fb0} ", 1713));
+assertNull(res[666].exec("Ax{3b1}x{10427}x{ff3a}x{1fb0}", 1714));
+assertNull(res[666].exec("Ax{391}x{1044F}x{ff3a}x{1fb0}", 1715));
+assertNull(res[666].exec("Ax{391}x{10427}x{ff5a}x{1fb0}", 1716));
+assertNull(res[666].exec("Ax{391}x{10427}x{ff3a}x{1fb8}", 1717));
+assertNull(res[666].exec("x{391}x{3b1}x{3b1}x{3b1}x{391}", 1718));
+assertNull(res[666].exec("x{391}x{3b1}x{3b1}x{3b1}x{391}X", 1719));
+assertNull(res[666].exec("x{391}x{3b1}x{3b1}x{3b1}x{391}X", 1720));
+assertNull(res[666].exec("x{391}", 1721));
+assertNull(res[666].exec("x{ff3a}", 1722));
+assertNull(res[666].exec("x{3b1}", 1723));
+assertNull(res[666].exec("x{ff5a} ", 1724));
+assertNull(res[666].exec("x{c0}", 1725));
+assertNull(res[666].exec("x{e0} ", 1726));
+assertNull(res[666].exec("x{104}", 1727));
+assertNull(res[666].exec("x{105}", 1728));
+assertNull(res[666].exec("x{109} ", 1729));
+assertNull(res[666].exec("** Failers", 1730));
+assertNull(res[666].exec("x{100}", 1731));
+assertNull(res[666].exec("x{10a} ", 1732));
+assertNull(res[666].exec("Z", 1733));
+assertNull(res[666].exec("z", 1734));
+assertNull(res[666].exec("x{39c}", 1735));
+assertNull(res[666].exec("x{178}", 1736));
+assertNull(res[666].exec("|", 1737));
+assertNull(res[666].exec("x{80}", 1738));
+assertNull(res[666].exec("x{ff}", 1739));
+assertNull(res[666].exec("x{100}", 1740));
+assertNull(res[666].exec("x{101} ", 1741));
+assertNull(res[666].exec("** Failers", 1742));
+assertNull(res[666].exec("x{102}", 1743));
+assertNull(res[666].exec("Y", 1744));
+assertNull(res[666].exec("y ", 1745));
+assertNull(res[667].exec("A", 1746));
+assertNull(res[667].exec("Ax{300}BC ", 1747));
+assertNull(res[667].exec("Ax{300}x{301}x{302}BC ", 1748));
+assertNull(res[667].exec("*** Failers", 1749));
+assertNull(res[667].exec("x{300} ", 1750));
+assertToStringEquals("X", res[668].exec("X123"), 1751);
+assertNull(res[668].exec("*** Failers", 1752));
+assertNull(res[668].exec("AXYZ", 1753));
+assertNull(res[669].exec("Ax{300}x{301}x{302}BCAx{300}x{301} ", 1754));
+assertNull(res[669].exec("Ax{300}x{301}x{302}BCAx{300}x{301}C ", 1755));
+assertNull(res[670].exec("Ax{300}x{301}x{302}BCAx{300}x{301} ", 1756));
+assertNull(res[670].exec("Ax{300}x{301}x{302}BCAx{300}x{301}C ", 1757));
+assertToStringEquals("A,,A", res[671].exec("Ax{300}x{301}x{302}BCAx{300}x{301} "), 1758);
+assertToStringEquals("A,,A", res[671].exec("Ax{300}x{301}x{302}BCAx{300}x{301}C "), 1759);
+assertToStringEquals("A,,A", res[672].exec("Ax{300}x{301}x{302}BCAx{300}x{301} "), 1760);
+assertToStringEquals("A,,A", res[672].exec("Ax{300}x{301}x{302}BCAx{300}x{301}C "), 1761);
+assertNull(res[673].exec("*** Failers", 1762));
+assertNull(res[673].exec("Ax{300}x{301}x{302}", 1763));
+assertNull(res[674].exec("Ax{300}x{301}Bx{300}X", 1764));
+assertNull(res[674].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}", 1765));
+assertNull(res[674].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}X", 1766));
+assertNull(res[674].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}DAx{300}X", 1767));
+assertNull(res[675].exec("Ax{300}x{301}Bx{300}X", 1768));
+assertNull(res[675].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}", 1769));
+assertNull(res[675].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}X", 1770));
+assertNull(res[675].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}DAx{300}X", 1771));
+assertNull(res[675].exec("x{2e81}x{3007}x{2f804}x{31a0}", 1772));
+assertNull(res[675].exec("** Failers", 1773));
+assertNull(res[675].exec("x{2e7f} ", 1774));
+assertNull(res[675].exec("x{3105}", 1775));
+assertNull(res[675].exec("** Failers", 1776));
+assertNull(res[675].exec("x{30ff} ", 1777));
+assertNull(res[676].exec("x{06e9}", 1778));
+assertNull(res[676].exec("x{060b}", 1779));
+assertNull(res[676].exec("** Failers", 1780));
+assertNull(res[676].exec("Xx{06e9} ", 1781));
+assertNull(res[677].exec("x{2f800}", 1782));
+assertNull(res[677].exec("** Failers", 1783));
+assertNull(res[677].exec("x{a014}", 1784));
+assertNull(res[677].exec("x{a4c6} ", 1785));
+assertNull(res[678].exec("AXYZ", 1786));
+assertNull(res[678].exec("x{1234}XYZ ", 1787));
+assertNull(res[678].exec("** Failers", 1788));
+assertNull(res[678].exec("X ", 1789));
+assertNull(res[679].exec("** Failers", 1790));
+assertNull(res[679].exec("AX", 1791));
+assertNull(res[680].exec("XYZ", 1792));
+assertNull(res[680].exec("AXYZ", 1793));
+assertNull(res[680].exec("x{1234}XYZ ", 1794));
+assertNull(res[680].exec("** Failers", 1795));
+assertNull(res[680].exec("ABXYZ ", 1796));
+assertNull(res[681].exec("XYZ", 1797));
+assertNull(res[681].exec("** Failers", 1798));
+assertNull(res[681].exec("AXYZ", 1799));
+assertNull(res[681].exec("x{1234}XYZ ", 1800));
+assertNull(res[681].exec("ABXYZ ", 1801));
+assertNull(res[681].exec("AXYZ", 1802));
+assertNull(res[681].exec("x{1234}XYZ", 1803));
+assertNull(res[681].exec("Ax{1234}XYZ", 1804));
+assertNull(res[681].exec("** Failers", 1805));
+assertNull(res[681].exec("XYZ", 1806));
+assertNull(res[681].exec("** Failers", 1807));
+assertNull(res[681].exec("AXYZ", 1808));
+assertNull(res[681].exec("x{1234}XYZ", 1809));
+assertNull(res[681].exec("Ax{1234}XYZ", 1810));
+assertNull(res[681].exec("XYZ", 1811));
+assertNull(res[682].exec("XYZ", 1812));
+assertNull(res[682].exec("AXYZ", 1813));
+assertNull(res[682].exec("x{1234}XYZ", 1814));
+assertNull(res[682].exec("Ax{1234}XYZ", 1815));
+assertNull(res[682].exec("** Failers", 1816));
+assertNull(res[683].exec("XYZ", 1817));
+assertNull(res[683].exec("** Failers", 1818));
+assertNull(res[683].exec("AXYZ", 1819));
+assertNull(res[683].exec("x{1234}XYZ", 1820));
+assertNull(res[683].exec("Ax{1234}XYZ", 1821));
+assertToStringEquals("AX", res[684].exec("AXYZ"), 1822);
+assertNull(res[684].exec("x{1234}XYZ ", 1823));
+assertNull(res[684].exec("** Failers", 1824));
+assertNull(res[684].exec("X ", 1825));
+assertNull(res[685].exec("** Failers", 1826));
+assertToStringEquals("AX", res[685].exec("AX"), 1827);
+assertToStringEquals("X", res[686].exec("XYZ"), 1828);
+assertToStringEquals("AX", res[686].exec("AXYZ"), 1829);
+assertNull(res[686].exec("x{1234}XYZ ", 1830));
+assertNull(res[686].exec("** Failers", 1831));
+assertNull(res[686].exec("ABXYZ ", 1832));
+assertToStringEquals("X", res[687].exec("XYZ"), 1833);
+assertNull(res[687].exec("** Failers", 1834));
+assertToStringEquals("AX", res[687].exec("AXYZ"), 1835);
+assertNull(res[687].exec("x{1234}XYZ ", 1836));
+assertNull(res[687].exec("ABXYZ ", 1837));
+assertToStringEquals("AX", res[688].exec("AXYZ"), 1838);
+assertNull(res[688].exec("x{1234}XYZ", 1839));
+assertNull(res[688].exec("Ax{1234}XYZ", 1840));
+assertNull(res[688].exec("** Failers", 1841));
+assertNull(res[688].exec("XYZ", 1842));
+assertNull(res[689].exec("** Failers", 1843));
+assertToStringEquals("AX", res[689].exec("AXYZ"), 1844);
+assertNull(res[689].exec("x{1234}XYZ", 1845));
+assertNull(res[689].exec("Ax{1234}XYZ", 1846));
+assertNull(res[689].exec("XYZ", 1847));
+assertToStringEquals("X", res[690].exec("XYZ"), 1848);
+assertToStringEquals("AX", res[690].exec("AXYZ"), 1849);
+assertNull(res[690].exec("x{1234}XYZ", 1850));
+assertNull(res[690].exec("Ax{1234}XYZ", 1851));
+assertNull(res[690].exec("** Failers", 1852));
+assertToStringEquals("X", res[691].exec("XYZ"), 1853);
+assertNull(res[691].exec("** Failers", 1854));
+assertToStringEquals("AX", res[691].exec("AXYZ"), 1855);
+assertNull(res[691].exec("x{1234}XYZ", 1856));
+assertNull(res[691].exec("Ax{1234}XYZ", 1857));
+assertNull(res[692].exec("abcdefgh", 1858));
+assertNull(res[692].exec("x{1234}\n\x0dx{3456}xyz ", 1859));
+assertNull(res[693].exec("abcdefgh", 1860));
+assertNull(res[693].exec("x{1234}\n\x0dx{3456}xyz ", 1861));
+assertNull(res[694].exec("** Failers", 1862));
+assertNull(res[694].exec("abcdefgh", 1863));
+assertNull(res[694].exec("x{1234}\n\x0dx{3456}xyz ", 1864));
+assertNull(res[695].exec(" AXY", 1865));
+assertNull(res[695].exec(" aXY", 1866));
+assertNull(res[695].exec(" x{1c5}XY", 1867));
+assertNull(res[695].exec(" ** Failers", 1868));
+assertNull(res[695].exec(" x{1bb}XY", 1869));
+assertNull(res[695].exec(" x{2b0}XY", 1870));
+assertNull(res[695].exec(" !XY ", 1871));
+assertNull(res[696].exec(" AXY", 1872));
+assertNull(res[696].exec(" aXY", 1873));
+assertNull(res[696].exec(" x{1c5}XY", 1874));
+assertNull(res[696].exec(" ** Failers", 1875));
+assertNull(res[696].exec(" x{1bb}XY", 1876));
+assertNull(res[696].exec(" x{2b0}XY", 1877));
+assertNull(res[696].exec(" !XY ", 1878));
+assertNull(res[696].exec(" AXY", 1879));
+assertNull(res[696].exec(" aXY", 1880));
+assertNull(res[696].exec(" AbcdeXyz ", 1881));
+assertNull(res[696].exec(" x{1c5}AbXY", 1882));
+assertNull(res[696].exec(" abcDEXypqreXlmn ", 1883));
+assertNull(res[696].exec(" ** Failers", 1884));
+assertNull(res[696].exec(" x{1bb}XY", 1885));
+assertNull(res[696].exec(" x{2b0}XY", 1886));
+assertNull(res[696].exec(" !XY ", 1887));
+assertNull(res[697].exec(" AXY", 1888));
+assertNull(res[697].exec(" aXY", 1889));
+assertNull(res[697].exec(" AbcdeXyz ", 1890));
+assertNull(res[697].exec(" x{1c5}AbXY", 1891));
+assertNull(res[697].exec(" abcDEXypqreXlmn ", 1892));
+assertNull(res[697].exec(" ** Failers", 1893));
+assertNull(res[697].exec(" x{1bb}XY", 1894));
+assertNull(res[697].exec(" x{2b0}XY", 1895));
+assertNull(res[697].exec(" !XY ", 1896));
+assertNull(res[697].exec(" AXY", 1897));
+assertNull(res[697].exec(" aXY", 1898));
+assertNull(res[697].exec(" AbcdeXyz ", 1899));
+assertNull(res[697].exec(" x{1c5}AbXY", 1900));
+assertNull(res[697].exec(" abcDEXypqreXlmn ", 1901));
+assertNull(res[697].exec(" ** Failers", 1902));
+assertNull(res[697].exec(" x{1bb}XY", 1903));
+assertNull(res[697].exec(" x{2b0}XY", 1904));
+assertNull(res[697].exec(" !XY ", 1905));
+assertNull(res[698].exec(" AXY", 1906));
+assertNull(res[698].exec(" aXY", 1907));
+assertNull(res[698].exec(" AbcdeXyz ", 1908));
+assertNull(res[698].exec(" x{1c5}AbXY", 1909));
+assertNull(res[698].exec(" abcDEXypqreXlmn ", 1910));
+assertNull(res[698].exec(" ** Failers", 1911));
+assertNull(res[698].exec(" x{1bb}XY", 1912));
+assertNull(res[698].exec(" x{2b0}XY", 1913));
+assertNull(res[698].exec(" !XY ", 1914));
+assertNull(res[699].exec(" !XY", 1915));
+assertNull(res[699].exec(" x{1bb}XY", 1916));
+assertNull(res[699].exec(" x{2b0}XY", 1917));
+assertNull(res[699].exec(" ** Failers", 1918));
+assertNull(res[699].exec(" x{1c5}XY", 1919));
+assertNull(res[699].exec(" AXY ", 1920));
+assertNull(res[700].exec(" !XY", 1921));
+assertNull(res[700].exec(" x{1bb}XY", 1922));
+assertNull(res[700].exec(" x{2b0}XY", 1923));
+assertNull(res[700].exec(" ** Failers", 1924));
+assertNull(res[700].exec(" x{1c5}XY", 1925));
+assertNull(res[700].exec(" AXY ", 1926));
+assertNull(res[701].exec("\xa0!", 1927));
+assertNull(res[701].exec("AabcabcYZ ", 1928));
+assertToStringEquals("L=abcX,L=abc,abc", res[702].exec("L=abcX"), 1929);
+assertNull(res[702].exec("x{c0}", 1930));
+assertNull(res[702].exec("x{e0} ", 1931));
+assertNull(res[702].exec("x{c0}", 1932));
+assertNull(res[702].exec("x{e0} ", 1933));
+assertNull(res[703].exec("x{1b00}x{12000}x{7c0}x{a840}x{10900}", 1934));
+assertNull(res[706].exec("123abcdefg", 1935));
+assertNull(res[706].exec("123abc\xc4\xc5zz", 1936));
+assertNull(res[710].exec("A\x80", 1937));
+assertNull(res[725].exec("x{60e} ", 1938));
+assertNull(res[725].exec("x{656} ", 1939));
+assertNull(res[725].exec("x{657} ", 1940));
+assertNull(res[725].exec("x{658} ", 1941));
+assertNull(res[725].exec("x{659} ", 1942));
+assertNull(res[725].exec("x{65a} ", 1943));
+assertNull(res[725].exec("x{65b} ", 1944));
+assertNull(res[725].exec("x{65c} ", 1945));
+assertNull(res[725].exec("x{65d} ", 1946));
+assertNull(res[725].exec("x{65e} ", 1947));
+assertNull(res[725].exec("x{66a} ", 1948));
+assertNull(res[725].exec("x{6e9} ", 1949));
+assertNull(res[725].exec("x{6ef}", 1950));
+assertNull(res[725].exec("x{6fa} ", 1951));
+assertNull(res[725].exec("** Failers", 1952));
+assertNull(res[725].exec("x{600}", 1953));
+assertNull(res[725].exec("x{650}", 1954));
+assertNull(res[725].exec("x{651} ", 1955));
+assertNull(res[725].exec("x{652} ", 1956));
+assertNull(res[725].exec("x{653} ", 1957));
+assertNull(res[725].exec("x{654} ", 1958));
+assertNull(res[725].exec("x{655} ", 1959));
+assertNull(res[725].exec("x{65f} ", 1960));
+assertNull(res[726].exec("x{1d2b} ", 1961));
+assertNull(res[727].exec("x{589}", 1962));
+assertNull(res[727].exec("x{60c}", 1963));
+assertNull(res[727].exec("x{61f} ", 1964));
+assertNull(res[727].exec("x{964}", 1965));
+assertNull(res[727].exec("x{965} ", 1966));
+assertNull(res[727].exec("x{970} ", 1967));
+assertNull(res[728].exec("x{64b}", 1968));
+assertNull(res[728].exec("x{654}", 1969));
+assertNull(res[728].exec("x{655}", 1970));
+assertNull(res[728].exec("x{200c} ", 1971));
+assertNull(res[728].exec("** Failers", 1972));
+assertNull(res[728].exec("x{64a}", 1973));
+assertNull(res[728].exec("x{656} ", 1974));
+assertNull(res[729].exec("x{10450}", 1975));
+assertNull(res[729].exec("x{1047f}", 1976));
+assertNull(res[730].exec("x{10400}", 1977));
+assertNull(res[730].exec("x{1044f}", 1978));
+assertNull(res[731].exec("x{10480}", 1979));
+assertNull(res[731].exec("x{1049d}", 1980));
+assertNull(res[731].exec("x{104a0}", 1981));
+assertNull(res[731].exec("x{104a9}", 1982));
+assertNull(res[731].exec("** Failers", 1983));
+assertNull(res[731].exec("x{1049e}", 1984));
+assertNull(res[731].exec("x{1049f}", 1985));
+assertNull(res[731].exec("x{104aa} ", 1986));
+assertNull(res[731].exec("\xe2\x80\xa8\xe2\x80\xa8", 1987));
+assertNull(res[731].exec("x{2028}x{2028}x{2028}", 1988));
+assertNull(res[732].exec("x{c0}x{e0}x{116}x{117}", 1989));
+assertNull(res[732].exec("x{c0}x{e0}x{116}x{117}", 1990));
+assertNull(res[733].exec("x{102A4}x{AA52}x{A91D}x{1C46}x{10283}x{1092E}x{1C6B}x{A93B}x{A8BF}x{1BA0}x{A50A}====", 1991));
+assertNull(res[733].exec("x{a77d}x{1d79}", 1992));
+assertNull(res[733].exec("x{1d79}x{a77d} ", 1993));
+assertNull(res[733].exec("x{a77d}x{1d79}", 1994));
+assertNull(res[733].exec("** Failers ", 1995));
+assertNull(res[733].exec("x{1d79}x{a77d} ", 1996));
+assertToStringEquals("AA,A", res[734].exec("AA"), 1997);
+assertToStringEquals("Aa,A", res[734].exec("Aa"), 1998);
+assertToStringEquals("aa,a", res[734].exec("aa"), 1999);
+assertToStringEquals("aA,a", res[734].exec("aA"), 2000);
+assertNull(res[734].exec("x{de}x{de}", 2001));
+assertNull(res[734].exec("x{de}x{fe}", 2002));
+assertNull(res[734].exec("x{fe}x{fe}", 2003));
+assertNull(res[734].exec("x{fe}x{de}", 2004));
+assertNull(res[734].exec("x{10a}x{10a}", 2005));
+assertNull(res[734].exec("x{10a}x{10b}", 2006));
+assertNull(res[734].exec("x{10b}x{10b}", 2007));
+assertNull(res[734].exec("x{10b}x{10a}", 2008));
+assertToStringEquals("abc", res[736].exec("abc"), 2009);
+assertToStringEquals("abc", res[737].exec("abc"), 2010);
+assertToStringEquals("abbbbc", res[737].exec("abbbbc"), 2011);
+assertToStringEquals("ac", res[737].exec("ac"), 2012);
+assertToStringEquals("abc", res[738].exec("abc"), 2013);
+assertToStringEquals("abbbbbbc", res[738].exec("abbbbbbc"), 2014);
+assertNull(res[738].exec("*** Failers ", 2015));
+assertNull(res[738].exec("ac", 2016));
+assertNull(res[738].exec("ab", 2017));
+assertToStringEquals("a", res[739].exec("a"), 2018);
+assertToStringEquals("aaaaaaaaaaaaaaaaa", res[739].exec("aaaaaaaaaaaaaaaaa"), 2019);
+assertToStringEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[739].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa "), 2020);
+assertToStringEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[739].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaF "), 2021);
+assertToStringEquals("a,a", res[740].exec("a"), 2022);
+assertToStringEquals("a,a", res[740].exec("abcd"), 2023);
+assertToStringEquals("a,a", res[740].exec("african"), 2024);
+assertToStringEquals("abc", res[741].exec("abcdef"), 2025);
+assertNull(res[741].exec("*** Failers", 2026));
+assertNull(res[741].exec("xyzabc", 2027));
+assertNull(res[741].exec("xyz\nabc ", 2028));
+assertToStringEquals("abc", res[742].exec("abcdef"), 2029);
+assertToStringEquals("abc", res[742].exec("xyz\nabc "), 2030);
+assertNull(res[742].exec("*** Failers", 2031));
+assertNull(res[742].exec("xyzabc", 2032));
+assertNull(res[743].exec("abcdef", 2033));
+assertNull(res[743].exec("*** Failers", 2034));
+assertNull(res[743].exec("xyzabc", 2035));
+assertNull(res[743].exec("xyz\nabc ", 2036));
+assertNull(res[744].exec("abcdef", 2037));
+assertNull(res[744].exec("*** Failers", 2038));
+assertNull(res[744].exec("xyzabc", 2039));
+assertNull(res[744].exec("xyz\nabc ", 2040));
+assertNull(res[745].exec("abcdef", 2041));
+assertNull(res[745].exec("xyzabc>3", 2042));
+assertNull(res[745].exec("*** Failers", 2043));
+assertNull(res[745].exec("xyzabc ", 2044));
+assertNull(res[745].exec("xyzabc>2 ", 2045));
+assertToStringEquals("x9yzz", res[746].exec("x9yzz"), 2046);
+assertToStringEquals("x0y+z", res[746].exec("x0y+z"), 2047);
+assertNull(res[746].exec("*** Failers", 2048));
+assertNull(res[746].exec("xyz", 2049));
+assertNull(res[746].exec("xxy0z ", 2050));
+assertToStringEquals("x yzz", res[747].exec("x yzz"), 2051);
+assertToStringEquals("x y+z", res[747].exec("x y+z"), 2052);
+assertNull(res[747].exec("*** Failers", 2053));
+assertNull(res[747].exec("xyz", 2054));
+assertNull(res[747].exec("xxyyz", 2055));
+assertToStringEquals("xxy+z", res[748].exec("xxy+z"), 2056);
+assertNull(res[748].exec("*** Failers", 2057));
+assertNull(res[748].exec("xxy0z", 2058));
+assertNull(res[748].exec("x+y+z ", 2059));
+assertToStringEquals("x+y", res[749].exec("x+y"), 2060);
+assertToStringEquals("x-y", res[749].exec("x-y"), 2061);
+assertNull(res[749].exec("*** Failers", 2062));
+assertNull(res[749].exec("x\ny", 2063));
+assertToStringEquals("x+y", res[750].exec("x+y"), 2064);
+assertToStringEquals("x-y", res[750].exec("x-y"), 2065);
+assertNull(res[750].exec("x\ny", 2066));
+assertNull(res[750].exec("a+bc+dp+q", 2067));
+assertNull(res[750].exec("a+bc\ndp+q", 2068));
+assertNull(res[750].exec("x\nyp+q ", 2069));
+assertNull(res[750].exec("*** Failers ", 2070));
+assertNull(res[750].exec("a\nbc\ndp+q", 2071));
+assertNull(res[750].exec("a+bc\ndp\nq", 2072));
+assertNull(res[750].exec("x\nyp\nq ", 2073));
+assertNull(res[751].exec("ba0", 2074));
+assertNull(res[751].exec("*** Failers", 2075));
+assertNull(res[751].exec("ba0\n", 2076));
+assertNull(res[751].exec("ba0\ncd ", 2077));
+assertNull(res[752].exec("ba0", 2078));
+assertNull(res[752].exec("*** Failers", 2079));
+assertNull(res[752].exec("ba0\n", 2080));
+assertNull(res[752].exec("ba0\ncd ", 2081));
+assertNull(res[753].exec("ba0", 2082));
+assertNull(res[753].exec("ba0\n", 2083));
+assertNull(res[753].exec("*** Failers", 2084));
+assertNull(res[753].exec("ba0\ncd ", 2085));
+assertNull(res[754].exec("ba0", 2086));
+assertNull(res[754].exec("ba0\n", 2087));
+assertNull(res[754].exec("*** Failers", 2088));
+assertNull(res[754].exec("ba0\ncd ", 2089));
+assertToStringEquals("a0", res[755].exec("ba0"), 2090);
+assertNull(res[755].exec("ba0\n", 2091));
+assertNull(res[755].exec("*** Failers", 2092));
+assertNull(res[755].exec("ba0\ncd ", 2093));
+assertToStringEquals("a0", res[756].exec("ba0"), 2094);
+assertToStringEquals("a0", res[756].exec("ba0\n"), 2095);
+assertToStringEquals("a0", res[756].exec("ba0\ncd "), 2096);
+assertNull(res[756].exec("*** Failers", 2097));
+assertToStringEquals("abc", res[757].exec("abc"), 2098);
+assertToStringEquals("aBc", res[757].exec("aBc"), 2099);
+assertToStringEquals("ABC", res[757].exec("ABC"), 2100);
+assertToStringEquals("b", res[758].exec("abcd"), 2101);
+assertToStringEquals("abz", res[759].exec("abz"), 2102);
+assertToStringEquals("abb", res[759].exec("abbz"), 2103);
+assertToStringEquals("az", res[759].exec("azz "), 2104);
+assertToStringEquals("yz", res[760].exec("ayzq"), 2105);
+assertToStringEquals("xyz", res[760].exec("axyzq"), 2106);
+assertToStringEquals("xxyz", res[760].exec("axxyz"), 2107);
+assertToStringEquals("xxxyz", res[760].exec("axxxyzq"), 2108);
+assertToStringEquals("xxxyz", res[760].exec("axxxxyzq"), 2109);
+assertNull(res[760].exec("*** Failers", 2110));
+assertNull(res[760].exec("ax", 2111));
+assertNull(res[760].exec("axx ", 2112));
+assertNull(res[760].exec(" ", 2113));
+assertToStringEquals("xxxyz", res[761].exec("axxxyzq"), 2114);
+assertToStringEquals("xxxyz", res[761].exec("axxxxyzq"), 2115);
+assertNull(res[761].exec("*** Failers", 2116));
+assertNull(res[761].exec("ax", 2117));
+assertNull(res[761].exec("axx ", 2118));
+assertNull(res[761].exec("ayzq", 2119));
+assertNull(res[761].exec("axyzq", 2120));
+assertNull(res[761].exec("axxyz", 2121));
+assertNull(res[761].exec(" ", 2122));
+assertToStringEquals("xxyz", res[762].exec("axxyz"), 2123);
+assertToStringEquals("xxxyz", res[762].exec("axxxyzq"), 2124);
+assertToStringEquals("xxxyz", res[762].exec("axxxxyzq"), 2125);
+assertNull(res[762].exec("*** Failers", 2126));
+assertNull(res[762].exec("ax", 2127));
+assertNull(res[762].exec("axx ", 2128));
+assertNull(res[762].exec("ayzq", 2129));
+assertNull(res[762].exec("axyzq", 2130));
+assertNull(res[762].exec(" ", 2131));
+assertToStringEquals("b", res[763].exec("bac"), 2132);
+assertToStringEquals("bcdef", res[763].exec("bcdefax"), 2133);
+assertToStringEquals("*** F", res[763].exec("*** Failers"), 2134);
+assertToStringEquals(" ", res[763].exec("aaaaa "), 2135);
+assertToStringEquals("b", res[764].exec("bac"), 2136);
+assertToStringEquals("bcdef", res[764].exec("bcdefax"), 2137);
+assertToStringEquals("*** F", res[764].exec("*** Failers"), 2138);
+assertToStringEquals("", res[764].exec("aaaaa "), 2139);
+assertToStringEquals("xyz", res[765].exec("xyz"), 2140);
+assertToStringEquals("wxyz", res[765].exec("awxyza"), 2141);
+assertToStringEquals("bcdef", res[765].exec("abcdefa"), 2142);
+assertToStringEquals("bcdef", res[765].exec("abcdefghijk"), 2143);
+assertToStringEquals("*** F", res[765].exec("*** Failers"), 2144);
+assertNull(res[765].exec("axya", 2145));
+assertNull(res[765].exec("axa", 2146));
+assertToStringEquals(" ", res[765].exec("aaaaa "), 2147);
+assertToStringEquals("1234", res[766].exec("1234b567"), 2148);
+assertToStringEquals("", res[766].exec("xyz"), 2149);
+assertToStringEquals("a", res[767].exec("a1234b567"), 2150);
+assertToStringEquals("xyz", res[767].exec("xyz"), 2151);
+assertToStringEquals(" ", res[767].exec(" "), 2152);
+assertToStringEquals("1234", res[768].exec("ab1234c56"), 2153);
+assertNull(res[768].exec("*** Failers", 2154));
+assertNull(res[768].exec("xyz", 2155));
+assertToStringEquals("ab", res[769].exec("ab123c56"), 2156);
+assertToStringEquals("*** Failers", res[769].exec("*** Failers"), 2157);
+assertNull(res[769].exec("789", 2158));
+assertToStringEquals("5A", res[770].exec("045ABC"), 2159);
+assertToStringEquals("A", res[770].exec("ABC"), 2160);
+assertNull(res[770].exec("*** Failers", 2161));
+assertNull(res[770].exec("XYZ", 2162));
+assertToStringEquals("A", res[771].exec("ABC"), 2163);
+assertToStringEquals("BA", res[771].exec("BAC"), 2164);
+assertToStringEquals("A", res[771].exec("9ABC "), 2165);
+assertNull(res[771].exec("*** Failers", 2166));
+assertToStringEquals("aaaa", res[772].exec("aaaa"), 2167);
+assertToStringEquals("xyz", res[773].exec("xyz"), 2168);
+assertToStringEquals("ggggggggxyz", res[773].exec("ggggggggxyz"), 2169);
+assertToStringEquals("abcdxyz", res[774].exec("abcdxyz"), 2170);
+assertToStringEquals("axyz", res[774].exec("axyz"), 2171);
+assertNull(res[774].exec("*** Failers", 2172));
+assertNull(res[774].exec("xyz", 2173));
+assertToStringEquals("xyz", res[775].exec("xyz"), 2174);
+assertToStringEquals("cxyz", res[775].exec("cxyz "), 2175);
+assertToStringEquals("12X", res[776].exec("12X"), 2176);
+assertToStringEquals("123X", res[776].exec("123X"), 2177);
+assertNull(res[776].exec("*** Failers", 2178));
+assertNull(res[776].exec("X", 2179));
+assertNull(res[776].exec("1X", 2180));
+assertNull(res[776].exec("1234X ", 2181));
+assertToStringEquals("a4", res[777].exec("a45"), 2182);
+assertToStringEquals("b9", res[777].exec("b93"), 2183);
+assertToStringEquals("c9", res[777].exec("c99z"), 2184);
+assertToStringEquals("d0", res[777].exec("d04"), 2185);
+assertNull(res[777].exec("*** Failers", 2186));
+assertNull(res[777].exec("e45", 2187));
+assertNull(res[777].exec("abcd ", 2188));
+assertNull(res[777].exec("abcd1234", 2189));
+assertNull(res[777].exec("1234 ", 2190));
+assertToStringEquals("a4", res[778].exec("a45"), 2191);
+assertToStringEquals("b9", res[778].exec("b93"), 2192);
+assertToStringEquals("c9", res[778].exec("c99z"), 2193);
+assertToStringEquals("d0", res[778].exec("d04"), 2194);
+assertToStringEquals("abcd1", res[778].exec("abcd1234"), 2195);
+assertToStringEquals("1", res[778].exec("1234 "), 2196);
+assertNull(res[778].exec("*** Failers", 2197));
+assertNull(res[778].exec("e45", 2198));
+assertNull(res[778].exec("abcd ", 2199));
+assertToStringEquals("a4", res[779].exec("a45"), 2200);
+assertToStringEquals("b9", res[779].exec("b93"), 2201);
+assertToStringEquals("c9", res[779].exec("c99z"), 2202);
+assertToStringEquals("d0", res[779].exec("d04"), 2203);
+assertToStringEquals("abcd1", res[779].exec("abcd1234"), 2204);
+assertNull(res[779].exec("*** Failers", 2205));
+assertNull(res[779].exec("1234 ", 2206));
+assertNull(res[779].exec("e45", 2207));
+assertNull(res[779].exec("abcd ", 2208));
+assertToStringEquals("aX", res[780].exec("aX"), 2209);
+assertToStringEquals("aaX", res[780].exec("aaX "), 2210);
+assertToStringEquals("a4", res[781].exec("a45"), 2211);
+assertToStringEquals("b9", res[781].exec("b93"), 2212);
+assertToStringEquals("c9", res[781].exec("c99z"), 2213);
+assertToStringEquals("d0", res[781].exec("d04"), 2214);
+assertToStringEquals("1", res[781].exec("1234 "), 2215);
+assertNull(res[781].exec("*** Failers", 2216));
+assertNull(res[781].exec("abcd1234", 2217));
+assertNull(res[781].exec("e45", 2218));
+assertToStringEquals("ab4", res[782].exec("ab45"), 2219);
+assertToStringEquals("bcd9", res[782].exec("bcd93"), 2220);
+assertNull(res[782].exec("*** Failers", 2221));
+assertNull(res[782].exec("1234 ", 2222));
+assertNull(res[782].exec("a36 ", 2223));
+assertNull(res[782].exec("abcd1234", 2224));
+assertNull(res[782].exec("ee45", 2225));
+assertToStringEquals("abc4,abc", res[783].exec("abc45"), 2226);
+assertToStringEquals("abcabcabc4,abc", res[783].exec("abcabcabc45"), 2227);
+assertToStringEquals("4,", res[783].exec("42xyz "), 2228);
+assertNull(res[783].exec("*** Failers", 2229));
+assertToStringEquals("abc4,abc", res[784].exec("abc45"), 2230);
+assertToStringEquals("abcabcabc4,abc", res[784].exec("abcabcabc45"), 2231);
+assertNull(res[784].exec("*** Failers", 2232));
+assertNull(res[784].exec("42xyz ", 2233));
+assertToStringEquals("abc4,abc", res[785].exec("abc45"), 2234);
+assertToStringEquals("4,", res[785].exec("42xyz "), 2235);
+assertNull(res[785].exec("*** Failers", 2236));
+assertNull(res[785].exec("abcabcabc45", 2237));
+assertToStringEquals("abcabc4,abc", res[786].exec("abcabc45"), 2238);
+assertToStringEquals("abcabcabc4,abc", res[786].exec("abcabcabc45"), 2239);
+assertNull(res[786].exec("*** Failers", 2240));
+assertNull(res[786].exec("abcabcabcabc45", 2241));
+assertNull(res[786].exec("abc45", 2242));
+assertNull(res[786].exec("42xyz ", 2243));
+assertNull(res[786].exec("1abc2abc3456", 2244));
+assertNull(res[786].exec("1abc2xyz3456 ", 2245));
+assertToStringEquals("ab=ab,ab,ab", res[787].exec("ab=ab"), 2246);
+assertToStringEquals("ab=ab,ab,ab", res[787].exec("ab=ab"), 2247);
+assertNull(res[787].exec("abc", 2248));
+assertNull(res[787].exec("a(b)c", 2249));
+assertNull(res[787].exec("a(b(c))d ", 2250));
+assertNull(res[787].exec("*** Failers)", 2251));
+assertNull(res[787].exec("a(b(c)d ", 2252));
+assertNull(res[787].exec(">abc>123<xyz<", 2253));
+assertNull(res[787].exec(">abc>1(2)3<xyz<", 2254));
+assertNull(res[787].exec(">abc>(1(2)3)<xyz<", 2255));
+assertNull(res[787].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9876", 2256));
+assertNull(res[787].exec("*** Failers ", 2257));
+assertNull(res[787].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 2258));
+assertNull(res[787].exec("<>", 2259));
+assertNull(res[787].exec("<abcd>", 2260));
+assertNull(res[787].exec("<abc <123> hij>", 2261));
+assertNull(res[787].exec("<abc <def> hij>", 2262));
+assertNull(res[787].exec("<abc<>def> ", 2263));
+assertNull(res[787].exec("<abc<> ", 2264));
+assertNull(res[787].exec("*** Failers", 2265));
+assertNull(res[787].exec("<abc", 2266));
+assertNull(res[787].exec("abc: ", 2267));
+assertNull(res[787].exec("12 ", 2268));
+assertNull(res[787].exec("*** Failers ", 2269));
+assertNull(res[787].exec("123 ", 2270));
+assertNull(res[787].exec("xyz ", 2271));
+assertNull(res[787].exec(" ", 2272));
+assertNull(res[787].exec("abc: ", 2273));
+assertNull(res[787].exec("12 ", 2274));
+assertNull(res[787].exec("*** Failers", 2275));
+assertNull(res[787].exec("123", 2276));
+assertNull(res[787].exec("xyz ", 2277));
+assertNull(res[788].exec("abcde: ", 2278));
+assertNull(res[788].exec("*** Failers ", 2279));
+assertNull(res[788].exec("abc.. ", 2280));
+assertNull(res[788].exec("123 ", 2281));
+assertNull(res[788].exec("vwxyz ", 2282));
+assertNull(res[788].exec(" ", 2283));
+assertNull(res[789].exec("12 ", 2284));
+assertNull(res[789].exec("*** Failers", 2285));
+assertNull(res[789].exec("abcde:", 2286));
+assertNull(res[789].exec("abc.. ", 2287));
+assertNull(res[789].exec("123", 2288));
+assertNull(res[789].exec("vwxyz ", 2289));
+assertNull(res[789].exec("abc12345", 2290));
+assertNull(res[789].exec("wxy123z", 2291));
+assertNull(res[789].exec("*** Failers", 2292));
+assertNull(res[789].exec("123abc", 2293));
+assertNull(res[789].exec("123abc", 2294));
+assertNull(res[789].exec("mno123456 ", 2295));
+assertNull(res[789].exec("*** Failers", 2296));
+assertNull(res[789].exec("abc12345", 2297));
+assertNull(res[789].exec("wxy123z", 2298));
+assertNull(res[789].exec("abcxyz", 2299));
+assertNull(res[789].exec("123abcxyz999 ", 2300));
+assertToStringEquals("abc", res[791].exec("abcdef"), 2301);
+assertNull(res[791].exec("*** Failers", 2302));
+assertToStringEquals("abc", res[791].exec("abcdefB "), 2303);
+assertToStringEquals(",", res[792].exec("bcd"), 2304);
+assertToStringEquals("aaa,aaa", res[792].exec("aaabcd"), 2305);
+assertToStringEquals(",", res[792].exec("xyz"), 2306);
+assertToStringEquals(",", res[792].exec("xyzN "), 2307);
+assertToStringEquals(",", res[792].exec("*** Failers"), 2308);
+assertToStringEquals(",", res[792].exec("bcdN "), 2309);
+assertToStringEquals("xyz", res[793].exec("xyz"), 2310);
+assertNull(res[793].exec("xyz\n", 2311));
+assertNull(res[793].exec("*** Failers", 2312));
+assertNull(res[793].exec("xyzZ", 2313));
+assertNull(res[793].exec("xyz\nZ ", 2314));
+assertToStringEquals("xyz", res[794].exec("xyz"), 2315);
+assertToStringEquals("xyz", res[794].exec("xyz\n "), 2316);
+assertToStringEquals("xyz", res[794].exec("abcxyz\npqr "), 2317);
+assertToStringEquals("xyz", res[794].exec("abcxyz\npqrZ "), 2318);
+assertToStringEquals("xyz", res[794].exec("xyz\nZ "), 2319);
+assertNull(res[794].exec("*** Failers", 2320));
+assertNull(res[794].exec("xyzZ", 2321));
+assertNull(res[795].exec("abcdef", 2322));
+assertNull(res[795].exec("defabcxyz>3 ", 2323));
+assertNull(res[795].exec("*** Failers ", 2324));
+assertNull(res[795].exec("defabcxyz", 2325));
+assertNull(res[796].exec("abP", 2326));
+assertNull(res[796].exec("abcdeP", 2327));
+assertToStringEquals("abcdef", res[796].exec("abcdefP"), 2328);
+assertNull(res[796].exec("*** Failers", 2329));
+assertNull(res[796].exec("abxP ", 2330));
+assertNull(res[797].exec("aP", 2331));
+assertNull(res[797].exec("aaP", 2332));
+assertNull(res[797].exec("aa2P ", 2333));
+assertNull(res[797].exec("aaaP", 2334));
+assertNull(res[797].exec("aaa23P ", 2335));
+assertNull(res[797].exec("aaaa12345P", 2336));
+assertToStringEquals("aa0z", res[797].exec("aa0zP"), 2337);
+assertToStringEquals("aaaa4444444444444z", res[797].exec("aaaa4444444444444zP "), 2338);
+assertNull(res[797].exec("*** Failers", 2339));
+assertNull(res[797].exec("azP ", 2340));
+assertNull(res[797].exec("aaaaaP ", 2341));
+assertNull(res[797].exec("a56P ", 2342));
+assertNull(res[799].exec("adfadadaklhlkalkajhlkjahdfasdfasdfladsfjkjPZ", 2343));
+assertNull(res[799].exec("lkjhlkjhlkjhlkjhabbbbbbcdaefabbbbbbbefaPBZ", 2344));
+assertNull(res[799].exec("cdabbbbbbbbPRBZ", 2345));
+assertNull(res[799].exec("efabbbbbbbbbbbbbbbbPRBZ", 2346));
+assertNull(res[799].exec("bbbbbbbbbbbbcdXyasdfadfPRBZ ", 2347));
+assertNull(res[799].exec("abc", 2348));
+assertNull(res[799].exec("** Failers", 2349));
+assertNull(res[799].exec("def ", 2350));
+assertToStringEquals("the quick brown fox", res[800].exec("the quick brown fox"), 2351);
+assertNull(res[800].exec("The quick brown FOX", 2352));
+assertToStringEquals("the quick brown fox", res[800].exec("What do you know about the quick brown fox?"), 2353);
+assertNull(res[800].exec("What do you know about THE QUICK BROWN FOX?", 2354));
+assertToStringEquals("the quick brown fox", res[801].exec("the quick brown fox"), 2355);
+assertToStringEquals("The quick brown FOX", res[801].exec("The quick brown FOX"), 2356);
+assertToStringEquals("the quick brown fox", res[801].exec("What do you know about the quick brown fox?"), 2357);
+assertToStringEquals("THE QUICK BROWN FOX", res[801].exec("What do you know about THE QUICK BROWN FOX?"), 2358);
+assertToStringEquals("abcd\x09\n\x0d\x0cae9;$\\?caxyz", res[802].exec("abcd\x09\n\x0d\x0cae9;$\\?caxyz"), 2359);
+assertToStringEquals("abxyzpqrrrabbxyyyypqAzz", res[803].exec("abxyzpqrrrabbxyyyypqAzz"), 2360);
+assertToStringEquals("abxyzpqrrrabbxyyyypqAzz", res[803].exec("abxyzpqrrrabbxyyyypqAzz"), 2361);
+assertToStringEquals("aabxyzpqrrrabbxyyyypqAzz", res[803].exec("aabxyzpqrrrabbxyyyypqAzz"), 2362);
+assertToStringEquals("aaabxyzpqrrrabbxyyyypqAzz", res[803].exec("aaabxyzpqrrrabbxyyyypqAzz"), 2363);
+assertToStringEquals("aaaabxyzpqrrrabbxyyyypqAzz", res[803].exec("aaaabxyzpqrrrabbxyyyypqAzz"), 2364);
+assertToStringEquals("abcxyzpqrrrabbxyyyypqAzz", res[803].exec("abcxyzpqrrrabbxyyyypqAzz"), 2365);
+assertToStringEquals("aabcxyzpqrrrabbxyyyypqAzz", res[803].exec("aabcxyzpqrrrabbxyyyypqAzz"), 2366);
+assertToStringEquals("aaabcxyzpqrrrabbxyyyypAzz", res[803].exec("aaabcxyzpqrrrabbxyyyypAzz"), 2367);
+assertToStringEquals("aaabcxyzpqrrrabbxyyyypqAzz", res[803].exec("aaabcxyzpqrrrabbxyyyypqAzz"), 2368);
+assertToStringEquals("aaabcxyzpqrrrabbxyyyypqqAzz", res[803].exec("aaabcxyzpqrrrabbxyyyypqqAzz"), 2369);
+assertToStringEquals("aaabcxyzpqrrrabbxyyyypqqqAzz", res[803].exec("aaabcxyzpqrrrabbxyyyypqqqAzz"), 2370);
+assertToStringEquals("aaabcxyzpqrrrabbxyyyypqqqqAzz", res[803].exec("aaabcxyzpqrrrabbxyyyypqqqqAzz"), 2371);
+assertToStringEquals("aaabcxyzpqrrrabbxyyyypqqqqqAzz", res[803].exec("aaabcxyzpqrrrabbxyyyypqqqqqAzz"), 2372);
+assertToStringEquals("aaabcxyzpqrrrabbxyyyypqqqqqqAzz", res[803].exec("aaabcxyzpqrrrabbxyyyypqqqqqqAzz"), 2373);
+assertToStringEquals("aaaabcxyzpqrrrabbxyyyypqAzz", res[803].exec("aaaabcxyzpqrrrabbxyyyypqAzz"), 2374);
+assertToStringEquals("abxyzzpqrrrabbxyyyypqAzz", res[803].exec("abxyzzpqrrrabbxyyyypqAzz"), 2375);
+assertToStringEquals("aabxyzzzpqrrrabbxyyyypqAzz", res[803].exec("aabxyzzzpqrrrabbxyyyypqAzz"), 2376);
+assertToStringEquals("aaabxyzzzzpqrrrabbxyyyypqAzz", res[803].exec("aaabxyzzzzpqrrrabbxyyyypqAzz"), 2377);
+assertToStringEquals("aaaabxyzzzzpqrrrabbxyyyypqAzz", res[803].exec("aaaabxyzzzzpqrrrabbxyyyypqAzz"), 2378);
+assertToStringEquals("abcxyzzpqrrrabbxyyyypqAzz", res[803].exec("abcxyzzpqrrrabbxyyyypqAzz"), 2379);
+assertToStringEquals("aabcxyzzzpqrrrabbxyyyypqAzz", res[803].exec("aabcxyzzzpqrrrabbxyyyypqAzz"), 2380);
+assertToStringEquals("aaabcxyzzzzpqrrrabbxyyyypqAzz", res[803].exec("aaabcxyzzzzpqrrrabbxyyyypqAzz"), 2381);
+assertToStringEquals("aaaabcxyzzzzpqrrrabbxyyyypqAzz", res[803].exec("aaaabcxyzzzzpqrrrabbxyyyypqAzz"), 2382);
+assertToStringEquals("aaaabcxyzzzzpqrrrabbbxyyyypqAzz", res[803].exec("aaaabcxyzzzzpqrrrabbbxyyyypqAzz"), 2383);
+assertToStringEquals("aaaabcxyzzzzpqrrrabbbxyyyyypqAzz", res[803].exec("aaaabcxyzzzzpqrrrabbbxyyyyypqAzz"), 2384);
+assertToStringEquals("aaabcxyzpqrrrabbxyyyypABzz", res[803].exec("aaabcxyzpqrrrabbxyyyypABzz"), 2385);
+assertToStringEquals("aaabcxyzpqrrrabbxyyyypABBzz", res[803].exec("aaabcxyzpqrrrabbxyyyypABBzz"), 2386);
+assertToStringEquals("aaabxyzpqrrrabbxyyyypqAzz", res[803].exec(">>>aaabxyzpqrrrabbxyyyypqAzz"), 2387);
+assertToStringEquals("aaaabxyzpqrrrabbxyyyypqAzz", res[803].exec(">aaaabxyzpqrrrabbxyyyypqAzz"), 2388);
+assertToStringEquals("abcxyzpqrrrabbxyyyypqAzz", res[803].exec(">>>>abcxyzpqrrrabbxyyyypqAzz"), 2389);
+assertNull(res[803].exec("*** Failers", 2390));
+assertNull(res[803].exec("abxyzpqrrabbxyyyypqAzz", 2391));
+assertNull(res[803].exec("abxyzpqrrrrabbxyyyypqAzz", 2392));
+assertNull(res[803].exec("abxyzpqrrrabxyyyypqAzz", 2393));
+assertNull(res[803].exec("aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz", 2394));
+assertNull(res[803].exec("aaaabcxyzzzzpqrrrabbbxyyypqAzz", 2395));
+assertNull(res[803].exec("aaabcxyzpqrrrabbxyyyypqqqqqqqAzz", 2396));
+assertToStringEquals("abczz,abc", res[804].exec("abczz"), 2397);
+assertToStringEquals("abcabczz,abc", res[804].exec("abcabczz"), 2398);
+assertNull(res[804].exec("*** Failers", 2399));
+assertNull(res[804].exec("zz", 2400));
+assertNull(res[804].exec("abcabcabczz", 2401));
+assertNull(res[804].exec(">>abczz", 2402));
+assertToStringEquals("bc,b", res[805].exec("bc"), 2403);
+assertToStringEquals("bbc,b", res[805].exec("bbc"), 2404);
+assertToStringEquals("bbbc,bb", res[805].exec("bbbc"), 2405);
+assertToStringEquals("bac,a", res[805].exec("bac"), 2406);
+assertToStringEquals("bbac,a", res[805].exec("bbac"), 2407);
+assertToStringEquals("aac,a", res[805].exec("aac"), 2408);
+assertToStringEquals("abbbbbbbbbbbc,bbbbbbbbbbb", res[805].exec("abbbbbbbbbbbc"), 2409);
+assertToStringEquals("bbbbbbbbbbbac,a", res[805].exec("bbbbbbbbbbbac"), 2410);
+assertNull(res[805].exec("*** Failers", 2411));
+assertNull(res[805].exec("aaac", 2412));
+assertNull(res[805].exec("abbbbbbbbbbbac", 2413));
+assertToStringEquals("bc,b", res[806].exec("bc"), 2414);
+assertToStringEquals("bbc,bb", res[806].exec("bbc"), 2415);
+assertToStringEquals("bbbc,bbb", res[806].exec("bbbc"), 2416);
+assertToStringEquals("bac,a", res[806].exec("bac"), 2417);
+assertToStringEquals("bbac,a", res[806].exec("bbac"), 2418);
+assertToStringEquals("aac,a", res[806].exec("aac"), 2419);
+assertToStringEquals("abbbbbbbbbbbc,bbbbbbbbbbb", res[806].exec("abbbbbbbbbbbc"), 2420);
+assertToStringEquals("bbbbbbbbbbbac,a", res[806].exec("bbbbbbbbbbbac"), 2421);
+assertNull(res[806].exec("*** Failers", 2422));
+assertNull(res[806].exec("aaac", 2423));
+assertNull(res[806].exec("abbbbbbbbbbbac", 2424));
+assertToStringEquals("bbc,bb", res[806].exec("bbc"), 2425);
+assertToStringEquals("babc,ba", res[807].exec("babc"), 2426);
+assertToStringEquals("bbabc,ba", res[807].exec("bbabc"), 2427);
+assertToStringEquals("bababc,ba", res[807].exec("bababc"), 2428);
+assertNull(res[807].exec("*** Failers", 2429));
+assertNull(res[807].exec("bababbc", 2430));
+assertNull(res[807].exec("babababc", 2431));
+assertToStringEquals("babc,ba", res[808].exec("babc"), 2432);
+assertToStringEquals("bbabc,ba", res[808].exec("bbabc"), 2433);
+assertToStringEquals("bababc,ba", res[808].exec("bababc"), 2434);
+assertNull(res[808].exec("*** Failers", 2435));
+assertNull(res[808].exec("bababbc", 2436));
+assertNull(res[808].exec("babababc", 2437));
assertThrows("var re = /^\\ca\\cA\\c[\\c{\\c:/;", 2438);
-assertEquals(null, res[808].exec("\x01\x01e;z", 2439));
-assertEquals("a", res[809].exec("athing"), 2440);
-assertEquals("b", res[809].exec("bthing"), 2441);
-assertEquals("]", res[809].exec("]thing"), 2442);
-assertEquals("c", res[809].exec("cthing"), 2443);
-assertEquals("d", res[809].exec("dthing"), 2444);
-assertEquals("e", res[809].exec("ething"), 2445);
-assertEquals(null, res[809].exec("*** Failers", 2446));
-assertEquals(null, res[809].exec("fthing", 2447));
-assertEquals(null, res[809].exec("[thing", 2448));
-assertEquals(null, res[809].exec("\\thing", 2449));
-assertEquals(null, res[810].exec("]thing", 2450));
-assertEquals(null, res[810].exec("cthing", 2451));
-assertEquals(null, res[810].exec("dthing", 2452));
-assertEquals(null, res[810].exec("ething", 2453));
-assertEquals(null, res[810].exec("*** Failers", 2454));
-assertEquals(null, res[810].exec("athing", 2455));
-assertEquals(null, res[810].exec("fthing", 2456));
-assertEquals("f", res[811].exec("fthing"), 2457);
-assertEquals("[", res[811].exec("[thing"), 2458);
-assertEquals("\\", res[811].exec("\\thing"), 2459);
-assertEquals("*", res[811].exec("*** Failers"), 2460);
-assertEquals(null, res[811].exec("athing", 2461));
-assertEquals(null, res[811].exec("bthing", 2462));
-assertEquals(null, res[811].exec("]thing", 2463));
-assertEquals(null, res[811].exec("cthing", 2464));
-assertEquals(null, res[811].exec("dthing", 2465));
-assertEquals(null, res[811].exec("ething", 2466));
-assertEquals(null, res[812].exec("athing", 2467));
-assertEquals(null, res[812].exec("fthing", 2468));
-assertEquals(null, res[812].exec("*** Failers", 2469));
-assertEquals(null, res[812].exec("]thing", 2470));
-assertEquals(null, res[812].exec("cthing", 2471));
-assertEquals(null, res[812].exec("dthing", 2472));
-assertEquals(null, res[812].exec("ething", 2473));
-assertEquals(null, res[812].exec("\ufffd", 2474));
-assertEquals(null, res[812].exec("\ufffd", 2475));
-assertEquals("0", res[813].exec("0"), 2476);
-assertEquals("1", res[813].exec("1"), 2477);
-assertEquals("2", res[813].exec("2"), 2478);
-assertEquals("3", res[813].exec("3"), 2479);
-assertEquals("4", res[813].exec("4"), 2480);
-assertEquals("5", res[813].exec("5"), 2481);
-assertEquals("6", res[813].exec("6"), 2482);
-assertEquals("7", res[813].exec("7"), 2483);
-assertEquals("8", res[813].exec("8"), 2484);
-assertEquals("9", res[813].exec("9"), 2485);
-assertEquals("10", res[813].exec("10"), 2486);
-assertEquals("100", res[813].exec("100"), 2487);
-assertEquals(null, res[813].exec("*** Failers", 2488));
-assertEquals(null, res[813].exec("abc", 2489));
-assertEquals("enter", res[814].exec("enter"), 2490);
-assertEquals("inter", res[814].exec("inter"), 2491);
-assertEquals("uponter", res[814].exec("uponter"), 2492);
-assertEquals("xxx0", res[815].exec("xxx0"), 2493);
-assertEquals("xxx1234", res[815].exec("xxx1234"), 2494);
-assertEquals(null, res[815].exec("*** Failers", 2495));
-assertEquals(null, res[815].exec("xxx", 2496));
-assertEquals("x123", res[816].exec("x123"), 2497);
-assertEquals("xx123", res[816].exec("xx123"), 2498);
-assertEquals("123456", res[816].exec("123456"), 2499);
-assertEquals(null, res[816].exec("*** Failers", 2500));
-assertEquals(null, res[816].exec("123", 2501));
-assertEquals("x1234", res[816].exec("x1234"), 2502);
-assertEquals("x123", res[817].exec("x123"), 2503);
-assertEquals("xx123", res[817].exec("xx123"), 2504);
-assertEquals("123456", res[817].exec("123456"), 2505);
-assertEquals(null, res[817].exec("*** Failers", 2506));
-assertEquals(null, res[817].exec("123", 2507));
-assertEquals("x1234", res[817].exec("x1234"), 2508);
-assertEquals("abc!pqr=apquxz.ixr.zzz.ac.uk,abc,pqr", res[818].exec("abc!pqr=apquxz.ixr.zzz.ac.uk"), 2509);
-assertEquals(null, res[818].exec("*** Failers", 2510));
-assertEquals(null, res[818].exec("!pqr=apquxz.ixr.zzz.ac.uk", 2511));
-assertEquals(null, res[818].exec("abc!=apquxz.ixr.zzz.ac.uk", 2512));
-assertEquals(null, res[818].exec("abc!pqr=apquxz:ixr.zzz.ac.uk", 2513));
-assertEquals(null, res[818].exec("abc!pqr=apquxz.ixr.zzz.ac.ukk", 2514));
-assertEquals(":", res[819].exec("Well, we need a colon: somewhere"), 2515);
-assertEquals(null, res[819].exec("*** Fail if we don't", 2516));
-assertEquals("0abc,0abc", res[820].exec("0abc"), 2517);
-assertEquals("abc,abc", res[820].exec("abc"), 2518);
-assertEquals("fed,fed", res[820].exec("fed"), 2519);
-assertEquals("E,E", res[820].exec("E"), 2520);
-assertEquals("::,::", res[820].exec("::"), 2521);
-assertEquals("5f03:12C0::932e,5f03:12C0::932e", res[820].exec("5f03:12C0::932e"), 2522);
-assertEquals("def,def", res[820].exec("fed def"), 2523);
-assertEquals("ff,ff", res[820].exec("Any old stuff"), 2524);
-assertEquals(null, res[820].exec("*** Failers", 2525));
-assertEquals(null, res[820].exec("0zzz", 2526));
-assertEquals(null, res[820].exec("gzzz", 2527));
-assertEquals(null, res[820].exec("fed ", 2528));
-assertEquals(null, res[820].exec("Any old rubbish", 2529));
-assertEquals(".1.2.3,1,2,3", res[821].exec(".1.2.3"), 2530);
-assertEquals("A.12.123.0,12,123,0", res[821].exec("A.12.123.0"), 2531);
-assertEquals(null, res[821].exec("*** Failers", 2532));
-assertEquals(null, res[821].exec(".1.2.3333", 2533));
-assertEquals(null, res[821].exec("1.2.3", 2534));
-assertEquals(null, res[821].exec("1234.2.3", 2535));
-assertEquals("1 IN SOA non-sp1 non-sp2(,1,non-sp1,non-sp2", res[822].exec("1 IN SOA non-sp1 non-sp2("), 2536);
-assertEquals("1 IN SOA non-sp1 non-sp2 (,1,non-sp1,non-sp2", res[822].exec("1 IN SOA non-sp1 non-sp2 ("), 2537);
-assertEquals(null, res[822].exec("*** Failers", 2538));
-assertEquals(null, res[822].exec("1IN SOA non-sp1 non-sp2(", 2539));
-assertEquals("a.,", res[823].exec("a."), 2540);
-assertEquals("Z.,", res[823].exec("Z."), 2541);
-assertEquals("2.,", res[823].exec("2."), 2542);
-assertEquals("ab-c.pq-r.,.pq-r", res[823].exec("ab-c.pq-r."), 2543);
-assertEquals("sxk.zzz.ac.uk.,.uk", res[823].exec("sxk.zzz.ac.uk."), 2544);
-assertEquals("x-.y-.,.y-", res[823].exec("x-.y-."), 2545);
-assertEquals(null, res[823].exec("*** Failers", 2546));
-assertEquals(null, res[823].exec("-abc.peq.", 2547));
-assertEquals("*.a,,,", res[824].exec("*.a"), 2548);
-assertEquals("*.b0-a,0-a,,", res[824].exec("*.b0-a"), 2549);
-assertEquals("*.c3-b.c,3-b,.c,", res[824].exec("*.c3-b.c"), 2550);
-assertEquals("*.c-a.b-c,-a,.b-c,-c", res[824].exec("*.c-a.b-c"), 2551);
-assertEquals(null, res[824].exec("*** Failers", 2552));
-assertEquals(null, res[824].exec("*.0", 2553));
-assertEquals(null, res[824].exec("*.a-", 2554));
-assertEquals(null, res[824].exec("*.a-b.c-", 2555));
-assertEquals(null, res[824].exec("*.c-a.0-c", 2556));
-assertEquals("abde,de,abd,e", res[825].exec("abde"), 2557);
-assertEquals("abdf,,abd,f", res[826].exec("abdf"), 2558);
-assertEquals("ab,abcd,cd,ab", res[827].exec("abcd"), 2559);
-assertEquals("a.b.c.d,.d", res[828].exec("a.b.c.d"), 2560);
-assertEquals("A.B.C.D,.D", res[828].exec("A.B.C.D"), 2561);
-assertEquals("a.b.c.1.2.3.C,.C", res[828].exec("a.b.c.1.2.3.C"), 2562);
-assertEquals("\"1234\",", res[829].exec("\"1234\""), 2563);
-assertEquals("\"abcd\" ;,;", res[829].exec("\"abcd\" ;"), 2564);
-assertEquals("\"\" ; rhubarb,; rhubarb", res[829].exec("\"\" ; rhubarb"), 2565);
-assertEquals(null, res[829].exec("*** Failers", 2566));
-assertEquals(null, res[829].exec("\"1234\" : things", 2567));
-assertEquals(null, res[830].exec("\\", 2568));
-assertEquals(null, res[830].exec("*** Failers", 2569));
-assertEquals("ab c", res[831].exec("ab c"), 2570);
-assertEquals(null, res[831].exec("*** Failers", 2571));
-assertEquals(null, res[831].exec("abc", 2572));
-assertEquals(null, res[831].exec("ab cde", 2573));
-assertEquals("ab c", res[831].exec("ab c"), 2574);
-assertEquals(null, res[831].exec("*** Failers", 2575));
-assertEquals(null, res[831].exec("abc", 2576));
-assertEquals(null, res[831].exec("ab cde", 2577));
-assertEquals("a bcd", res[832].exec("a bcd"), 2578);
-assertEquals(null, res[832].exec("a b d", 2579));
-assertEquals(null, res[832].exec("*** Failers", 2580));
-assertEquals(null, res[832].exec("abcd", 2581));
-assertEquals(null, res[832].exec("ab d", 2582));
-assertEquals("abcdefhijklm,abc,bc,c,def,ef,f,hij,ij,j,klm,lm,m", res[833].exec("abcdefhijklm"), 2583);
-assertEquals("abcdefhijklm,bc,c,ef,f,ij,j,lm,m", res[834].exec("abcdefhijklm"), 2584);
-assertEquals(null, res[835].exec("a+ Z0+\x08\n\x1d\x12", 2585));
-assertEquals(null, res[835].exec(".^$(*+)|{?,?}", 2586));
-assertEquals("z", res[836].exec("z"), 2587);
-assertEquals("az", res[836].exec("az"), 2588);
-assertEquals("aaaz", res[836].exec("aaaz"), 2589);
-assertEquals("a", res[836].exec("a"), 2590);
-assertEquals("aa", res[836].exec("aa"), 2591);
-assertEquals("aaaa", res[836].exec("aaaa"), 2592);
-assertEquals("a", res[836].exec("a+"), 2593);
-assertEquals("aa", res[836].exec("aa+"), 2594);
-assertEquals("z", res[837].exec("z"), 2595);
-assertEquals("a", res[837].exec("az"), 2596);
-assertEquals("a", res[837].exec("aaaz"), 2597);
-assertEquals("a", res[837].exec("a"), 2598);
-assertEquals("a", res[837].exec("aa"), 2599);
-assertEquals("a", res[837].exec("aaaa"), 2600);
-assertEquals("a", res[837].exec("a+"), 2601);
-assertEquals("a", res[837].exec("aa+"), 2602);
-assertEquals("az", res[838].exec("az"), 2603);
-assertEquals("aaaz", res[838].exec("aaaz"), 2604);
-assertEquals("aa", res[838].exec("aa"), 2605);
-assertEquals("aaaa", res[838].exec("aaaa"), 2606);
-assertEquals("aa", res[838].exec("aa+"), 2607);
-assertEquals("az", res[839].exec("az"), 2608);
-assertEquals("aa", res[839].exec("aaaz"), 2609);
-assertEquals("aa", res[839].exec("aa"), 2610);
-assertEquals("aa", res[839].exec("aaaa"), 2611);
-assertEquals("aa", res[839].exec("aa+"), 2612);
-assertEquals("1234567890", res[840].exec("1234567890"), 2613);
-assertEquals("12345678ab", res[840].exec("12345678ab"), 2614);
-assertEquals("12345678__", res[840].exec("12345678__"), 2615);
-assertEquals(null, res[840].exec("*** Failers", 2616));
-assertEquals(null, res[840].exec("1234567", 2617));
-assertEquals("uoie", res[841].exec("uoie"), 2618);
-assertEquals("1234", res[841].exec("1234"), 2619);
-assertEquals("12345", res[841].exec("12345"), 2620);
-assertEquals("aaaaa", res[841].exec("aaaaa"), 2621);
-assertEquals(null, res[841].exec("*** Failers", 2622));
-assertEquals(null, res[841].exec("123456", 2623));
-assertEquals("uoie", res[842].exec("uoie"), 2624);
-assertEquals("1234", res[842].exec("1234"), 2625);
-assertEquals("1234", res[842].exec("12345"), 2626);
-assertEquals("aaaa", res[842].exec("aaaaa"), 2627);
-assertEquals("1234", res[842].exec("123456"), 2628);
-assertEquals("From abcd Mon Sep 01 12:33,abcd", res[843].exec("From abcd Mon Sep 01 12:33:02 1997"), 2629);
-assertEquals("From abcd Mon Sep 01 12:33,Sep ", res[844].exec("From abcd Mon Sep 01 12:33:02 1997"), 2630);
-assertEquals("From abcd Mon Sep 1 12:33,Sep ", res[844].exec("From abcd Mon Sep 1 12:33:02 1997"), 2631);
-assertEquals(null, res[844].exec("*** Failers", 2632));
-assertEquals(null, res[844].exec("From abcd Sep 01 12:33:02 1997", 2633));
-assertEquals(null, res[845].exec("12\n34", 2634));
-assertEquals(null, res[845].exec("12\x0d34", 2635));
-assertEquals("brown", res[846].exec("the quick brown\x09 fox"), 2636);
-assertEquals("foolish see?,lish see?", res[847].exec("foobar is foolish see?"), 2637);
-assertEquals("rowbar etc, etc", res[848].exec("foobar crowbar etc"), 2638);
-assertEquals("barrel,rel", res[848].exec("barrel"), 2639);
-assertEquals("2barrel,rel", res[848].exec("2barrel"), 2640);
-assertEquals("A barrel,rel", res[848].exec("A barrel"), 2641);
-assertEquals("abc,abc", res[849].exec("abc456"), 2642);
-assertEquals(null, res[849].exec("*** Failers", 2643));
-assertEquals(null, res[849].exec("abc123", 2644));
-assertEquals("1234", res[850].exec("1234"), 2645);
-assertEquals("1234", res[851].exec("1234"), 2646);
-assertEquals("abcd", res[852].exec("abcd"), 2647);
-assertEquals("abcd", res[853].exec("abcd"), 2648);
-assertEquals("abc", res[854].exec("the abc"), 2649);
-assertEquals(null, res[854].exec("*** Failers", 2650));
-assertEquals(null, res[854].exec("abc", 2651));
-assertEquals("abc", res[855].exec("abc"), 2652);
-assertEquals(null, res[855].exec("*** Failers", 2653));
-assertEquals(null, res[855].exec("the abc", 2654));
-assertEquals("aabb,b", res[856].exec("aabbbbb"), 2655);
-assertEquals("aabbbbb,abbbbb", res[857].exec("aabbbbb"), 2656);
-assertEquals("aa,a", res[858].exec("aabbbbb"), 2657);
-assertEquals("aabb,b", res[859].exec("aabbbbb"), 2658);
-assertEquals("Alan Other <user@dom.ain>", res[860].exec("Alan Other <user@dom.ain>"), 2659);
-assertEquals("user@dom.ain", res[860].exec("<user@dom.ain>"), 2660);
-assertEquals("user@dom.ain", res[860].exec("user@dom.ain"), 2661);
-assertEquals("\"A. Other\" <user.1234@dom.ain> (a comment)", res[860].exec("\"A. Other\" <user.1234@dom.ain> (a comment)"), 2662);
-assertEquals(" Other <user.1234@dom.ain> (a comment)", res[860].exec("A. Other <user.1234@dom.ain> (a comment)"), 2663);
-assertEquals("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"@x400-re.lay", res[860].exec("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"@x400-re.lay"), 2664);
-assertEquals("user@some.where", res[860].exec("A missing angle <user@some.where"), 2665);
-assertEquals(null, res[860].exec("*** Failers", 2666));
-assertEquals(null, res[860].exec("The quick brown fox", 2667));
-assertEquals("Alan Other <user@dom.ain>", res[861].exec("Alan Other <user@dom.ain>"), 2668);
-assertEquals("user@dom.ain", res[861].exec("<user@dom.ain>"), 2669);
-assertEquals("user@dom.ain", res[861].exec("user@dom.ain"), 2670);
-assertEquals("\"A. Other\" <user.1234@dom.ain>", res[861].exec("\"A. Other\" <user.1234@dom.ain> (a comment)"), 2671);
-assertEquals(" Other <user.1234@dom.ain>", res[861].exec("A. Other <user.1234@dom.ain> (a comment)"), 2672);
-assertEquals("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"@x400-re.lay", res[861].exec("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"@x400-re.lay"), 2673);
-assertEquals("user@some.where", res[861].exec("A missing angle <user@some.where"), 2674);
-assertEquals(null, res[861].exec("*** Failers", 2675));
-assertEquals(null, res[861].exec("The quick brown fox", 2676));
-assertEquals(null, res[861].exec("abc\x00def\x00pqr\x00xyz\x000AB", 2677));
-assertEquals(null, res[861].exec("abc456 abc\x00def\x00pqr\x00xyz\x000ABCDE", 2678));
-assertEquals("abc\x0def\x00pqr\x000xyz\x0000AB", res[862].exec("abc\x0def\x00pqr\x000xyz\x0000AB"), 2679);
-assertEquals("abc\x0def\x00pqr\x000xyz\x0000AB", res[862].exec("abc456 abc\x0def\x00pqr\x000xyz\x0000ABCDE"), 2680);
-assertEquals("\x00", res[863].exec("\x00A"), 2681);
-assertEquals("\x01", res[863].exec("\x01B"), 2682);
-assertEquals("\x1f", res[863].exec("\x1fC"), 2683);
-assertEquals("\x00\x00\x00\x00", res[864].exec("\x00\x00\x00\x00"), 2684);
-assertEquals(null, res[865].exec("The Ax0x0Z", 2685));
-assertEquals(null, res[865].exec("An A\x00x0\x00Z", 2686));
-assertEquals(null, res[865].exec("*** Failers", 2687));
-assertEquals(null, res[865].exec("A\x00Z", 2688));
-assertEquals(null, res[865].exec("A\x00x0\x00x0Z", 2689));
-assertEquals(" ", res[866].exec(" abc"), 2690);
-assertEquals("\x0c", res[866].exec("\x0cabc"), 2691);
-assertEquals("\n", res[866].exec("\nabc"), 2692);
-assertEquals("\x0d", res[866].exec("\x0dabc"), 2693);
-assertEquals("\x09", res[866].exec("\x09abc"), 2694);
-assertEquals(null, res[866].exec("*** Failers", 2695));
-assertEquals(null, res[866].exec("abc", 2696));
-assertEquals("abc", res[867].exec("abc"), 2697);
-assertEquals("abbbbc", res[868].exec("abbbbc"), 2698);
-assertEquals("abbbc", res[868].exec("abbbc"), 2699);
-assertEquals("abbc", res[868].exec("abbc"), 2700);
-assertEquals(null, res[868].exec("*** Failers", 2701));
-assertEquals(null, res[868].exec("abc", 2702));
-assertEquals(null, res[868].exec("abbbbbc", 2703));
-assertEquals("track1.title:TBlah blah blah,track1,title,Blah blah blah", res[869].exec("track1.title:TBlah blah blah"), 2704);
-assertEquals("track1.title:TBlah blah blah,track1,title,Blah blah blah", res[870].exec("track1.title:TBlah blah blah"), 2705);
-assertEquals("track1.title:TBlah blah blah,track1,title,Blah blah blah", res[871].exec("track1.title:TBlah blah blah"), 2706);
-assertEquals("WXY_^abc", res[872].exec("WXY_^abc"), 2707);
-assertEquals(null, res[872].exec("*** Failers", 2708));
-assertEquals(null, res[872].exec("wxy", 2709));
-assertEquals("WXY_^abc", res[873].exec("WXY_^abc"), 2710);
-assertEquals("wxy_^ABC", res[873].exec("wxy_^ABC"), 2711);
-assertEquals("WXY_^abc", res[874].exec("WXY_^abc"), 2712);
-assertEquals("wxy_^ABC", res[874].exec("wxy_^ABC"), 2713);
-assertEquals("abc", res[875].exec("abc"), 2714);
-assertEquals("abc", res[875].exec("qqq\nabc"), 2715);
-assertEquals("abc", res[875].exec("abc\nzzz"), 2716);
-assertEquals("abc", res[875].exec("qqq\nabc\nzzz"), 2717);
-assertEquals("abc", res[876].exec("abc"), 2718);
-assertEquals(null, res[876].exec("*** Failers", 2719));
-assertEquals(null, res[876].exec("qqq\nabc", 2720));
-assertEquals(null, res[876].exec("abc\nzzz", 2721));
-assertEquals(null, res[876].exec("qqq\nabc\nzzz", 2722));
-assertEquals(null, res[877].exec("abc", 2723));
-assertEquals(null, res[877].exec("abc\n ", 2724));
-assertEquals(null, res[877].exec("*** Failers", 2725));
-assertEquals(null, res[877].exec("qqq\nabc", 2726));
-assertEquals(null, res[877].exec("abc\nzzz", 2727));
-assertEquals(null, res[877].exec("qqq\nabc\nzzz", 2728));
-assertEquals(null, res[878].exec("abc\ndef", 2729));
-assertEquals(null, res[879].exec("*** Failers", 2730));
-assertEquals(null, res[879].exec("abc\ndef", 2731));
-assertEquals("b", res[880].exec("b::c"), 2732);
-assertEquals("::", res[880].exec("c::b"), 2733);
-assertEquals("az-", res[881].exec("az-"), 2734);
-assertEquals("a", res[881].exec("*** Failers"), 2735);
-assertEquals(null, res[881].exec("b", 2736));
-assertEquals("za-", res[882].exec("za-"), 2737);
-assertEquals("a", res[882].exec("*** Failers"), 2738);
-assertEquals(null, res[882].exec("b", 2739));
-assertEquals("a-z", res[883].exec("a-z"), 2740);
-assertEquals("a", res[883].exec("*** Failers"), 2741);
-assertEquals(null, res[883].exec("b", 2742));
-assertEquals("abcdxyz", res[884].exec("abcdxyz"), 2743);
-assertEquals("12-34", res[885].exec("12-34"), 2744);
-assertEquals(null, res[885].exec("*** Failers", 2745));
-assertEquals(null, res[885].exec("aaa", 2746));
-assertEquals("12-34z", res[886].exec("12-34z"), 2747);
-assertEquals(null, res[886].exec("*** Failers", 2748));
-assertEquals(null, res[886].exec("aaa", 2749));
-assertEquals("\\", res[887].exec("\\\\"), 2750);
-assertEquals(" Z", res[888].exec("the Zoo"), 2751);
-assertEquals(null, res[888].exec("*** Failers", 2752));
-assertEquals(null, res[888].exec("Zulu", 2753));
-assertEquals("ab{3cd", res[889].exec("ab{3cd"), 2754);
-assertEquals("ab{3,cd", res[890].exec("ab{3,cd"), 2755);
-assertEquals("ab{3,4a}cd", res[891].exec("ab{3,4a}cd"), 2756);
-assertEquals("{4,5a}bc", res[892].exec("{4,5a}bc"), 2757);
-assertEquals(null, res[893].exec("a\x0db", 2758));
-assertEquals(null, res[893].exec("*** Failers", 2759));
-assertEquals(null, res[893].exec("a\nb", 2760));
-assertEquals("abc", res[894].exec("abc"), 2761);
-assertEquals(null, res[894].exec("abc\n", 2762));
-assertEquals(null, res[894].exec("*** Failers", 2763));
-assertEquals(null, res[894].exec("abc\ndef", 2764));
-assertEquals("abcS,abc", res[895].exec("abcS"), 2765);
-assertEquals("abc\x93,abc", res[896].exec("abc\x93"), 2766);
-assertEquals("abc\xd3,abc", res[897].exec("abc\xd3"), 2767);
-assertEquals("abc@,abc", res[898].exec("abc@"), 2768);
-assertEquals("abc@,abc", res[898].exec("abc@"), 2769);
-assertEquals("abc@,abc", res[898].exec("abc@0"), 2770);
-assertEquals("abc@,abc", res[898].exec("abc@0"), 2771);
-assertEquals("abc@,abc", res[898].exec("abc@0"), 2772);
-assertEquals("abc@,abc", res[898].exec("abc@0"), 2773);
-assertEquals("abc@,abc", res[898].exec("abc@0"), 2774);
-assertEquals("abc@,abc", res[898].exec("abc@0"), 2775);
-assertEquals(null, res[899].exec("abc\x0081", 2776));
-assertEquals(null, res[899].exec("abc\x0081", 2777));
-assertEquals(null, res[900].exec("abc\x0091", 2778));
-assertEquals(null, res[900].exec("abc\x0091", 2779));
-assertEquals("abcdefghijk\nS,a,b,c,d,e,f,g,h,i,j,k", res[901].exec("abcdefghijk\nS"), 2780);
-assertEquals("abidef", res[902].exec("abidef"), 2781);
-assertEquals("bc", res[903].exec("bc"), 2782);
-assertEquals("xyz,,", res[904].exec("xyz"), 2783);
-assertEquals("abc\x08de", res[905].exec("abc\x08de"), 2784);
-assertEquals("abc\x01de", res[906].exec("abc\x01de"), 2785);
-assertEquals("abc\x01de,abc", res[907].exec("abc\x01de"), 2786);
-assertEquals(null, res[907].exec("a\nb", 2787));
-assertEquals("baNOTcccc,b,a,NOT,cccc", res[908].exec("baNOTccccd"), 2788);
-assertEquals("baNOTccc,b,a,NOT,ccc", res[908].exec("baNOTcccd"), 2789);
-assertEquals("baNOTcc,b,a,NO,Tcc", res[908].exec("baNOTccd"), 2790);
-assertEquals("baccc,b,a,,ccc", res[908].exec("bacccd"), 2791);
-assertEquals("*** Failers,*,*,* Fail,ers", res[908].exec("*** Failers"), 2792);
-assertEquals(null, res[908].exec("anything", 2793));
-assertEquals(null, res[908].exec("b\x08c ", 2794));
-assertEquals(null, res[908].exec("baccd", 2795));
-assertEquals("A", res[909].exec("Abc"), 2796);
-assertEquals("b", res[910].exec("Abc "), 2797);
-assertEquals("AAA", res[911].exec("AAAaAbc"), 2798);
-assertEquals("bc ", res[912].exec("AAAaAbc "), 2799);
-assertEquals("bbb\nccc", res[913].exec("bbb\nccc"), 2800);
-assertEquals("c", res[914].exec("abc"), 2801);
-assertEquals("s", res[914].exec("*** Failers"), 2802);
-assertEquals(" ", res[914].exec("abk "), 2803);
-assertEquals("abc", res[915].exec("abc"), 2804);
-assertEquals("bc", res[915].exec("kbc"), 2805);
-assertEquals("bc ", res[915].exec("kabc "), 2806);
-assertEquals("ers", res[915].exec("*** Failers"), 2807);
-assertEquals(null, res[915].exec("abk", 2808));
-assertEquals(null, res[915].exec("akb", 2809));
-assertEquals(null, res[915].exec("akk ", 2810));
-assertEquals("12345678@a.b.c.d", res[916].exec("12345678@a.b.c.d"), 2811);
-assertEquals("123456789@x.y.z", res[916].exec("123456789@x.y.z"), 2812);
-assertEquals(null, res[916].exec("*** Failers", 2813));
-assertEquals(null, res[916].exec("12345678@x.y.uk", 2814));
-assertEquals(null, res[916].exec("1234567@a.b.c.d ", 2815));
-assertEquals("b", res[917].exec("aaaabcd"), 2816);
-assertEquals("A", res[917].exec("aaAabcd "), 2817);
-assertEquals("b", res[918].exec("aaaabcd"), 2818);
-assertEquals("b", res[918].exec("aaAabcd "), 2819);
-assertEquals("b", res[919].exec("aaaabcd"), 2820);
-assertEquals("A", res[919].exec("aaAabcd "), 2821);
-assertEquals("b", res[920].exec("aaaabcd"), 2822);
-assertEquals("b", res[920].exec("aaAabcd "), 2823);
-assertEquals("PSTAIREISLL", res[922].exec("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx"), 2824);
-assertEquals("PSTAIREISLL", res[923].exec("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx"), 2825);
-assertEquals(".230003938,.23", res[924].exec("1.230003938"), 2826);
-assertEquals(".875000282,.875", res[924].exec("1.875000282 "), 2827);
-assertEquals(".235,.23", res[924].exec("1.235 "), 2828);
-assertEquals(null, res[924].exec(" ", 2829));
-assertEquals(".23,.23,", res[925].exec("1.230003938 "), 2830);
-assertEquals(".875,.875,5", res[925].exec("1.875000282"), 2831);
-assertEquals(null, res[925].exec("*** Failers ", 2832));
-assertEquals(null, res[925].exec("1.235 ", 2833));
+assertNull(res[808].exec("\x01\x01e;z", 2439));
+assertToStringEquals("a", res[809].exec("athing"), 2440);
+assertToStringEquals("b", res[809].exec("bthing"), 2441);
+assertToStringEquals("]", res[809].exec("]thing"), 2442);
+assertToStringEquals("c", res[809].exec("cthing"), 2443);
+assertToStringEquals("d", res[809].exec("dthing"), 2444);
+assertToStringEquals("e", res[809].exec("ething"), 2445);
+assertNull(res[809].exec("*** Failers", 2446));
+assertNull(res[809].exec("fthing", 2447));
+assertNull(res[809].exec("[thing", 2448));
+assertNull(res[809].exec("\\thing", 2449));
+assertNull(res[810].exec("]thing", 2450));
+assertNull(res[810].exec("cthing", 2451));
+assertNull(res[810].exec("dthing", 2452));
+assertNull(res[810].exec("ething", 2453));
+assertNull(res[810].exec("*** Failers", 2454));
+assertNull(res[810].exec("athing", 2455));
+assertNull(res[810].exec("fthing", 2456));
+assertToStringEquals("f", res[811].exec("fthing"), 2457);
+assertToStringEquals("[", res[811].exec("[thing"), 2458);
+assertToStringEquals("\\", res[811].exec("\\thing"), 2459);
+assertToStringEquals("*", res[811].exec("*** Failers"), 2460);
+assertNull(res[811].exec("athing", 2461));
+assertNull(res[811].exec("bthing", 2462));
+assertNull(res[811].exec("]thing", 2463));
+assertNull(res[811].exec("cthing", 2464));
+assertNull(res[811].exec("dthing", 2465));
+assertNull(res[811].exec("ething", 2466));
+assertNull(res[812].exec("athing", 2467));
+assertNull(res[812].exec("fthing", 2468));
+assertNull(res[812].exec("*** Failers", 2469));
+assertNull(res[812].exec("]thing", 2470));
+assertNull(res[812].exec("cthing", 2471));
+assertNull(res[812].exec("dthing", 2472));
+assertNull(res[812].exec("ething", 2473));
+assertNull(res[812].exec("\ufffd", 2474));
+assertNull(res[812].exec("\ufffd", 2475));
+assertToStringEquals("0", res[813].exec("0"), 2476);
+assertToStringEquals("1", res[813].exec("1"), 2477);
+assertToStringEquals("2", res[813].exec("2"), 2478);
+assertToStringEquals("3", res[813].exec("3"), 2479);
+assertToStringEquals("4", res[813].exec("4"), 2480);
+assertToStringEquals("5", res[813].exec("5"), 2481);
+assertToStringEquals("6", res[813].exec("6"), 2482);
+assertToStringEquals("7", res[813].exec("7"), 2483);
+assertToStringEquals("8", res[813].exec("8"), 2484);
+assertToStringEquals("9", res[813].exec("9"), 2485);
+assertToStringEquals("10", res[813].exec("10"), 2486);
+assertToStringEquals("100", res[813].exec("100"), 2487);
+assertNull(res[813].exec("*** Failers", 2488));
+assertNull(res[813].exec("abc", 2489));
+assertToStringEquals("enter", res[814].exec("enter"), 2490);
+assertToStringEquals("inter", res[814].exec("inter"), 2491);
+assertToStringEquals("uponter", res[814].exec("uponter"), 2492);
+assertToStringEquals("xxx0", res[815].exec("xxx0"), 2493);
+assertToStringEquals("xxx1234", res[815].exec("xxx1234"), 2494);
+assertNull(res[815].exec("*** Failers", 2495));
+assertNull(res[815].exec("xxx", 2496));
+assertToStringEquals("x123", res[816].exec("x123"), 2497);
+assertToStringEquals("xx123", res[816].exec("xx123"), 2498);
+assertToStringEquals("123456", res[816].exec("123456"), 2499);
+assertNull(res[816].exec("*** Failers", 2500));
+assertNull(res[816].exec("123", 2501));
+assertToStringEquals("x1234", res[816].exec("x1234"), 2502);
+assertToStringEquals("x123", res[817].exec("x123"), 2503);
+assertToStringEquals("xx123", res[817].exec("xx123"), 2504);
+assertToStringEquals("123456", res[817].exec("123456"), 2505);
+assertNull(res[817].exec("*** Failers", 2506));
+assertNull(res[817].exec("123", 2507));
+assertToStringEquals("x1234", res[817].exec("x1234"), 2508);
+assertToStringEquals("abc!pqr=apquxz.ixr.zzz.ac.uk,abc,pqr", res[818].exec("abc!pqr=apquxz.ixr.zzz.ac.uk"), 2509);
+assertNull(res[818].exec("*** Failers", 2510));
+assertNull(res[818].exec("!pqr=apquxz.ixr.zzz.ac.uk", 2511));
+assertNull(res[818].exec("abc!=apquxz.ixr.zzz.ac.uk", 2512));
+assertNull(res[818].exec("abc!pqr=apquxz:ixr.zzz.ac.uk", 2513));
+assertNull(res[818].exec("abc!pqr=apquxz.ixr.zzz.ac.ukk", 2514));
+assertToStringEquals(":", res[819].exec("Well, we need a colon: somewhere"), 2515);
+assertNull(res[819].exec("*** Fail if we don't", 2516));
+assertToStringEquals("0abc,0abc", res[820].exec("0abc"), 2517);
+assertToStringEquals("abc,abc", res[820].exec("abc"), 2518);
+assertToStringEquals("fed,fed", res[820].exec("fed"), 2519);
+assertToStringEquals("E,E", res[820].exec("E"), 2520);
+assertToStringEquals("::,::", res[820].exec("::"), 2521);
+assertToStringEquals("5f03:12C0::932e,5f03:12C0::932e", res[820].exec("5f03:12C0::932e"), 2522);
+assertToStringEquals("def,def", res[820].exec("fed def"), 2523);
+assertToStringEquals("ff,ff", res[820].exec("Any old stuff"), 2524);
+assertNull(res[820].exec("*** Failers", 2525));
+assertNull(res[820].exec("0zzz", 2526));
+assertNull(res[820].exec("gzzz", 2527));
+assertNull(res[820].exec("fed ", 2528));
+assertNull(res[820].exec("Any old rubbish", 2529));
+assertToStringEquals(".1.2.3,1,2,3", res[821].exec(".1.2.3"), 2530);
+assertToStringEquals("A.12.123.0,12,123,0", res[821].exec("A.12.123.0"), 2531);
+assertNull(res[821].exec("*** Failers", 2532));
+assertNull(res[821].exec(".1.2.3333", 2533));
+assertNull(res[821].exec("1.2.3", 2534));
+assertNull(res[821].exec("1234.2.3", 2535));
+assertToStringEquals("1 IN SOA non-sp1 non-sp2(,1,non-sp1,non-sp2", res[822].exec("1 IN SOA non-sp1 non-sp2("), 2536);
+assertToStringEquals("1 IN SOA non-sp1 non-sp2 (,1,non-sp1,non-sp2", res[822].exec("1 IN SOA non-sp1 non-sp2 ("), 2537);
+assertNull(res[822].exec("*** Failers", 2538));
+assertNull(res[822].exec("1IN SOA non-sp1 non-sp2(", 2539));
+assertToStringEquals("a.,", res[823].exec("a."), 2540);
+assertToStringEquals("Z.,", res[823].exec("Z."), 2541);
+assertToStringEquals("2.,", res[823].exec("2."), 2542);
+assertToStringEquals("ab-c.pq-r.,.pq-r", res[823].exec("ab-c.pq-r."), 2543);
+assertToStringEquals("sxk.zzz.ac.uk.,.uk", res[823].exec("sxk.zzz.ac.uk."), 2544);
+assertToStringEquals("x-.y-.,.y-", res[823].exec("x-.y-."), 2545);
+assertNull(res[823].exec("*** Failers", 2546));
+assertNull(res[823].exec("-abc.peq.", 2547));
+assertToStringEquals("*.a,,,", res[824].exec("*.a"), 2548);
+assertToStringEquals("*.b0-a,0-a,,", res[824].exec("*.b0-a"), 2549);
+assertToStringEquals("*.c3-b.c,3-b,.c,", res[824].exec("*.c3-b.c"), 2550);
+assertToStringEquals("*.c-a.b-c,-a,.b-c,-c", res[824].exec("*.c-a.b-c"), 2551);
+assertNull(res[824].exec("*** Failers", 2552));
+assertNull(res[824].exec("*.0", 2553));
+assertNull(res[824].exec("*.a-", 2554));
+assertNull(res[824].exec("*.a-b.c-", 2555));
+assertNull(res[824].exec("*.c-a.0-c", 2556));
+assertToStringEquals("abde,de,abd,e", res[825].exec("abde"), 2557);
+assertToStringEquals("abdf,,abd,f", res[826].exec("abdf"), 2558);
+assertToStringEquals("ab,abcd,cd,ab", res[827].exec("abcd"), 2559);
+assertToStringEquals("a.b.c.d,.d", res[828].exec("a.b.c.d"), 2560);
+assertToStringEquals("A.B.C.D,.D", res[828].exec("A.B.C.D"), 2561);
+assertToStringEquals("a.b.c.1.2.3.C,.C", res[828].exec("a.b.c.1.2.3.C"), 2562);
+assertToStringEquals("\"1234\",", res[829].exec("\"1234\""), 2563);
+assertToStringEquals("\"abcd\" ;,;", res[829].exec("\"abcd\" ;"), 2564);
+assertToStringEquals("\"\" ; rhubarb,; rhubarb", res[829].exec("\"\" ; rhubarb"), 2565);
+assertNull(res[829].exec("*** Failers", 2566));
+assertNull(res[829].exec("\"1234\" : things", 2567));
+assertNull(res[830].exec("\\", 2568));
+assertNull(res[830].exec("*** Failers", 2569));
+assertToStringEquals("ab c", res[831].exec("ab c"), 2570);
+assertNull(res[831].exec("*** Failers", 2571));
+assertNull(res[831].exec("abc", 2572));
+assertNull(res[831].exec("ab cde", 2573));
+assertToStringEquals("ab c", res[831].exec("ab c"), 2574);
+assertNull(res[831].exec("*** Failers", 2575));
+assertNull(res[831].exec("abc", 2576));
+assertNull(res[831].exec("ab cde", 2577));
+assertToStringEquals("a bcd", res[832].exec("a bcd"), 2578);
+assertNull(res[832].exec("a b d", 2579));
+assertNull(res[832].exec("*** Failers", 2580));
+assertNull(res[832].exec("abcd", 2581));
+assertNull(res[832].exec("ab d", 2582));
+assertToStringEquals("abcdefhijklm,abc,bc,c,def,ef,f,hij,ij,j,klm,lm,m", res[833].exec("abcdefhijklm"), 2583);
+assertToStringEquals("abcdefhijklm,bc,c,ef,f,ij,j,lm,m", res[834].exec("abcdefhijklm"), 2584);
+assertNull(res[835].exec("a+ Z0+\x08\n\x1d\x12", 2585));
+assertNull(res[835].exec(".^$(*+)|{?,?}", 2586));
+assertToStringEquals("z", res[836].exec("z"), 2587);
+assertToStringEquals("az", res[836].exec("az"), 2588);
+assertToStringEquals("aaaz", res[836].exec("aaaz"), 2589);
+assertToStringEquals("a", res[836].exec("a"), 2590);
+assertToStringEquals("aa", res[836].exec("aa"), 2591);
+assertToStringEquals("aaaa", res[836].exec("aaaa"), 2592);
+assertToStringEquals("a", res[836].exec("a+"), 2593);
+assertToStringEquals("aa", res[836].exec("aa+"), 2594);
+assertToStringEquals("z", res[837].exec("z"), 2595);
+assertToStringEquals("a", res[837].exec("az"), 2596);
+assertToStringEquals("a", res[837].exec("aaaz"), 2597);
+assertToStringEquals("a", res[837].exec("a"), 2598);
+assertToStringEquals("a", res[837].exec("aa"), 2599);
+assertToStringEquals("a", res[837].exec("aaaa"), 2600);
+assertToStringEquals("a", res[837].exec("a+"), 2601);
+assertToStringEquals("a", res[837].exec("aa+"), 2602);
+assertToStringEquals("az", res[838].exec("az"), 2603);
+assertToStringEquals("aaaz", res[838].exec("aaaz"), 2604);
+assertToStringEquals("aa", res[838].exec("aa"), 2605);
+assertToStringEquals("aaaa", res[838].exec("aaaa"), 2606);
+assertToStringEquals("aa", res[838].exec("aa+"), 2607);
+assertToStringEquals("az", res[839].exec("az"), 2608);
+assertToStringEquals("aa", res[839].exec("aaaz"), 2609);
+assertToStringEquals("aa", res[839].exec("aa"), 2610);
+assertToStringEquals("aa", res[839].exec("aaaa"), 2611);
+assertToStringEquals("aa", res[839].exec("aa+"), 2612);
+assertToStringEquals("1234567890", res[840].exec("1234567890"), 2613);
+assertToStringEquals("12345678ab", res[840].exec("12345678ab"), 2614);
+assertToStringEquals("12345678__", res[840].exec("12345678__"), 2615);
+assertNull(res[840].exec("*** Failers", 2616));
+assertNull(res[840].exec("1234567", 2617));
+assertToStringEquals("uoie", res[841].exec("uoie"), 2618);
+assertToStringEquals("1234", res[841].exec("1234"), 2619);
+assertToStringEquals("12345", res[841].exec("12345"), 2620);
+assertToStringEquals("aaaaa", res[841].exec("aaaaa"), 2621);
+assertNull(res[841].exec("*** Failers", 2622));
+assertNull(res[841].exec("123456", 2623));
+assertToStringEquals("uoie", res[842].exec("uoie"), 2624);
+assertToStringEquals("1234", res[842].exec("1234"), 2625);
+assertToStringEquals("1234", res[842].exec("12345"), 2626);
+assertToStringEquals("aaaa", res[842].exec("aaaaa"), 2627);
+assertToStringEquals("1234", res[842].exec("123456"), 2628);
+assertToStringEquals("From abcd Mon Sep 01 12:33,abcd", res[843].exec("From abcd Mon Sep 01 12:33:02 1997"), 2629);
+assertToStringEquals("From abcd Mon Sep 01 12:33,Sep ", res[844].exec("From abcd Mon Sep 01 12:33:02 1997"), 2630);
+assertToStringEquals("From abcd Mon Sep 1 12:33,Sep ", res[844].exec("From abcd Mon Sep 1 12:33:02 1997"), 2631);
+assertNull(res[844].exec("*** Failers", 2632));
+assertNull(res[844].exec("From abcd Sep 01 12:33:02 1997", 2633));
+assertNull(res[845].exec("12\n34", 2634));
+assertNull(res[845].exec("12\x0d34", 2635));
+assertToStringEquals("brown", res[846].exec("the quick brown\x09 fox"), 2636);
+assertToStringEquals("foolish see?,lish see?", res[847].exec("foobar is foolish see?"), 2637);
+assertToStringEquals("rowbar etc, etc", res[848].exec("foobar crowbar etc"), 2638);
+assertToStringEquals("barrel,rel", res[848].exec("barrel"), 2639);
+assertToStringEquals("2barrel,rel", res[848].exec("2barrel"), 2640);
+assertToStringEquals("A barrel,rel", res[848].exec("A barrel"), 2641);
+assertToStringEquals("abc,abc", res[849].exec("abc456"), 2642);
+assertNull(res[849].exec("*** Failers", 2643));
+assertNull(res[849].exec("abc123", 2644));
+assertToStringEquals("1234", res[850].exec("1234"), 2645);
+assertToStringEquals("1234", res[851].exec("1234"), 2646);
+assertToStringEquals("abcd", res[852].exec("abcd"), 2647);
+assertToStringEquals("abcd", res[853].exec("abcd"), 2648);
+assertToStringEquals("abc", res[854].exec("the abc"), 2649);
+assertNull(res[854].exec("*** Failers", 2650));
+assertNull(res[854].exec("abc", 2651));
+assertToStringEquals("abc", res[855].exec("abc"), 2652);
+assertNull(res[855].exec("*** Failers", 2653));
+assertNull(res[855].exec("the abc", 2654));
+assertToStringEquals("aabb,b", res[856].exec("aabbbbb"), 2655);
+assertToStringEquals("aabbbbb,abbbbb", res[857].exec("aabbbbb"), 2656);
+assertToStringEquals("aa,a", res[858].exec("aabbbbb"), 2657);
+assertToStringEquals("aabb,b", res[859].exec("aabbbbb"), 2658);
+assertToStringEquals("Alan Other <user@dom.ain>", res[860].exec("Alan Other <user@dom.ain>"), 2659);
+assertToStringEquals("user@dom.ain", res[860].exec("<user@dom.ain>"), 2660);
+assertToStringEquals("user@dom.ain", res[860].exec("user@dom.ain"), 2661);
+assertToStringEquals("\"A. Other\" <user.1234@dom.ain> (a comment)", res[860].exec("\"A. Other\" <user.1234@dom.ain> (a comment)"), 2662);
+assertToStringEquals(" Other <user.1234@dom.ain> (a comment)", res[860].exec("A. Other <user.1234@dom.ain> (a comment)"), 2663);
+assertToStringEquals("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"@x400-re.lay", res[860].exec("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"@x400-re.lay"), 2664);
+assertToStringEquals("user@some.where", res[860].exec("A missing angle <user@some.where"), 2665);
+assertNull(res[860].exec("*** Failers", 2666));
+assertNull(res[860].exec("The quick brown fox", 2667));
+assertToStringEquals("Alan Other <user@dom.ain>", res[861].exec("Alan Other <user@dom.ain>"), 2668);
+assertToStringEquals("user@dom.ain", res[861].exec("<user@dom.ain>"), 2669);
+assertToStringEquals("user@dom.ain", res[861].exec("user@dom.ain"), 2670);
+assertToStringEquals("\"A. Other\" <user.1234@dom.ain>", res[861].exec("\"A. Other\" <user.1234@dom.ain> (a comment)"), 2671);
+assertToStringEquals(" Other <user.1234@dom.ain>", res[861].exec("A. Other <user.1234@dom.ain> (a comment)"), 2672);
+assertToStringEquals("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"@x400-re.lay", res[861].exec("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"@x400-re.lay"), 2673);
+assertToStringEquals("user@some.where", res[861].exec("A missing angle <user@some.where"), 2674);
+assertNull(res[861].exec("*** Failers", 2675));
+assertNull(res[861].exec("The quick brown fox", 2676));
+assertNull(res[861].exec("abc\x00def\x00pqr\x00xyz\x000AB", 2677));
+assertNull(res[861].exec("abc456 abc\x00def\x00pqr\x00xyz\x000ABCDE", 2678));
+assertToStringEquals("abc\x0def\x00pqr\x000xyz\x0000AB", res[862].exec("abc\x0def\x00pqr\x000xyz\x0000AB"), 2679);
+assertToStringEquals("abc\x0def\x00pqr\x000xyz\x0000AB", res[862].exec("abc456 abc\x0def\x00pqr\x000xyz\x0000ABCDE"), 2680);
+assertToStringEquals("\x00", res[863].exec("\x00A"), 2681);
+assertToStringEquals("\x01", res[863].exec("\x01B"), 2682);
+assertToStringEquals("\x1f", res[863].exec("\x1fC"), 2683);
+assertToStringEquals("\x00\x00\x00\x00", res[864].exec("\x00\x00\x00\x00"), 2684);
+assertNull(res[865].exec("The Ax0x0Z", 2685));
+assertNull(res[865].exec("An A\x00x0\x00Z", 2686));
+assertNull(res[865].exec("*** Failers", 2687));
+assertNull(res[865].exec("A\x00Z", 2688));
+assertNull(res[865].exec("A\x00x0\x00x0Z", 2689));
+assertToStringEquals(" ", res[866].exec(" abc"), 2690);
+assertToStringEquals("\x0c", res[866].exec("\x0cabc"), 2691);
+assertToStringEquals("\n", res[866].exec("\nabc"), 2692);
+assertToStringEquals("\x0d", res[866].exec("\x0dabc"), 2693);
+assertToStringEquals("\x09", res[866].exec("\x09abc"), 2694);
+assertNull(res[866].exec("*** Failers", 2695));
+assertNull(res[866].exec("abc", 2696));
+assertToStringEquals("abc", res[867].exec("abc"), 2697);
+assertToStringEquals("abbbbc", res[868].exec("abbbbc"), 2698);
+assertToStringEquals("abbbc", res[868].exec("abbbc"), 2699);
+assertToStringEquals("abbc", res[868].exec("abbc"), 2700);
+assertNull(res[868].exec("*** Failers", 2701));
+assertNull(res[868].exec("abc", 2702));
+assertNull(res[868].exec("abbbbbc", 2703));
+assertToStringEquals("track1.title:TBlah blah blah,track1,title,Blah blah blah", res[869].exec("track1.title:TBlah blah blah"), 2704);
+assertToStringEquals("track1.title:TBlah blah blah,track1,title,Blah blah blah", res[870].exec("track1.title:TBlah blah blah"), 2705);
+assertToStringEquals("track1.title:TBlah blah blah,track1,title,Blah blah blah", res[871].exec("track1.title:TBlah blah blah"), 2706);
+assertToStringEquals("WXY_^abc", res[872].exec("WXY_^abc"), 2707);
+assertNull(res[872].exec("*** Failers", 2708));
+assertNull(res[872].exec("wxy", 2709));
+assertToStringEquals("WXY_^abc", res[873].exec("WXY_^abc"), 2710);
+assertToStringEquals("wxy_^ABC", res[873].exec("wxy_^ABC"), 2711);
+assertToStringEquals("WXY_^abc", res[874].exec("WXY_^abc"), 2712);
+assertToStringEquals("wxy_^ABC", res[874].exec("wxy_^ABC"), 2713);
+assertToStringEquals("abc", res[875].exec("abc"), 2714);
+assertToStringEquals("abc", res[875].exec("qqq\nabc"), 2715);
+assertToStringEquals("abc", res[875].exec("abc\nzzz"), 2716);
+assertToStringEquals("abc", res[875].exec("qqq\nabc\nzzz"), 2717);
+assertToStringEquals("abc", res[876].exec("abc"), 2718);
+assertNull(res[876].exec("*** Failers", 2719));
+assertNull(res[876].exec("qqq\nabc", 2720));
+assertNull(res[876].exec("abc\nzzz", 2721));
+assertNull(res[876].exec("qqq\nabc\nzzz", 2722));
+assertNull(res[877].exec("abc", 2723));
+assertNull(res[877].exec("abc\n ", 2724));
+assertNull(res[877].exec("*** Failers", 2725));
+assertNull(res[877].exec("qqq\nabc", 2726));
+assertNull(res[877].exec("abc\nzzz", 2727));
+assertNull(res[877].exec("qqq\nabc\nzzz", 2728));
+assertNull(res[878].exec("abc\ndef", 2729));
+assertNull(res[879].exec("*** Failers", 2730));
+assertNull(res[879].exec("abc\ndef", 2731));
+assertToStringEquals("b", res[880].exec("b::c"), 2732);
+assertToStringEquals("::", res[880].exec("c::b"), 2733);
+assertToStringEquals("az-", res[881].exec("az-"), 2734);
+assertToStringEquals("a", res[881].exec("*** Failers"), 2735);
+assertNull(res[881].exec("b", 2736));
+assertToStringEquals("za-", res[882].exec("za-"), 2737);
+assertToStringEquals("a", res[882].exec("*** Failers"), 2738);
+assertNull(res[882].exec("b", 2739));
+assertToStringEquals("a-z", res[883].exec("a-z"), 2740);
+assertToStringEquals("a", res[883].exec("*** Failers"), 2741);
+assertNull(res[883].exec("b", 2742));
+assertToStringEquals("abcdxyz", res[884].exec("abcdxyz"), 2743);
+assertToStringEquals("12-34", res[885].exec("12-34"), 2744);
+assertNull(res[885].exec("*** Failers", 2745));
+assertNull(res[885].exec("aaa", 2746));
+assertToStringEquals("12-34z", res[886].exec("12-34z"), 2747);
+assertNull(res[886].exec("*** Failers", 2748));
+assertNull(res[886].exec("aaa", 2749));
+assertToStringEquals("\\", res[887].exec("\\\\"), 2750);
+assertToStringEquals(" Z", res[888].exec("the Zoo"), 2751);
+assertNull(res[888].exec("*** Failers", 2752));
+assertNull(res[888].exec("Zulu", 2753));
+assertToStringEquals("ab{3cd", res[889].exec("ab{3cd"), 2754);
+assertToStringEquals("ab{3,cd", res[890].exec("ab{3,cd"), 2755);
+assertToStringEquals("ab{3,4a}cd", res[891].exec("ab{3,4a}cd"), 2756);
+assertToStringEquals("{4,5a}bc", res[892].exec("{4,5a}bc"), 2757);
+assertNull(res[893].exec("a\x0db", 2758));
+assertNull(res[893].exec("*** Failers", 2759));
+assertNull(res[893].exec("a\nb", 2760));
+assertToStringEquals("abc", res[894].exec("abc"), 2761);
+assertNull(res[894].exec("abc\n", 2762));
+assertNull(res[894].exec("*** Failers", 2763));
+assertNull(res[894].exec("abc\ndef", 2764));
+assertToStringEquals("abcS,abc", res[895].exec("abcS"), 2765);
+assertToStringEquals("abc\x93,abc", res[896].exec("abc\x93"), 2766);
+assertToStringEquals("abc\xd3,abc", res[897].exec("abc\xd3"), 2767);
+assertToStringEquals("abc@,abc", res[898].exec("abc@"), 2768);
+assertToStringEquals("abc@,abc", res[898].exec("abc@"), 2769);
+assertToStringEquals("abc@,abc", res[898].exec("abc@0"), 2770);
+assertToStringEquals("abc@,abc", res[898].exec("abc@0"), 2771);
+assertToStringEquals("abc@,abc", res[898].exec("abc@0"), 2772);
+assertToStringEquals("abc@,abc", res[898].exec("abc@0"), 2773);
+assertToStringEquals("abc@,abc", res[898].exec("abc@0"), 2774);
+assertToStringEquals("abc@,abc", res[898].exec("abc@0"), 2775);
+assertNull(res[899].exec("abc\x0081", 2776));
+assertNull(res[899].exec("abc\x0081", 2777));
+assertNull(res[900].exec("abc\x0091", 2778));
+assertNull(res[900].exec("abc\x0091", 2779));
+assertToStringEquals("abcdefghijk\nS,a,b,c,d,e,f,g,h,i,j,k", res[901].exec("abcdefghijk\nS"), 2780);
+assertToStringEquals("abidef", res[902].exec("abidef"), 2781);
+assertToStringEquals("bc", res[903].exec("bc"), 2782);
+assertToStringEquals("xyz,,", res[904].exec("xyz"), 2783);
+assertToStringEquals("abc\x08de", res[905].exec("abc\x08de"), 2784);
+assertToStringEquals("abc\x01de", res[906].exec("abc\x01de"), 2785);
+assertToStringEquals("abc\x01de,abc", res[907].exec("abc\x01de"), 2786);
+assertNull(res[907].exec("a\nb", 2787));
+assertToStringEquals("baNOTcccc,b,a,NOT,cccc", res[908].exec("baNOTccccd"), 2788);
+assertToStringEquals("baNOTccc,b,a,NOT,ccc", res[908].exec("baNOTcccd"), 2789);
+assertToStringEquals("baNOTcc,b,a,NO,Tcc", res[908].exec("baNOTccd"), 2790);
+assertToStringEquals("baccc,b,a,,ccc", res[908].exec("bacccd"), 2791);
+assertToStringEquals("*** Failers,*,*,* Fail,ers", res[908].exec("*** Failers"), 2792);
+assertNull(res[908].exec("anything", 2793));
+assertNull(res[908].exec("b\x08c ", 2794));
+assertNull(res[908].exec("baccd", 2795));
+assertToStringEquals("A", res[909].exec("Abc"), 2796);
+assertToStringEquals("b", res[910].exec("Abc "), 2797);
+assertToStringEquals("AAA", res[911].exec("AAAaAbc"), 2798);
+assertToStringEquals("bc ", res[912].exec("AAAaAbc "), 2799);
+assertToStringEquals("bbb\nccc", res[913].exec("bbb\nccc"), 2800);
+assertToStringEquals("c", res[914].exec("abc"), 2801);
+assertToStringEquals("s", res[914].exec("*** Failers"), 2802);
+assertToStringEquals(" ", res[914].exec("abk "), 2803);
+assertToStringEquals("abc", res[915].exec("abc"), 2804);
+assertToStringEquals("bc", res[915].exec("kbc"), 2805);
+assertToStringEquals("bc ", res[915].exec("kabc "), 2806);
+assertToStringEquals("ers", res[915].exec("*** Failers"), 2807);
+assertNull(res[915].exec("abk", 2808));
+assertNull(res[915].exec("akb", 2809));
+assertNull(res[915].exec("akk ", 2810));
+assertToStringEquals("12345678@a.b.c.d", res[916].exec("12345678@a.b.c.d"), 2811);
+assertToStringEquals("123456789@x.y.z", res[916].exec("123456789@x.y.z"), 2812);
+assertNull(res[916].exec("*** Failers", 2813));
+assertNull(res[916].exec("12345678@x.y.uk", 2814));
+assertNull(res[916].exec("1234567@a.b.c.d ", 2815));
+assertToStringEquals("b", res[917].exec("aaaabcd"), 2816);
+assertToStringEquals("A", res[917].exec("aaAabcd "), 2817);
+assertToStringEquals("b", res[918].exec("aaaabcd"), 2818);
+assertToStringEquals("b", res[918].exec("aaAabcd "), 2819);
+assertToStringEquals("b", res[919].exec("aaaabcd"), 2820);
+assertToStringEquals("A", res[919].exec("aaAabcd "), 2821);
+assertToStringEquals("b", res[920].exec("aaaabcd"), 2822);
+assertToStringEquals("b", res[920].exec("aaAabcd "), 2823);
+assertToStringEquals("PSTAIREISLL", res[922].exec("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx"), 2824);
+assertToStringEquals("PSTAIREISLL", res[923].exec("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx"), 2825);
+assertToStringEquals(".230003938,.23", res[924].exec("1.230003938"), 2826);
+assertToStringEquals(".875000282,.875", res[924].exec("1.875000282 "), 2827);
+assertToStringEquals(".235,.23", res[924].exec("1.235 "), 2828);
+assertNull(res[924].exec(" ", 2829));
+assertToStringEquals(".23,.23,", res[925].exec("1.230003938 "), 2830);
+assertToStringEquals(".875,.875,5", res[925].exec("1.875000282"), 2831);
+assertNull(res[925].exec("*** Failers ", 2832));
+assertNull(res[925].exec("1.235 ", 2833));
assertThrows("var re = /a(?)b/;", 2834);
-assertEquals(null, res[925].exec("ab ", 2835));
-assertEquals("foo table,foo,table", res[926].exec("Food is on the foo table"), 2836);
-assertEquals("food is under the bar in the bar,d is under the bar in the ", res[927].exec("The food is under the bar in the barn."), 2837);
-assertEquals("food is under the bar,d is under the ", res[928].exec("The food is under the bar in the barn."), 2838);
-assertEquals("I have 2 numbers: 53147,I have 2 numbers: 53147,", res[929].exec("I have 2 numbers: 53147"), 2839);
-assertEquals("I have 2 numbers: 53147,I have 2 numbers: 5314,7", res[930].exec("I have 2 numbers: 53147"), 2840);
-assertEquals(",,", res[931].exec("I have 2 numbers: 53147"), 2841);
-assertEquals("I have 2,I have ,2", res[932].exec("I have 2 numbers: 53147"), 2842);
-assertEquals("I have 2 numbers: 53147,I have 2 numbers: 5314,7", res[933].exec("I have 2 numbers: 53147"), 2843);
-assertEquals("I have 2 numbers: 53147,I have 2 numbers: ,53147", res[934].exec("I have 2 numbers: 53147"), 2844);
-assertEquals("I have 2 numbers: 53147,I have 2 numbers: ,53147", res[935].exec("I have 2 numbers: 53147"), 2845);
-assertEquals("I have 2 numbers: 53147,I have 2 numbers: ,53147", res[936].exec("I have 2 numbers: 53147"), 2846);
-assertEquals("AB", res[937].exec("ABC123"), 2847);
-assertEquals(" ", res[937].exec(" "), 2848);
-assertEquals("ABC,ABC", res[938].exec("ABC445"), 2849);
-assertEquals(null, res[938].exec("*** Failers", 2850));
-assertEquals(null, res[938].exec("ABC123", 2851));
-assertEquals("W46]", res[939].exec("W46]789 "), 2852);
-assertEquals("-46]", res[939].exec("-46]789"), 2853);
-assertEquals(null, res[939].exec("*** Failers", 2854));
-assertEquals(null, res[939].exec("Wall", 2855));
-assertEquals(null, res[939].exec("Zebra", 2856));
-assertEquals(null, res[939].exec("42", 2857));
-assertEquals(null, res[939].exec("[abcd] ", 2858));
-assertEquals(null, res[939].exec("]abcd[", 2859));
-assertEquals(null, res[939].exec(" ", 2860));
-assertEquals("W", res[940].exec("W46]789 "), 2861);
-assertEquals("W", res[940].exec("Wall"), 2862);
-assertEquals("Z", res[940].exec("Zebra"), 2863);
-assertEquals("X", res[940].exec("Xylophone "), 2864);
-assertEquals("4", res[940].exec("42"), 2865);
-assertEquals("[", res[940].exec("[abcd] "), 2866);
-assertEquals("]", res[940].exec("]abcd["), 2867);
-assertEquals("\\", res[940].exec("\\backslash "), 2868);
-assertEquals(null, res[940].exec("*** Failers", 2869));
-assertEquals(null, res[940].exec("-46]789", 2870));
-assertEquals(null, res[940].exec("well", 2871));
-assertEquals("01/01/2000", res[941].exec("01/01/2000"), 2872);
-assertEquals(",", res[944].exec("bcd"), 2873);
-assertEquals(",", res[944].exec("abc"), 2874);
-assertEquals(",", res[944].exec("aab "), 2875);
-assertEquals(",", res[945].exec("bcd"), 2876);
-assertEquals("a,a", res[945].exec("abc"), 2877);
-assertEquals("a,a", res[945].exec("aab "), 2878);
-assertEquals(",", res[946].exec("bcd"), 2879);
-assertEquals("a,a", res[946].exec("abc"), 2880);
-assertEquals("aa,a", res[946].exec("aab "), 2881);
-assertEquals(",", res[947].exec("bcd"), 2882);
-assertEquals("a,a", res[947].exec("abc"), 2883);
-assertEquals("aa,a", res[947].exec("aab"), 2884);
-assertEquals("aaa,a", res[947].exec("aaa "), 2885);
-assertEquals(",", res[948].exec("bcd"), 2886);
-assertEquals("a,a", res[948].exec("abc"), 2887);
-assertEquals("aa,a", res[948].exec("aab"), 2888);
-assertEquals("aaa,a", res[948].exec("aaa"), 2889);
-assertEquals("aaaaaaaa,a", res[948].exec("aaaaaaaa "), 2890);
-assertEquals(null, res[949].exec("bcd", 2891));
-assertEquals("a,a", res[949].exec("abc"), 2892);
-assertEquals("a,a", res[949].exec("aab "), 2893);
-assertEquals(null, res[950].exec("bcd", 2894));
-assertEquals("a,a", res[950].exec("abc"), 2895);
-assertEquals("aa,a", res[950].exec("aab "), 2896);
-assertEquals(null, res[951].exec("bcd", 2897));
-assertEquals("a,a", res[951].exec("abc"), 2898);
-assertEquals("aa,a", res[951].exec("aab"), 2899);
-assertEquals("aaa,a", res[951].exec("aaa "), 2900);
-assertEquals(null, res[952].exec("bcd", 2901));
-assertEquals("a,a", res[952].exec("abc"), 2902);
-assertEquals("aa,a", res[952].exec("aab"), 2903);
-assertEquals("aaa,a", res[952].exec("aaa"), 2904);
-assertEquals("aaaaaaaa,a", res[952].exec("aaaaaaaa "), 2905);
-assertEquals("bib.gif", res[953].exec("borfle\nbib.gif\nno"), 2906);
-assertEquals("bib.gif", res[954].exec("borfle\nbib.gif\nno"), 2907);
-assertEquals("bib.gif", res[955].exec("borfle\nbib.gif\nno"), 2908);
-assertEquals("bib.gif", res[956].exec("borfle\nbib.gif\nno"), 2909);
-assertEquals("bib.gif", res[957].exec("borfle\nbib.gif\nno"), 2910);
-assertEquals("no", res[958].exec("borfle\nbib.gif\nno"), 2911);
-assertEquals("borfle", res[959].exec("borfle\nbib.gif\nno"), 2912);
-assertEquals("no", res[960].exec("borfle\nbib.gif\nno"), 2913);
-assertEquals("borfle", res[961].exec("borfle\nbib.gif\nno"), 2914);
-assertEquals("", res[962].exec("borfle\nbib.gif\nno\n"), 2915);
-assertEquals("borfle", res[963].exec("borfle\nbib.gif\nno\n"), 2916);
-assertEquals("", res[964].exec("borfle\nbib.gif\nno\n"), 2917);
-assertEquals("borfle", res[965].exec("borfle\nbib.gif\nno\n"), 2918);
-assertEquals("1234X,1234X", res[966].exec("abcde\n1234Xyz"), 2919);
-assertEquals("B,B", res[966].exec("BarFoo "), 2920);
-assertEquals(null, res[966].exec("*** Failers", 2921));
-assertEquals(null, res[966].exec("abcde\nBar ", 2922));
-assertEquals("1234X,1234X", res[967].exec("abcde\n1234Xyz"), 2923);
-assertEquals("B,B", res[967].exec("BarFoo "), 2924);
-assertEquals("B,B", res[967].exec("abcde\nBar "), 2925);
-assertEquals("1234X,1234X", res[968].exec("abcde\n1234Xyz"), 2926);
-assertEquals("B,B", res[968].exec("BarFoo "), 2927);
-assertEquals(null, res[968].exec("*** Failers", 2928));
-assertEquals(null, res[968].exec("abcde\nBar ", 2929));
-assertEquals("1234X,1234X", res[969].exec("abcde\n1234Xyz"), 2930);
-assertEquals("B,B", res[969].exec("BarFoo "), 2931);
-assertEquals("B,B", res[969].exec("abcde\nBar "), 2932);
-assertEquals("1234X,1234X", res[969].exec("abcde\n1234Xyz"), 2933);
-assertEquals("B,B", res[969].exec("BarFoo "), 2934);
-assertEquals(null, res[969].exec("*** Failers ", 2935));
-assertEquals("B,B", res[969].exec("abcde\nBar "), 2936);
-assertEquals("1234X,1234X", res[969].exec("abcde\n1234Xyz"), 2937);
-assertEquals("B,B", res[969].exec("BarFoo "), 2938);
-assertEquals(null, res[969].exec("*** Failers ", 2939));
-assertEquals("B,B", res[969].exec("abcde\nBar "), 2940);
-assertEquals(null, res[970].exec("**** Failers", 2941));
-assertEquals(null, res[970].exec("abc\nB", 2942));
-assertEquals(null, res[970].exec(" ", 2943));
-assertEquals(null, res[970].exec("abc\nB", 2944));
-assertEquals(null, res[970].exec("abc\nB", 2945));
-assertEquals(null, res[970].exec(" ", 2946));
-assertEquals(null, res[970].exec("abc\nB", 2947));
-assertEquals(null, res[970].exec("abc\nB", 2948));
-assertEquals("B", res[970].exec("B\n"), 2949);
-assertEquals("123456654321", res[971].exec("123456654321"), 2950);
-assertEquals("123456654321", res[972].exec("123456654321 "), 2951);
-assertEquals("123456654321", res[973].exec("123456654321"), 2952);
-assertEquals("abcabcabcabc", res[974].exec("abcabcabcabc"), 2953);
-assertEquals("abcabcabcabc", res[975].exec("abcabcabcabc"), 2954);
-assertEquals("abcabcabcabc,c", res[976].exec("abcabcabcabc "), 2955);
-assertEquals("n", res[977].exec("n"), 2956);
-assertEquals(null, res[977].exec("*** Failers ", 2957));
-assertEquals(null, res[977].exec("z ", 2958));
-assertEquals("abcd", res[978].exec("abcd"), 2959);
-assertEquals(null, res[978].exec("*** Failers", 2960));
-assertEquals(null, res[978].exec("abce ", 2961));
-assertEquals("abe", res[979].exec("abe"), 2962);
-assertEquals(null, res[979].exec("*** Failers", 2963));
-assertEquals(null, res[979].exec("abcde ", 2964));
-assertEquals("abd,", res[980].exec("abd"), 2965);
-assertEquals(null, res[980].exec("*** Failers", 2966));
-assertEquals(null, res[980].exec("abcd ", 2967));
-assertEquals("a,", res[981].exec("a"), 2968);
-assertEquals("ab,b", res[981].exec("ab"), 2969);
-assertEquals("abbbb,bbbb", res[981].exec("abbbb"), 2970);
-assertEquals("a,", res[981].exec("*** Failers"), 2971);
-assertEquals(null, res[981].exec("bbbbb ", 2972));
-assertEquals("abe", res[982].exec("abe"), 2973);
-assertEquals(null, res[982].exec("*** Failers", 2974));
-assertEquals(null, res[982].exec("ab1e ", 2975));
-assertEquals("\"quick\",quick", res[983].exec("the \"quick\" brown fox"), 2976);
-assertEquals("\"the \\\"quick\\\" brown fox\", brown fox", res[983].exec("\"the \\\"quick\\\" brown fox\" "), 2977);
-assertEquals("", res[984].exec("abc"), 2978);
-assertEquals("", res[985].exec("abc "), 2979);
-assertEquals("", res[986].exec("abc "), 2980);
+assertNull(res[925].exec("ab ", 2835));
+assertToStringEquals("foo table,foo,table", res[926].exec("Food is on the foo table"), 2836);
+assertToStringEquals("food is under the bar in the bar,d is under the bar in the ", res[927].exec("The food is under the bar in the barn."), 2837);
+assertToStringEquals("food is under the bar,d is under the ", res[928].exec("The food is under the bar in the barn."), 2838);
+assertToStringEquals("I have 2 numbers: 53147,I have 2 numbers: 53147,", res[929].exec("I have 2 numbers: 53147"), 2839);
+assertToStringEquals("I have 2 numbers: 53147,I have 2 numbers: 5314,7", res[930].exec("I have 2 numbers: 53147"), 2840);
+assertToStringEquals(",,", res[931].exec("I have 2 numbers: 53147"), 2841);
+assertToStringEquals("I have 2,I have ,2", res[932].exec("I have 2 numbers: 53147"), 2842);
+assertToStringEquals("I have 2 numbers: 53147,I have 2 numbers: 5314,7", res[933].exec("I have 2 numbers: 53147"), 2843);
+assertToStringEquals("I have 2 numbers: 53147,I have 2 numbers: ,53147", res[934].exec("I have 2 numbers: 53147"), 2844);
+assertToStringEquals("I have 2 numbers: 53147,I have 2 numbers: ,53147", res[935].exec("I have 2 numbers: 53147"), 2845);
+assertToStringEquals("I have 2 numbers: 53147,I have 2 numbers: ,53147", res[936].exec("I have 2 numbers: 53147"), 2846);
+assertToStringEquals("AB", res[937].exec("ABC123"), 2847);
+assertToStringEquals(" ", res[937].exec(" "), 2848);
+assertToStringEquals("ABC,ABC", res[938].exec("ABC445"), 2849);
+assertNull(res[938].exec("*** Failers", 2850));
+assertNull(res[938].exec("ABC123", 2851));
+assertToStringEquals("W46]", res[939].exec("W46]789 "), 2852);
+assertToStringEquals("-46]", res[939].exec("-46]789"), 2853);
+assertNull(res[939].exec("*** Failers", 2854));
+assertNull(res[939].exec("Wall", 2855));
+assertNull(res[939].exec("Zebra", 2856));
+assertNull(res[939].exec("42", 2857));
+assertNull(res[939].exec("[abcd] ", 2858));
+assertNull(res[939].exec("]abcd[", 2859));
+assertNull(res[939].exec(" ", 2860));
+assertToStringEquals("W", res[940].exec("W46]789 "), 2861);
+assertToStringEquals("W", res[940].exec("Wall"), 2862);
+assertToStringEquals("Z", res[940].exec("Zebra"), 2863);
+assertToStringEquals("X", res[940].exec("Xylophone "), 2864);
+assertToStringEquals("4", res[940].exec("42"), 2865);
+assertToStringEquals("[", res[940].exec("[abcd] "), 2866);
+assertToStringEquals("]", res[940].exec("]abcd["), 2867);
+assertToStringEquals("\\", res[940].exec("\\backslash "), 2868);
+assertNull(res[940].exec("*** Failers", 2869));
+assertNull(res[940].exec("-46]789", 2870));
+assertNull(res[940].exec("well", 2871));
+assertToStringEquals("01/01/2000", res[941].exec("01/01/2000"), 2872);
+assertToStringEquals(",", res[944].exec("bcd"), 2873);
+assertToStringEquals(",", res[944].exec("abc"), 2874);
+assertToStringEquals(",", res[944].exec("aab "), 2875);
+assertToStringEquals(",", res[945].exec("bcd"), 2876);
+assertToStringEquals("a,a", res[945].exec("abc"), 2877);
+assertToStringEquals("a,a", res[945].exec("aab "), 2878);
+assertToStringEquals(",", res[946].exec("bcd"), 2879);
+assertToStringEquals("a,a", res[946].exec("abc"), 2880);
+assertToStringEquals("aa,a", res[946].exec("aab "), 2881);
+assertToStringEquals(",", res[947].exec("bcd"), 2882);
+assertToStringEquals("a,a", res[947].exec("abc"), 2883);
+assertToStringEquals("aa,a", res[947].exec("aab"), 2884);
+assertToStringEquals("aaa,a", res[947].exec("aaa "), 2885);
+assertToStringEquals(",", res[948].exec("bcd"), 2886);
+assertToStringEquals("a,a", res[948].exec("abc"), 2887);
+assertToStringEquals("aa,a", res[948].exec("aab"), 2888);
+assertToStringEquals("aaa,a", res[948].exec("aaa"), 2889);
+assertToStringEquals("aaaaaaaa,a", res[948].exec("aaaaaaaa "), 2890);
+assertNull(res[949].exec("bcd", 2891));
+assertToStringEquals("a,a", res[949].exec("abc"), 2892);
+assertToStringEquals("a,a", res[949].exec("aab "), 2893);
+assertNull(res[950].exec("bcd", 2894));
+assertToStringEquals("a,a", res[950].exec("abc"), 2895);
+assertToStringEquals("aa,a", res[950].exec("aab "), 2896);
+assertNull(res[951].exec("bcd", 2897));
+assertToStringEquals("a,a", res[951].exec("abc"), 2898);
+assertToStringEquals("aa,a", res[951].exec("aab"), 2899);
+assertToStringEquals("aaa,a", res[951].exec("aaa "), 2900);
+assertNull(res[952].exec("bcd", 2901));
+assertToStringEquals("a,a", res[952].exec("abc"), 2902);
+assertToStringEquals("aa,a", res[952].exec("aab"), 2903);
+assertToStringEquals("aaa,a", res[952].exec("aaa"), 2904);
+assertToStringEquals("aaaaaaaa,a", res[952].exec("aaaaaaaa "), 2905);
+assertToStringEquals("bib.gif", res[953].exec("borfle\nbib.gif\nno"), 2906);
+assertToStringEquals("bib.gif", res[954].exec("borfle\nbib.gif\nno"), 2907);
+assertToStringEquals("bib.gif", res[955].exec("borfle\nbib.gif\nno"), 2908);
+assertToStringEquals("bib.gif", res[956].exec("borfle\nbib.gif\nno"), 2909);
+assertToStringEquals("bib.gif", res[957].exec("borfle\nbib.gif\nno"), 2910);
+assertToStringEquals("no", res[958].exec("borfle\nbib.gif\nno"), 2911);
+assertToStringEquals("borfle", res[959].exec("borfle\nbib.gif\nno"), 2912);
+assertToStringEquals("no", res[960].exec("borfle\nbib.gif\nno"), 2913);
+assertToStringEquals("borfle", res[961].exec("borfle\nbib.gif\nno"), 2914);
+assertToStringEquals("", res[962].exec("borfle\nbib.gif\nno\n"), 2915);
+assertToStringEquals("borfle", res[963].exec("borfle\nbib.gif\nno\n"), 2916);
+assertToStringEquals("", res[964].exec("borfle\nbib.gif\nno\n"), 2917);
+assertToStringEquals("borfle", res[965].exec("borfle\nbib.gif\nno\n"), 2918);
+assertToStringEquals("1234X,1234X", res[966].exec("abcde\n1234Xyz"), 2919);
+assertToStringEquals("B,B", res[966].exec("BarFoo "), 2920);
+assertNull(res[966].exec("*** Failers", 2921));
+assertNull(res[966].exec("abcde\nBar ", 2922));
+assertToStringEquals("1234X,1234X", res[967].exec("abcde\n1234Xyz"), 2923);
+assertToStringEquals("B,B", res[967].exec("BarFoo "), 2924);
+assertToStringEquals("B,B", res[967].exec("abcde\nBar "), 2925);
+assertToStringEquals("1234X,1234X", res[968].exec("abcde\n1234Xyz"), 2926);
+assertToStringEquals("B,B", res[968].exec("BarFoo "), 2927);
+assertNull(res[968].exec("*** Failers", 2928));
+assertNull(res[968].exec("abcde\nBar ", 2929));
+assertToStringEquals("1234X,1234X", res[969].exec("abcde\n1234Xyz"), 2930);
+assertToStringEquals("B,B", res[969].exec("BarFoo "), 2931);
+assertToStringEquals("B,B", res[969].exec("abcde\nBar "), 2932);
+assertToStringEquals("1234X,1234X", res[969].exec("abcde\n1234Xyz"), 2933);
+assertToStringEquals("B,B", res[969].exec("BarFoo "), 2934);
+assertNull(res[969].exec("*** Failers ", 2935));
+assertToStringEquals("B,B", res[969].exec("abcde\nBar "), 2936);
+assertToStringEquals("1234X,1234X", res[969].exec("abcde\n1234Xyz"), 2937);
+assertToStringEquals("B,B", res[969].exec("BarFoo "), 2938);
+assertNull(res[969].exec("*** Failers ", 2939));
+assertToStringEquals("B,B", res[969].exec("abcde\nBar "), 2940);
+assertNull(res[970].exec("**** Failers", 2941));
+assertNull(res[970].exec("abc\nB", 2942));
+assertNull(res[970].exec(" ", 2943));
+assertNull(res[970].exec("abc\nB", 2944));
+assertNull(res[970].exec("abc\nB", 2945));
+assertNull(res[970].exec(" ", 2946));
+assertNull(res[970].exec("abc\nB", 2947));
+assertNull(res[970].exec("abc\nB", 2948));
+assertToStringEquals("B", res[970].exec("B\n"), 2949);
+assertToStringEquals("123456654321", res[971].exec("123456654321"), 2950);
+assertToStringEquals("123456654321", res[972].exec("123456654321 "), 2951);
+assertToStringEquals("123456654321", res[973].exec("123456654321"), 2952);
+assertToStringEquals("abcabcabcabc", res[974].exec("abcabcabcabc"), 2953);
+assertToStringEquals("abcabcabcabc", res[975].exec("abcabcabcabc"), 2954);
+assertToStringEquals("abcabcabcabc,c", res[976].exec("abcabcabcabc "), 2955);
+assertToStringEquals("n", res[977].exec("n"), 2956);
+assertNull(res[977].exec("*** Failers ", 2957));
+assertNull(res[977].exec("z ", 2958));
+assertToStringEquals("abcd", res[978].exec("abcd"), 2959);
+assertNull(res[978].exec("*** Failers", 2960));
+assertNull(res[978].exec("abce ", 2961));
+assertToStringEquals("abe", res[979].exec("abe"), 2962);
+assertNull(res[979].exec("*** Failers", 2963));
+assertNull(res[979].exec("abcde ", 2964));
+assertToStringEquals("abd,", res[980].exec("abd"), 2965);
+assertNull(res[980].exec("*** Failers", 2966));
+assertNull(res[980].exec("abcd ", 2967));
+assertToStringEquals("a,", res[981].exec("a"), 2968);
+assertToStringEquals("ab,b", res[981].exec("ab"), 2969);
+assertToStringEquals("abbbb,bbbb", res[981].exec("abbbb"), 2970);
+assertToStringEquals("a,", res[981].exec("*** Failers"), 2971);
+assertNull(res[981].exec("bbbbb ", 2972));
+assertToStringEquals("abe", res[982].exec("abe"), 2973);
+assertNull(res[982].exec("*** Failers", 2974));
+assertNull(res[982].exec("ab1e ", 2975));
+assertToStringEquals("\"quick\",quick", res[983].exec("the \"quick\" brown fox"), 2976);
+assertToStringEquals("\"the \\\"quick\\\" brown fox\", brown fox", res[983].exec("\"the \\\"quick\\\" brown fox\" "), 2977);
+assertToStringEquals("", res[984].exec("abc"), 2978);
+assertToStringEquals("", res[985].exec("abc "), 2979);
+assertToStringEquals("", res[986].exec("abc "), 2980);
assertThrows("var re = //;", 2981);
-assertEquals("", res[986].exec("abc"), 2982);
-assertEquals("acb", res[988].exec("acb"), 2983);
-assertEquals("a\nb", res[988].exec("a\nb"), 2984);
-assertEquals("acb", res[989].exec("acb"), 2985);
-assertEquals(null, res[989].exec("*** Failers ", 2986));
-assertEquals(null, res[989].exec("a\nb ", 2987));
-assertEquals("acb", res[990].exec("acb"), 2988);
-assertEquals("a\nb", res[990].exec("a\nb "), 2989);
-assertEquals("acb", res[991].exec("acb"), 2990);
-assertEquals(null, res[991].exec("a\nb ", 2991));
-assertEquals("bac,a", res[992].exec("bac"), 2992);
-assertEquals("bbac,a", res[992].exec("bbac"), 2993);
-assertEquals("bbbac,a", res[992].exec("bbbac"), 2994);
-assertEquals("bbbbac,a", res[992].exec("bbbbac"), 2995);
-assertEquals("bbbbbac,a", res[992].exec("bbbbbac "), 2996);
-assertEquals("bac,a", res[993].exec("bac"), 2997);
-assertEquals("bbac,a", res[993].exec("bbac"), 2998);
-assertEquals("bbbac,a", res[993].exec("bbbac"), 2999);
-assertEquals("bbbbac,a", res[993].exec("bbbbac"), 3000);
-assertEquals("bbbbbac,a", res[993].exec("bbbbbac "), 3001);
-assertEquals("x", res[994].exec("x\nb\n"), 3002);
-assertEquals("x", res[994].exec("a\x08x\n "), 3003);
-assertEquals(null, res[995].exec("\x00{ab} ", 3004));
-assertEquals("CD,", res[996].exec("CD "), 3005);
-assertEquals("CD,", res[997].exec("CD "), 3006);
-assertEquals(null, res[997].exec("foo", 3007));
-assertEquals(null, res[997].exec("catfood", 3008));
-assertEquals(null, res[997].exec("arfootle", 3009));
-assertEquals(null, res[997].exec("rfoosh", 3010));
-assertEquals(null, res[997].exec("*** Failers", 3011));
-assertEquals(null, res[997].exec("barfoo", 3012));
-assertEquals(null, res[997].exec("towbarfoo", 3013));
-assertEquals(null, res[997].exec("catfood", 3014));
-assertEquals(null, res[997].exec("*** Failers", 3015));
-assertEquals(null, res[997].exec("foo", 3016));
-assertEquals(null, res[997].exec("barfoo", 3017));
-assertEquals(null, res[997].exec("towbarfoo", 3018));
-assertEquals(null, res[997].exec("fooabar", 3019));
-assertEquals(null, res[997].exec("*** Failers", 3020));
-assertEquals(null, res[997].exec("bar", 3021));
-assertEquals(null, res[997].exec("foobbar", 3022));
-assertEquals(null, res[997].exec(" ", 3023));
-assertEquals(null, res[998].exec("abc", 3024));
-assertEquals(null, res[998].exec("*** Failers", 3025));
-assertEquals(null, res[998].exec("abc\n ", 3026));
-assertEquals(null, res[998].exec("qqq\nabc", 3027));
-assertEquals(null, res[998].exec("abc\nzzz", 3028));
-assertEquals(null, res[998].exec("qqq\nabc\nzzz", 3029));
-assertEquals(null, res[998].exec("/this/is/a/very/long/line/in/deed/with/very/many/slashes/in/it/you/see/", 3030));
-assertEquals(null, res[998].exec("/this/is/a/very/long/line/in/deed/with/very/many/slashes/in/and/foo", 3031));
-assertEquals(null, res[998].exec("1.230003938", 3032));
-assertEquals(null, res[998].exec("1.875000282", 3033));
-assertEquals(null, res[998].exec("*** Failers ", 3034));
-assertEquals(null, res[998].exec("1.235 ", 3035));
-assertEquals(null, res[998].exec("now is the time for all good men to come to the aid of the party", 3036));
-assertEquals(null, res[998].exec("*** Failers", 3037));
-assertEquals(null, res[998].exec("this is not a line with only words and spaces!", 3038));
-assertEquals("12345a,12345,a", res[999].exec("12345a"), 3039);
-assertEquals("12345,1234,5", res[999].exec("12345+ "), 3040);
-assertEquals("12345a,12345,a", res[999].exec("12345a"), 3041);
-assertEquals(null, res[999].exec("*** Failers", 3042));
-assertEquals("12345,1234,5", res[999].exec("12345+ "), 3043);
-assertEquals(null, res[999].exec("aaab", 3044));
-assertEquals(null, res[999].exec("aaab", 3045));
-assertEquals(null, res[999].exec("aaab", 3046));
-assertEquals(null, res[999].exec("aaabbbccc", 3047));
-assertEquals(null, res[999].exec("aaabbbbccccd", 3048));
-assertEquals("aaabbbbcccc,ccc", res[1000].exec("aaabbbbccccd"), 3049);
-assertEquals("abc,b", res[1000].exec("((abc(ade)ufh()()x"), 3050);
-assertEquals(null, res[1000].exec("", 3051));
-assertEquals("abc,b", res[1000].exec("(abc)"), 3052);
-assertEquals("abc,b", res[1000].exec("(abc(def)xyz)"), 3053);
-assertEquals(null, res[1000].exec("*** Failers", 3054));
-assertEquals(null, res[1000].exec("ab", 3055));
-assertEquals(null, res[1000].exec("Ab", 3056));
-assertEquals(null, res[1000].exec("*** Failers ", 3057));
-assertEquals(null, res[1000].exec("aB", 3058));
-assertEquals(null, res[1000].exec("AB", 3059));
-assertEquals(null, res[1000].exec(" ", 3060));
-assertEquals("bc,b", res[1000].exec("a bcd e"), 3061);
-assertEquals(null, res[1000].exec("*** Failers", 3062));
-assertEquals("c,", res[1000].exec("a b cd e"), 3063);
-assertEquals("abc,b", res[1000].exec("abcd e "), 3064);
-assertEquals("bc,b", res[1000].exec("a bcde "), 3065);
-assertEquals("bc,b", res[1000].exec("a bcde f"), 3066);
-assertEquals(null, res[1000].exec("*** Failers", 3067));
-assertEquals("abc,b", res[1000].exec("abcdef "), 3068);
-assertEquals("abc,b", res[1000].exec("abc"), 3069);
-assertEquals("c,", res[1000].exec("aBc"), 3070);
-assertEquals(null, res[1000].exec("*** Failers", 3071));
-assertEquals(null, res[1000].exec("abC", 3072));
-assertEquals(null, res[1000].exec("aBC ", 3073));
-assertEquals("bc,b", res[1000].exec("Abc"), 3074);
-assertEquals("c,", res[1000].exec("ABc"), 3075);
-assertEquals(null, res[1000].exec("ABC", 3076));
-assertEquals(null, res[1000].exec("AbC", 3077));
-assertEquals(null, res[1000].exec("", 3078));
-assertEquals("abc,b", res[1000].exec("abc"), 3079);
-assertEquals("c,", res[1000].exec("aBc"), 3080);
-assertEquals(null, res[1000].exec("*** Failers ", 3081));
-assertEquals(null, res[1000].exec("ABC", 3082));
-assertEquals(null, res[1000].exec("abC", 3083));
-assertEquals(null, res[1000].exec("aBC", 3084));
-assertEquals(null, res[1000].exec("", 3085));
-assertEquals("c,", res[1000].exec("aBc"), 3086);
-assertEquals("c,", res[1000].exec("aBBc"), 3087);
-assertEquals(null, res[1000].exec("*** Failers ", 3088));
-assertEquals(null, res[1000].exec("aBC", 3089));
-assertEquals(null, res[1000].exec("aBBC", 3090));
-assertEquals(null, res[1000].exec("", 3091));
-assertEquals("abc,b", res[1000].exec("abcd"), 3092);
-assertEquals(null, res[1000].exec("abCd", 3093));
-assertEquals(null, res[1000].exec("*** Failers", 3094));
-assertEquals(null, res[1000].exec("aBCd", 3095));
-assertEquals("abc,b", res[1000].exec("abcD "), 3096);
-assertEquals(null, res[1000].exec("", 3097));
-assertEquals(null, res[1000].exec("more than million", 3098));
-assertEquals(null, res[1000].exec("more than MILLION", 3099));
-assertEquals(null, res[1000].exec("more \n than Million ", 3100));
-assertEquals(null, res[1000].exec("*** Failers", 3101));
-assertEquals(null, res[1000].exec("MORE THAN MILLION ", 3102));
-assertEquals(null, res[1000].exec("more \n than \n million ", 3103));
-assertEquals(null, res[1000].exec("more than million", 3104));
-assertEquals(null, res[1000].exec("more than MILLION", 3105));
-assertEquals(null, res[1000].exec("more \n than Million ", 3106));
-assertEquals(null, res[1000].exec("*** Failers", 3107));
-assertEquals(null, res[1000].exec("MORE THAN MILLION ", 3108));
-assertEquals(null, res[1000].exec("more \n than \n million ", 3109));
-assertEquals(null, res[1000].exec("", 3110));
-assertEquals("abc,b", res[1000].exec("abc"), 3111);
-assertEquals("bc,b", res[1000].exec("aBbc"), 3112);
-assertEquals("c,", res[1000].exec("aBBc "), 3113);
-assertEquals(null, res[1000].exec("*** Failers", 3114));
-assertEquals("bc,b", res[1000].exec("Abc"), 3115);
-assertEquals(null, res[1000].exec("abAb ", 3116));
-assertEquals(null, res[1000].exec("abbC ", 3117));
-assertEquals(null, res[1000].exec("", 3118));
-assertEquals("abc,b", res[1000].exec("abc"), 3119);
-assertEquals("c,", res[1000].exec("aBc"), 3120);
-assertEquals(null, res[1000].exec("*** Failers", 3121));
-assertEquals(null, res[1000].exec("Ab ", 3122));
-assertEquals(null, res[1000].exec("abC", 3123));
-assertEquals(null, res[1000].exec("aBC ", 3124));
-assertEquals(null, res[1000].exec("", 3125));
-assertEquals("c,", res[1000].exec("abxxc"), 3126);
-assertEquals("c,", res[1000].exec("aBxxc"), 3127);
-assertEquals(null, res[1000].exec("*** Failers", 3128));
-assertEquals("c,", res[1000].exec("Abxxc"), 3129);
-assertEquals("c,", res[1000].exec("ABxxc"), 3130);
-assertEquals(null, res[1000].exec("abxxC ", 3131));
-assertEquals("abc,b", res[1000].exec("abc:"), 3132);
-assertEquals(null, res[1000].exec("12", 3133));
-assertEquals(null, res[1000].exec("*** Failers", 3134));
-assertEquals(null, res[1000].exec("123", 3135));
-assertEquals(null, res[1000].exec("xyz ", 3136));
-assertEquals("abc,b", res[1000].exec("abc:"), 3137);
-assertEquals(null, res[1000].exec("12", 3138));
-assertEquals(null, res[1000].exec("*** Failers", 3139));
-assertEquals(null, res[1000].exec("123", 3140));
-assertEquals(null, res[1000].exec("xyz ", 3141));
-assertEquals(null, res[1000].exec("", 3142));
-assertEquals(null, res[1000].exec("foobar", 3143));
-assertEquals("c,", res[1000].exec("cat"), 3144);
-assertEquals("c,", res[1000].exec("fcat"), 3145);
-assertEquals("c,", res[1000].exec("focat "), 3146);
-assertEquals(null, res[1000].exec("*** Failers", 3147));
-assertEquals("c,", res[1000].exec("foocat "), 3148);
-assertEquals(null, res[1000].exec("foobar", 3149));
-assertEquals("c,", res[1000].exec("cat"), 3150);
-assertEquals("c,", res[1000].exec("fcat"), 3151);
-assertEquals("c,", res[1000].exec("focat "), 3152);
-assertEquals(null, res[1000].exec("*** Failers", 3153));
-assertEquals("c,", res[1000].exec("foocat "), 3154);
-assertEquals(null, res[1000].exec("a", 3155));
-assertEquals(null, res[1000].exec("aa", 3156));
-assertEquals(null, res[1000].exec("aaaa", 3157));
-assertEquals(null, res[1000].exec("", 3158));
-assertEquals("abc,abc", res[1001].exec("abc"), 3159);
-assertEquals("abcabc,abc", res[1001].exec("abcabc"), 3160);
-assertEquals("abcabcabc,abc", res[1001].exec("abcabcabc"), 3161);
-assertEquals(",", res[1001].exec("xyz "), 3162);
-assertEquals("a,a", res[1002].exec("a"), 3163);
-assertEquals("aaaaa,aaaaa", res[1002].exec("aaaaa "), 3164);
-assertEquals("a,a", res[1003].exec("a"), 3165);
-assertEquals("b,b", res[1003].exec("b"), 3166);
-assertEquals("ababab,ababab", res[1003].exec("ababab"), 3167);
-assertEquals("aaaab,aaaab", res[1003].exec("aaaabcde"), 3168);
-assertEquals("bbbb,bbbb", res[1003].exec("bbbb "), 3169);
-assertEquals("b,b", res[1004].exec("b"), 3170);
-assertEquals("bbbb,bbbb", res[1004].exec("bbbb"), 3171);
-assertEquals(",", res[1004].exec("aaa "), 3172);
-assertEquals("cccc,cccc", res[1005].exec("cccc"), 3173);
-assertEquals(",", res[1005].exec("abab "), 3174);
-assertEquals("a,a", res[1006].exec("a"), 3175);
-assertEquals("aaaa,a", res[1006].exec("aaaa "), 3176);
-assertEquals("a,a", res[1007].exec("a"), 3177);
-assertEquals("b,b", res[1007].exec("b"), 3178);
-assertEquals("abab,b", res[1007].exec("abab"), 3179);
-assertEquals("baba,a", res[1007].exec("baba "), 3180);
-assertEquals("b,b", res[1008].exec("b"), 3181);
-assertEquals("bbbb,b", res[1008].exec("bbbb"), 3182);
-assertEquals(",", res[1008].exec("aaa "), 3183);
-assertEquals("c,c", res[1009].exec("c"), 3184);
-assertEquals("cccc,c", res[1009].exec("cccc"), 3185);
-assertEquals(",", res[1009].exec("baba "), 3186);
-assertEquals(",", res[1009].exec("a"), 3187);
-assertEquals(",", res[1009].exec("aaabcde "), 3188);
-assertEquals(",", res[1009].exec("aaaaa"), 3189);
-assertEquals(",", res[1009].exec("aabbaa "), 3190);
-assertEquals(",", res[1009].exec("aaaaa"), 3191);
-assertEquals(",", res[1009].exec("aabbaa "), 3192);
-assertEquals("12-sep-98,8", res[1009].exec("12-sep-98"), 3193);
-assertEquals("12-09-98,8", res[1009].exec("12-09-98"), 3194);
-assertEquals("*** F,F", res[1009].exec("*** Failers"), 3195);
-assertEquals("sep-12-98,8", res[1009].exec("sep-12-98"), 3196);
-assertEquals(" , ", res[1009].exec(" "), 3197);
-assertEquals("s,s", res[1009].exec("saturday"), 3198);
-assertEquals("sund,d", res[1009].exec("sunday"), 3199);
-assertEquals("S,S", res[1009].exec("Saturday"), 3200);
-assertEquals("Sund,d", res[1009].exec("Sunday"), 3201);
-assertEquals("SATURDAY,Y", res[1009].exec("SATURDAY"), 3202);
-assertEquals("SUNDAY,Y", res[1009].exec("SUNDAY"), 3203);
-assertEquals("SunD,D", res[1009].exec("SunDay"), 3204);
-assertEquals(",", res[1009].exec("abcx"), 3205);
-assertEquals(",", res[1009].exec("aBCx"), 3206);
-assertEquals(",", res[1009].exec("bbx"), 3207);
-assertEquals("BBx,x", res[1009].exec("BBx"), 3208);
-assertEquals("*** F,F", res[1009].exec("*** Failers"), 3209);
-assertEquals(",", res[1009].exec("abcX"), 3210);
-assertEquals(",", res[1009].exec("aBCX"), 3211);
-assertEquals(",", res[1009].exec("bbX"), 3212);
-assertEquals("BBX , ", res[1009].exec("BBX "), 3213);
-assertEquals(",", res[1009].exec("ac"), 3214);
-assertEquals(",", res[1009].exec("aC"), 3215);
-assertEquals(",", res[1009].exec("bD"), 3216);
-assertEquals("eleph,h", res[1009].exec("elephant"), 3217);
-assertEquals("Europe , ", res[1009].exec("Europe "), 3218);
-assertEquals("frog,g", res[1009].exec("frog"), 3219);
-assertEquals("Fr,r", res[1009].exec("France"), 3220);
-assertEquals("*** F,F", res[1009].exec("*** Failers"), 3221);
-assertEquals("Afric,c", res[1009].exec("Africa "), 3222);
-assertEquals(",", res[1009].exec("ab"), 3223);
-assertEquals(",", res[1009].exec("aBd"), 3224);
-assertEquals("xy,y", res[1009].exec("xy"), 3225);
-assertEquals("xY,Y", res[1009].exec("xY"), 3226);
-assertEquals("ze,e", res[1009].exec("zebra"), 3227);
-assertEquals("Z,Z", res[1009].exec("Zambesi"), 3228);
-assertEquals("*** F,F", res[1009].exec("*** Failers"), 3229);
-assertEquals(",", res[1009].exec("aCD "), 3230);
-assertEquals("XY , ", res[1009].exec("XY "), 3231);
-assertEquals("foo\n,\n", res[1009].exec("foo\nbar"), 3232);
-assertEquals("*** F,F", res[1009].exec("*** Failers"), 3233);
-assertEquals(",", res[1009].exec("bar"), 3234);
-assertEquals(",", res[1009].exec("baz\nbar "), 3235);
-assertEquals(",", res[1009].exec("barbaz"), 3236);
-assertEquals(",", res[1009].exec("barbarbaz "), 3237);
-assertEquals("koo,o", res[1009].exec("koobarbaz "), 3238);
-assertEquals("*** F,F", res[1009].exec("*** Failers"), 3239);
-assertEquals(",", res[1009].exec("baz"), 3240);
-assertEquals("foo,o", res[1009].exec("foobarbaz "), 3241);
-assertEquals("abc", res[1012].exec("abc"), 3242);
-assertEquals("abc", res[1012].exec("xabcy"), 3243);
-assertEquals("abc", res[1012].exec("ababc"), 3244);
-assertEquals(null, res[1012].exec("*** Failers", 3245));
-assertEquals(null, res[1012].exec("xbc", 3246));
-assertEquals(null, res[1012].exec("axc", 3247));
-assertEquals(null, res[1012].exec("abx", 3248));
-assertEquals("abc", res[1013].exec("abc"), 3249);
-assertEquals("abc", res[1014].exec("abc"), 3250);
-assertEquals("abbc", res[1014].exec("abbc"), 3251);
-assertEquals("abbbbc", res[1014].exec("abbbbc"), 3252);
-assertEquals("a", res[1015].exec("abbbbc"), 3253);
-assertEquals("abbb", res[1016].exec("abbbbc"), 3254);
-assertEquals("abbbbc", res[1017].exec("abbbbc"), 3255);
-assertEquals("abbc", res[1018].exec("abbc"), 3256);
-assertEquals(null, res[1018].exec("*** Failers", 3257));
-assertEquals(null, res[1018].exec("abc", 3258));
-assertEquals(null, res[1018].exec("abq", 3259));
-assertEquals("abbbbc", res[1020].exec("abbbbc"), 3260);
-assertEquals("abbbbc", res[1021].exec("abbbbc"), 3261);
-assertEquals("abbbbc", res[1022].exec("abbbbc"), 3262);
-assertEquals("abbbbc", res[1023].exec("abbbbc"), 3263);
-assertEquals(null, res[1024].exec("*** Failers", 3264));
-assertEquals(null, res[1024].exec("abq", 3265));
-assertEquals(null, res[1024].exec("abbbbc", 3266));
-assertEquals("abbc", res[1025].exec("abbc"), 3267);
-assertEquals("abc", res[1025].exec("abc"), 3268);
-assertEquals("abc", res[1026].exec("abc"), 3269);
-assertEquals("abc", res[1028].exec("abc"), 3270);
-assertEquals("abc", res[1029].exec("abc"), 3271);
-assertEquals("abc", res[1030].exec("abc"), 3272);
-assertEquals(null, res[1030].exec("*** Failers", 3273));
-assertEquals(null, res[1030].exec("abbbbc", 3274));
-assertEquals(null, res[1030].exec("abcc", 3275));
-assertEquals("abc", res[1031].exec("abcc"), 3276);
-assertEquals("abc", res[1033].exec("aabc"), 3277);
-assertEquals(null, res[1033].exec("*** Failers", 3278));
-assertEquals("abc", res[1033].exec("aabc"), 3279);
-assertEquals(null, res[1033].exec("aabcd", 3280));
-assertEquals("", res[1034].exec("abc"), 3281);
-assertEquals("", res[1035].exec("abc"), 3282);
-assertEquals("abc", res[1036].exec("abc"), 3283);
-assertEquals("axc", res[1036].exec("axc"), 3284);
-assertEquals("axyzc", res[1037].exec("axyzc"), 3285);
-assertEquals("abd", res[1038].exec("abd"), 3286);
-assertEquals(null, res[1038].exec("*** Failers", 3287));
-assertEquals(null, res[1038].exec("axyzd", 3288));
-assertEquals(null, res[1038].exec("abc", 3289));
-assertEquals("ace", res[1039].exec("ace"), 3290);
-assertEquals("ac", res[1040].exec("aac"), 3291);
-assertEquals("a-", res[1041].exec("a-"), 3292);
-assertEquals("a-", res[1042].exec("a-"), 3293);
-assertEquals("a]", res[1043].exec("a]"), 3294);
-assertEquals(null, res[1044].exec("a]b", 3295));
-assertEquals("aed", res[1045].exec("aed"), 3296);
-assertEquals(null, res[1045].exec("*** Failers", 3297));
-assertEquals(null, res[1045].exec("abd", 3298));
-assertEquals(null, res[1045].exec("abd", 3299));
-assertEquals("adc", res[1046].exec("adc"), 3300);
-assertEquals(null, res[1047].exec("adc", 3301));
-assertEquals(null, res[1047].exec("*** Failers", 3302));
-assertEquals(null, res[1047].exec("a-c", 3303));
-assertEquals(null, res[1047].exec("a]c", 3304));
-assertEquals("a", res[1048].exec("a-"), 3305);
-assertEquals("a", res[1048].exec("-a"), 3306);
-assertEquals("a", res[1048].exec("-a-"), 3307);
-assertEquals(null, res[1049].exec("*** Failers", 3308));
-assertEquals(null, res[1049].exec("xy", 3309));
-assertEquals(null, res[1049].exec("yz", 3310));
-assertEquals(null, res[1049].exec("xyz", 3311));
-assertEquals("a", res[1050].exec("*** Failers"), 3312);
-assertEquals(null, res[1050].exec("a-", 3313));
-assertEquals(null, res[1050].exec("-a", 3314));
-assertEquals(null, res[1050].exec("-a-", 3315));
-assertEquals("y", res[1051].exec("xy"), 3316);
-assertEquals("y", res[1052].exec("yz"), 3317);
-assertEquals("y", res[1053].exec("xyz"), 3318);
-assertEquals("a", res[1054].exec("a"), 3319);
-assertEquals("-", res[1055].exec("-"), 3320);
-assertEquals("*", res[1055].exec("*** Failers"), 3321);
-assertEquals("-", res[1055].exec("-"), 3322);
-assertEquals(null, res[1055].exec("a", 3323));
-assertEquals("a b", res[1056].exec("a b"), 3324);
-assertEquals("a-b", res[1057].exec("a-b"), 3325);
-assertEquals(null, res[1057].exec("*** Failers", 3326));
-assertEquals("a-b", res[1057].exec("a-b"), 3327);
-assertEquals(null, res[1057].exec("a b", 3328));
-assertEquals("1", res[1058].exec("1"), 3329);
-assertEquals("-", res[1059].exec("-"), 3330);
-assertEquals("*", res[1059].exec("*** Failers"), 3331);
-assertEquals("-", res[1059].exec("-"), 3332);
-assertEquals(null, res[1059].exec("1", 3333));
-assertEquals("a", res[1060].exec("a"), 3334);
-assertEquals("-", res[1061].exec("-"), 3335);
-assertEquals("*", res[1061].exec("*** Failers"), 3336);
-assertEquals("-", res[1061].exec("-"), 3337);
-assertEquals(null, res[1061].exec("a", 3338));
-assertEquals("a b", res[1062].exec("a b"), 3339);
-assertEquals("a-b", res[1063].exec("a-b"), 3340);
-assertEquals(null, res[1063].exec("*** Failers", 3341));
-assertEquals("a-b", res[1063].exec("a-b"), 3342);
-assertEquals(null, res[1063].exec("a b", 3343));
-assertEquals("1", res[1064].exec("1"), 3344);
-assertEquals("-", res[1065].exec("-"), 3345);
-assertEquals("*", res[1065].exec("*** Failers"), 3346);
-assertEquals("-", res[1065].exec("-"), 3347);
-assertEquals(null, res[1065].exec("1", 3348));
-assertEquals("ab", res[1066].exec("abc"), 3349);
-assertEquals("ab", res[1066].exec("abcd"), 3350);
-assertEquals("ef,", res[1067].exec("def"), 3351);
-assertEquals("a(b", res[1069].exec("a(b"), 3352);
-assertEquals(null, res[1069].exec("ab", 3353));
-assertEquals(null, res[1069].exec("a((b", 3354));
-assertEquals(null, res[1070].exec("a\x08", 3355));
-assertEquals("a,a,a", res[1071].exec("abc"), 3356);
-assertEquals("abc,a,c", res[1072].exec("abc"), 3357);
-assertEquals("abc", res[1073].exec("aabbabc"), 3358);
-assertEquals("abc", res[1074].exec("aabbabc"), 3359);
-assertEquals("abc", res[1075].exec("abcabc"), 3360);
-assertEquals("ab,b", res[1076].exec("ab"), 3361);
-assertEquals("ab,b", res[1077].exec("ab"), 3362);
-assertEquals("ab,b", res[1078].exec("ab"), 3363);
-assertEquals("ab,b", res[1079].exec("ab"), 3364);
-assertEquals("a,a", res[1080].exec("ab"), 3365);
-assertEquals("a,a", res[1081].exec("ab"), 3366);
-assertEquals("cde", res[1082].exec("cde"), 3367);
-assertEquals(null, res[1083].exec("*** Failers", 3368));
-assertEquals(null, res[1083].exec("b", 3369));
-assertEquals("abbbcd,c", res[1085].exec("abbbcd"), 3370);
-assertEquals("abcd,a", res[1086].exec("abcd"), 3371);
-assertEquals("e", res[1087].exec("e"), 3372);
-assertEquals("ef,e", res[1088].exec("ef"), 3373);
-assertEquals("abcdefg", res[1089].exec("abcdefg"), 3374);
-assertEquals("ab", res[1090].exec("xabyabbbz"), 3375);
-assertEquals("a", res[1090].exec("xayabbbz"), 3376);
-assertEquals("cde,cd", res[1091].exec("abcde"), 3377);
-assertEquals("hij", res[1092].exec("hij"), 3378);
-assertEquals("ef,", res[1094].exec("abcdef"), 3379);
-assertEquals("bcd,b", res[1095].exec("abcd"), 3380);
-assertEquals("abc,a", res[1096].exec("abc"), 3381);
-assertEquals("abc,bc", res[1097].exec("abc"), 3382);
-assertEquals("abcd,bc,d", res[1098].exec("abcd"), 3383);
-assertEquals("abcd,bc,d", res[1099].exec("abcd"), 3384);
-assertEquals("abcd,b,cd", res[1100].exec("abcd"), 3385);
-assertEquals("adcdcde", res[1101].exec("adcdcde"), 3386);
-assertEquals(null, res[1102].exec("*** Failers", 3387));
-assertEquals(null, res[1102].exec("abcde", 3388));
-assertEquals(null, res[1102].exec("adcdcde", 3389));
-assertEquals("abc,ab", res[1103].exec("abc"), 3390);
-assertEquals("abcd,abc,a,b,d", res[1104].exec("abcd"), 3391);
-assertEquals("alpha", res[1105].exec("alpha"), 3392);
-assertEquals("bh,", res[1106].exec("abh"), 3393);
-assertEquals("effgz,effgz,", res[1107].exec("effgz"), 3394);
-assertEquals("ij,ij,j", res[1107].exec("ij"), 3395);
-assertEquals("effgz,effgz,", res[1107].exec("reffgz"), 3396);
-assertEquals(null, res[1107].exec("*** Failers", 3397));
-assertEquals(null, res[1107].exec("effg", 3398));
-assertEquals(null, res[1107].exec("bcdd", 3399));
-assertEquals("a,a,a,a,a,a,a,a,a,a,a", res[1108].exec("a"), 3400);
-assertEquals("a,a,a,a,a,a,a,a,a,a", res[1109].exec("a"), 3401);
-assertEquals(null, res[1110].exec("*** Failers", 3402));
-assertEquals(null, res[1110].exec("aa", 3403));
-assertEquals(null, res[1110].exec("uh-uh", 3404));
-assertEquals("multiple words", res[1111].exec("multiple words, yeah"), 3405);
-assertEquals("abcde,ab,de", res[1112].exec("abcde"), 3406);
-assertEquals("(a, b),a,b", res[1113].exec("(a, b)"), 3407);
-assertEquals("abcd", res[1115].exec("abcd"), 3408);
-assertEquals("abcd,bc", res[1116].exec("abcd"), 3409);
-assertEquals("ac", res[1117].exec("ac"), 3410);
-assertEquals("ABC", res[1118].exec("ABC"), 3411);
-assertEquals("ABC", res[1118].exec("XABCY"), 3412);
-assertEquals("ABC", res[1118].exec("ABABC"), 3413);
-assertEquals(null, res[1118].exec("*** Failers", 3414));
-assertEquals(null, res[1118].exec("aaxabxbaxbbx", 3415));
-assertEquals(null, res[1118].exec("XBC", 3416));
-assertEquals(null, res[1118].exec("AXC", 3417));
-assertEquals(null, res[1118].exec("ABX", 3418));
-assertEquals("ABC", res[1119].exec("ABC"), 3419);
-assertEquals("ABC", res[1120].exec("ABC"), 3420);
-assertEquals("ABBC", res[1120].exec("ABBC"), 3421);
-assertEquals("ABBBBC", res[1121].exec("ABBBBC"), 3422);
-assertEquals("ABBBBC", res[1122].exec("ABBBBC"), 3423);
-assertEquals("ABBC", res[1123].exec("ABBC"), 3424);
-assertEquals(null, res[1124].exec("*** Failers", 3425));
-assertEquals(null, res[1124].exec("ABC", 3426));
-assertEquals(null, res[1124].exec("ABQ", 3427));
-assertEquals("ABBBBC", res[1126].exec("ABBBBC"), 3428);
-assertEquals("ABBBBC", res[1127].exec("ABBBBC"), 3429);
-assertEquals("ABBBBC", res[1128].exec("ABBBBC"), 3430);
-assertEquals("ABBBBC", res[1129].exec("ABBBBC"), 3431);
-assertEquals(null, res[1130].exec("*** Failers", 3432));
-assertEquals(null, res[1130].exec("ABQ", 3433));
-assertEquals(null, res[1130].exec("ABBBBC", 3434));
-assertEquals("ABBC", res[1131].exec("ABBC"), 3435);
-assertEquals("ABC", res[1131].exec("ABC"), 3436);
-assertEquals("ABC", res[1132].exec("ABC"), 3437);
-assertEquals("ABC", res[1134].exec("ABC"), 3438);
-assertEquals("ABC", res[1135].exec("ABC"), 3439);
-assertEquals("ABC", res[1136].exec("ABC"), 3440);
-assertEquals(null, res[1136].exec("*** Failers", 3441));
-assertEquals(null, res[1136].exec("ABBBBC", 3442));
-assertEquals(null, res[1136].exec("ABCC", 3443));
-assertEquals("ABC", res[1137].exec("ABCC"), 3444);
-assertEquals("ABC", res[1139].exec("AABC"), 3445);
-assertEquals("", res[1140].exec("ABC"), 3446);
-assertEquals("", res[1141].exec("ABC"), 3447);
-assertEquals("ABC", res[1142].exec("ABC"), 3448);
-assertEquals("AXC", res[1142].exec("AXC"), 3449);
-assertEquals("AXYZC", res[1143].exec("AXYZC"), 3450);
-assertEquals(null, res[1144].exec("*** Failers", 3451));
-assertEquals("AABC", res[1144].exec("AABC"), 3452);
-assertEquals(null, res[1144].exec("AXYZD", 3453));
-assertEquals("ABD", res[1145].exec("ABD"), 3454);
-assertEquals("ACE", res[1146].exec("ACE"), 3455);
-assertEquals(null, res[1146].exec("*** Failers", 3456));
-assertEquals(null, res[1146].exec("ABC", 3457));
-assertEquals(null, res[1146].exec("ABD", 3458));
-assertEquals("AC", res[1147].exec("AAC"), 3459);
-assertEquals("A-", res[1148].exec("A-"), 3460);
-assertEquals("A-", res[1149].exec("A-"), 3461);
-assertEquals("A]", res[1150].exec("A]"), 3462);
-assertEquals(null, res[1151].exec("A]B", 3463));
-assertEquals("AED", res[1152].exec("AED"), 3464);
-assertEquals("ADC", res[1153].exec("ADC"), 3465);
-assertEquals(null, res[1153].exec("*** Failers", 3466));
-assertEquals(null, res[1153].exec("ABD", 3467));
-assertEquals(null, res[1153].exec("A-C", 3468));
-assertEquals(null, res[1154].exec("ADC", 3469));
-assertEquals("AB", res[1155].exec("ABC"), 3470);
-assertEquals("AB", res[1155].exec("ABCD"), 3471);
-assertEquals("EF,", res[1156].exec("DEF"), 3472);
-assertEquals(null, res[1157].exec("*** Failers", 3473));
-assertEquals(null, res[1157].exec("A]C", 3474));
-assertEquals(null, res[1157].exec("B", 3475));
-assertEquals("A(B", res[1158].exec("A(B"), 3476);
-assertEquals(null, res[1158].exec("AB", 3477));
-assertEquals(null, res[1158].exec("A((B", 3478));
-assertEquals(null, res[1159].exec("AB", 3479));
-assertEquals("A,A,A", res[1160].exec("ABC"), 3480);
-assertEquals("ABC,A,C", res[1161].exec("ABC"), 3481);
-assertEquals("ABC", res[1162].exec("AABBABC"), 3482);
-assertEquals("ABC", res[1163].exec("AABBABC"), 3483);
-assertEquals("ABC", res[1164].exec("ABCABC"), 3484);
-assertEquals("ABC", res[1165].exec("ABCABC"), 3485);
-assertEquals("ABC", res[1166].exec("ABCABC"), 3486);
-assertEquals("AB,B", res[1167].exec("AB"), 3487);
-assertEquals("AB,B", res[1168].exec("AB"), 3488);
-assertEquals("AB,B", res[1169].exec("AB"), 3489);
-assertEquals("AB,B", res[1170].exec("AB"), 3490);
-assertEquals("A,A", res[1171].exec("AB"), 3491);
-assertEquals("A,A", res[1172].exec("AB"), 3492);
-assertEquals(",", res[1173].exec("AB"), 3493);
-assertEquals("CDE", res[1174].exec("CDE"), 3494);
-assertEquals("ABBBCD,C", res[1177].exec("ABBBCD"), 3495);
-assertEquals("ABCD,A", res[1178].exec("ABCD"), 3496);
-assertEquals("E", res[1179].exec("E"), 3497);
-assertEquals("EF,E", res[1180].exec("EF"), 3498);
-assertEquals("ABCDEFG", res[1181].exec("ABCDEFG"), 3499);
-assertEquals("AB", res[1182].exec("XABYABBBZ"), 3500);
-assertEquals("A", res[1182].exec("XAYABBBZ"), 3501);
-assertEquals("CDE,CD", res[1183].exec("ABCDE"), 3502);
-assertEquals("HIJ", res[1184].exec("HIJ"), 3503);
-assertEquals(null, res[1185].exec("ABCDE", 3504));
-assertEquals("EF,", res[1186].exec("ABCDEF"), 3505);
-assertEquals("BCD,B", res[1187].exec("ABCD"), 3506);
-assertEquals("ABC,A", res[1188].exec("ABC"), 3507);
-assertEquals("ABC,BC", res[1189].exec("ABC"), 3508);
-assertEquals("ABCD,BC,D", res[1190].exec("ABCD"), 3509);
-assertEquals("ABCD,BC,D", res[1191].exec("ABCD"), 3510);
-assertEquals("ABCD,B,CD", res[1192].exec("ABCD"), 3511);
-assertEquals("ADCDCDE", res[1193].exec("ADCDCDE"), 3512);
-assertEquals("ABC,AB", res[1195].exec("ABC"), 3513);
-assertEquals("ABCD,ABC,A,B,D", res[1196].exec("ABCD"), 3514);
-assertEquals("ALPHA", res[1197].exec("ALPHA"), 3515);
-assertEquals("BH,", res[1198].exec("ABH"), 3516);
-assertEquals("EFFGZ,EFFGZ,", res[1199].exec("EFFGZ"), 3517);
-assertEquals("IJ,IJ,J", res[1199].exec("IJ"), 3518);
-assertEquals("EFFGZ,EFFGZ,", res[1199].exec("REFFGZ"), 3519);
-assertEquals(null, res[1199].exec("*** Failers", 3520));
-assertEquals(null, res[1199].exec("ADCDCDE", 3521));
-assertEquals(null, res[1199].exec("EFFG", 3522));
-assertEquals(null, res[1199].exec("BCDD", 3523));
-assertEquals("A,A,A,A,A,A,A,A,A,A,A", res[1200].exec("A"), 3524);
-assertEquals("A,A,A,A,A,A,A,A,A,A", res[1201].exec("A"), 3525);
-assertEquals("A,A", res[1202].exec("A"), 3526);
-assertEquals("C,C", res[1203].exec("C"), 3527);
-assertEquals(null, res[1204].exec("*** Failers", 3528));
-assertEquals(null, res[1204].exec("AA", 3529));
-assertEquals(null, res[1204].exec("UH-UH", 3530));
-assertEquals("MULTIPLE WORDS", res[1205].exec("MULTIPLE WORDS, YEAH"), 3531);
-assertEquals("ABCDE,AB,DE", res[1206].exec("ABCDE"), 3532);
-assertEquals("(A, B),A,B", res[1207].exec("(A, B)"), 3533);
-assertEquals("ABCD", res[1209].exec("ABCD"), 3534);
-assertEquals("ABCD,BC", res[1210].exec("ABCD"), 3535);
-assertEquals("AC", res[1211].exec("AC"), 3536);
-assertEquals("ad", res[1212].exec("abad"), 3537);
-assertEquals("ad", res[1213].exec("abad"), 3538);
-assertEquals("ad", res[1214].exec("abad"), 3539);
-assertEquals("ace,e", res[1215].exec("ace"), 3540);
-assertEquals("ace,e", res[1216].exec("ace"), 3541);
-assertEquals("ace,e", res[1217].exec("ace"), 3542);
-assertEquals("acd,d", res[1217].exec("acdbcdbe"), 3543);
-assertEquals("acdbcdbe,e", res[1218].exec("acdbcdbe"), 3544);
-assertEquals("acdb,b", res[1219].exec("acdbcdbe"), 3545);
-assertEquals("acdbcdb,b", res[1220].exec("acdbcdbe"), 3546);
-assertEquals("acdbcd,d", res[1221].exec("acdbcdbe"), 3547);
-assertEquals("foobar,bar,,bar", res[1222].exec("foobar"), 3548);
-assertEquals("acdbcdbe,e", res[1223].exec("acdbcdbe"), 3549);
-assertEquals("acdbcdbe,e", res[1224].exec("acdbcdbe"), 3550);
-assertEquals("acdbcdbe,e", res[1225].exec("acdbcdbe"), 3551);
-assertEquals("acdbcdb,b", res[1226].exec("acdbcdbe"), 3552);
-assertEquals("acdbcdbe,e", res[1227].exec("acdbcdbe"), 3553);
-assertEquals("acdbcdb,b", res[1228].exec("acdbcdbe"), 3554);
-assertEquals("ace,c,e", res[1229].exec("ace"), 3555);
-assertEquals("AB,A", res[1230].exec("AB"), 3556);
-assertEquals(".,.,", res[1231].exec("."), 3557);
-assertEquals("<&", res[1232].exec("<&OUT"), 3558);
-assertEquals("foobar,,,,b,a,r", res[1233].exec("foobar"), 3559);
-assertEquals(",,,,,,", res[1233].exec("ab"), 3560);
-assertEquals(",,,,,,", res[1233].exec("*** Failers"), 3561);
-assertEquals(",,,,,,", res[1233].exec("cb"), 3562);
-assertEquals(",,,,,,", res[1233].exec("b"), 3563);
-assertEquals(",,,,,,", res[1233].exec("ab"), 3564);
-assertEquals(",,,,,,", res[1233].exec("b"), 3565);
-assertEquals(",,,,,,", res[1233].exec("b"), 3566);
-assertEquals("aba", res[1234].exec("aba"), 3567);
-assertEquals("a", res[1235].exec("aba"), 3568);
-assertEquals(",", res[1236].exec("abc"), 3569);
-assertEquals("aax,a", res[1237].exec("aax"), 3570);
-assertEquals("aax,a,a", res[1238].exec("aax"), 3571);
-assertEquals("aax,a,a", res[1239].exec("aax"), 3572);
-assertEquals("ab,", res[1240].exec("cab"), 3573);
-assertEquals("ab,", res[1241].exec("cab"), 3574);
-assertEquals("ab,", res[1241].exec("ab"), 3575);
-assertEquals("ab,", res[1241].exec("ab"), 3576);
-assertEquals(null, res[1241].exec("Ab", 3577));
-assertEquals(null, res[1241].exec("Ab", 3578));
-assertEquals(null, res[1241].exec("*** Failers", 3579));
-assertEquals(null, res[1241].exec("cb", 3580));
-assertEquals(null, res[1241].exec("aB", 3581));
-assertEquals("ab,", res[1241].exec("ab"), 3582);
-assertEquals("ab,", res[1241].exec("ab"), 3583);
-assertEquals(null, res[1241].exec("Ab", 3584));
-assertEquals(null, res[1241].exec("Ab", 3585));
-assertEquals(null, res[1241].exec("*** Failers", 3586));
-assertEquals(null, res[1241].exec("aB", 3587));
-assertEquals(null, res[1241].exec("aB", 3588));
-assertEquals("ab,", res[1241].exec("ab"), 3589);
-assertEquals("ab,", res[1241].exec("ab"), 3590);
-assertEquals(null, res[1241].exec("aB", 3591));
-assertEquals(null, res[1241].exec("aB", 3592));
-assertEquals(null, res[1241].exec("*** Failers", 3593));
-assertEquals(null, res[1241].exec("aB", 3594));
-assertEquals(null, res[1241].exec("Ab", 3595));
-assertEquals(null, res[1241].exec("aB", 3596));
-assertEquals(null, res[1241].exec("aB", 3597));
-assertEquals(null, res[1241].exec("*** Failers", 3598));
-assertEquals(null, res[1241].exec("Ab", 3599));
-assertEquals(null, res[1241].exec("AB", 3600));
-assertEquals("ab,", res[1241].exec("ab"), 3601);
-assertEquals("ab,", res[1241].exec("ab"), 3602);
-assertEquals(null, res[1241].exec("aB", 3603));
-assertEquals(null, res[1241].exec("aB", 3604));
-assertEquals(null, res[1241].exec("*** Failers", 3605));
-assertEquals(null, res[1241].exec("AB", 3606));
-assertEquals(null, res[1241].exec("Ab", 3607));
-assertEquals(null, res[1241].exec("aB", 3608));
-assertEquals(null, res[1241].exec("aB", 3609));
-assertEquals(null, res[1241].exec("*** Failers", 3610));
-assertEquals(null, res[1241].exec("Ab", 3611));
-assertEquals(null, res[1241].exec("AB", 3612));
-assertEquals(null, res[1241].exec("*** Failers", 3613));
-assertEquals(null, res[1241].exec("AB", 3614));
-assertEquals(null, res[1241].exec("a\nB", 3615));
-assertEquals(null, res[1241].exec("a\nB", 3616));
-assertEquals("cabbbb", res[1242].exec("cabbbb"), 3617);
-assertEquals("caaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", res[1243].exec("caaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), 3618);
-assertEquals("foobar1234baz", res[1244].exec("foobar1234baz"), 3619);
-assertEquals("x~~,~~", res[1245].exec("x~~"), 3620);
-assertEquals("aaac", res[1246].exec("aaac"), 3621);
-assertEquals("aaac", res[1247].exec("aaac"), 3622);
-assertEquals(null, res[1247].exec("*** Failers", 3623));
-assertEquals(null, res[1247].exec("B\nB", 3624));
-assertEquals(null, res[1247].exec("dbcb", 3625));
-assertEquals(null, res[1247].exec("dbaacb", 3626));
-assertEquals(null, res[1247].exec("dbaacb", 3627));
-assertEquals(null, res[1247].exec("cdaccb", 3628));
-assertEquals(null, res[1248].exec("*** Failers", 3629));
-assertEquals(null, res[1248].exec("dbcb", 3630));
-assertEquals(null, res[1248].exec("a--", 3631));
-assertEquals(null, res[1248].exec("a\nb\nc\n", 3632));
-assertEquals(null, res[1248].exec("a\nb\nc\n", 3633));
-assertEquals(null, res[1248].exec("a\nb\n", 3634));
-assertEquals(null, res[1248].exec("a\nb\n", 3635));
-assertEquals(null, res[1248].exec("a\nb\n", 3636));
-assertEquals(null, res[1248].exec("a\nb\n", 3637));
-assertEquals(null, res[1248].exec("a\nb\nc\n", 3638));
-assertEquals(null, res[1248].exec("a\nb\nc\n", 3639));
-assertEquals(null, res[1248].exec("a\nb\nc\n", 3640));
-assertEquals(null, res[1248].exec("a\nb\nc\n", 3641));
-assertEquals(null, res[1250].exec("*** Failers", 3642));
-assertEquals(null, res[1250].exec("a\nb\nc\n", 3643));
-assertEquals(null, res[1250].exec("a\nb\nc\n", 3644));
-assertEquals(null, res[1250].exec("a\nb\nc\n", 3645));
-assertEquals(null, res[1250].exec("a", 3646));
-assertEquals(null, res[1250].exec("*** Failers", 3647));
-assertEquals(null, res[1250].exec("a", 3648));
-assertEquals(null, res[1250].exec("a", 3649));
-assertEquals(null, res[1250].exec("a", 3650));
-assertEquals("one:,one:", res[1251].exec("one:"), 3651);
-assertEquals(null, res[1251].exec("a", 3652));
-assertEquals("abcd,,abcd", res[1252].exec("abcd"), 3653);
-assertEquals("xy:z:::abcd,xy:z:::,abcd", res[1252].exec("xy:z:::abcd"), 3654);
-assertEquals("aexyc,c", res[1253].exec("aexycd"), 3655);
-assertEquals("aab,aa", res[1254].exec("caab"), 3656);
-assertEquals("abcd,,abcd", res[1255].exec("abcd"), 3657);
-assertEquals("xy:z:::abcd,xy:z:::,abcd", res[1255].exec("xy:z:::abcd"), 3658);
-assertEquals("Failers,,Failers", res[1255].exec("*** Failers"), 3659);
-assertEquals(null, res[1255].exec("abcd:", 3660));
-assertEquals(null, res[1255].exec("abcd:", 3661));
-assertEquals("aexyc,c", res[1256].exec("aexycd"), 3662);
-assertEquals(null, res[1257].exec("aaab", 3663));
-assertEquals(":[,:[", res[1258].exec("a:[b]:"), 3664);
-assertEquals("=[,=[", res[1259].exec("a=[b]="), 3665);
-assertEquals(".[,.[", res[1260].exec("a.[b]."), 3666);
-assertEquals(null, res[1260].exec("aaab", 3667));
-assertEquals(null, res[1260].exec("aaab", 3668));
-assertEquals(null, res[1260].exec("((abc(ade)ufh()()x", 3669));
-assertEquals(null, res[1261].exec("*** Failers", 3670));
-assertEquals(null, res[1261].exec("aaab", 3671));
-assertEquals(null, res[1261].exec("a\nb\n", 3672));
-assertEquals(null, res[1262].exec("a\nb\n", 3673));
-assertEquals(null, res[1264].exec("a\nb", 3674));
-assertEquals(null, res[1265].exec("a\nb", 3675));
-assertEquals(null, res[1265].exec("*** Failers", 3676));
-assertEquals(null, res[1265].exec("alphabetabcd", 3677));
-assertEquals(null, res[1265].exec("endingwxyz", 3678));
-assertEquals(null, res[1265].exec("*** Failers", 3679));
-assertEquals(null, res[1265].exec("a rather long string that doesn't end with one of them", 3680));
-assertEquals(null, res[1265].exec("word cat dog elephant mussel cow horse canary baboon snake shark otherword", 3681));
-assertEquals(null, res[1265].exec("word cat dog elephant mussel cow horse canary baboon snake shark", 3682));
-assertEquals(null, res[1265].exec("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope", 3683));
-assertEquals(null, res[1265].exec("999foo", 3684));
-assertEquals(null, res[1265].exec("123999foo ", 3685));
-assertEquals(null, res[1265].exec("*** Failers", 3686));
-assertEquals(null, res[1265].exec("123abcfoo", 3687));
-assertEquals(null, res[1265].exec("999foo", 3688));
-assertEquals(null, res[1265].exec("123999foo ", 3689));
-assertEquals(null, res[1265].exec("*** Failers", 3690));
-assertEquals(null, res[1265].exec("123abcfoo", 3691));
-assertEquals(null, res[1265].exec("123abcfoo", 3692));
-assertEquals(null, res[1265].exec("123456foo ", 3693));
-assertEquals(null, res[1265].exec("*** Failers", 3694));
-assertEquals(null, res[1265].exec("123999foo ", 3695));
-assertEquals(null, res[1265].exec("123abcfoo ", 3696));
-assertEquals(null, res[1265].exec("123456foo ", 3697));
-assertEquals(null, res[1265].exec("*** Failers", 3698));
-assertEquals(null, res[1265].exec("123999foo ", 3699));
-assertEquals("ZA,A,", res[1266].exec("ZABCDEFG"), 3700);
-assertEquals("ZA,A,", res[1267].exec("ZABCDEFG"), 3701);
-assertEquals("ZA,A,,", res[1268].exec("ZABCDEFG"), 3702);
-assertEquals("ZA,A,,", res[1268].exec("ZABCDEFG"), 3703);
-assertEquals("ZA,A,,", res[1268].exec("ZABCDEFG"), 3704);
-assertEquals("a", res[1269].exec("abbab"), 3705);
-assertEquals("", res[1269].exec("abcde"), 3706);
-assertEquals("", res[1269].exec("-things"), 3707);
-assertEquals("", res[1269].exec("0digit"), 3708);
-assertEquals("", res[1269].exec("*** Failers"), 3709);
-assertEquals("", res[1269].exec("bcdef "), 3710);
-assertEquals("a", res[1270].exec("abcde"), 3711);
-assertEquals("-", res[1270].exec("-things"), 3712);
-assertEquals("0", res[1270].exec("0digit"), 3713);
-assertEquals(null, res[1270].exec("*** Failers", 3714));
-assertEquals(null, res[1270].exec("bcdef ", 3715));
-assertEquals(null, res[1271].exec("> \x09\n\x0c\x0d\x0b<", 3716));
-assertEquals(null, res[1271].exec(" ", 3717));
-assertEquals(null, res[1272].exec("> \x09\n\x0c\x0d\x0b<", 3718));
-assertEquals(null, res[1272].exec(" ", 3719));
-assertEquals(" \x09\n\x0c\x0d\x0b", res[1273].exec("> \x09\n\x0c\x0d\x0b<"), 3720);
-assertEquals(" ", res[1273].exec(" "), 3721);
-assertEquals(" \x09\n\x0c\x0d\x0b", res[1274].exec("> \x09\n\x0c\x0d\x0b<"), 3722);
-assertEquals(" ", res[1274].exec(" "), 3723);
-assertEquals(null, res[1275].exec("ab", 3724));
-assertEquals(null, res[1278].exec("abcabcabc", 3725));
-assertEquals(null, res[1278].exec("abc(*+|abc ", 3726));
-assertEquals(null, res[1279].exec("abc abcabc", 3727));
-assertEquals(null, res[1279].exec("*** Failers", 3728));
-assertEquals(null, res[1279].exec("abcabcabc ", 3729));
-assertEquals(null, res[1280].exec("abc#not comment\n literal ", 3730));
-assertEquals(null, res[1281].exec("abc#not comment\n literal ", 3731));
-assertEquals(null, res[1282].exec("abc#not comment\n literal ", 3732));
-assertEquals(null, res[1283].exec("abc#not comment\n literal ", 3733));
-assertEquals(null, res[1284].exec("abc\\$xyz", 3734));
-assertEquals(null, res[1285].exec("abc$xyz", 3735));
-assertEquals(null, res[1286].exec("abc", 3736));
-assertEquals(null, res[1286].exec("*** Failers", 3737));
-assertEquals(null, res[1286].exec("xyzabc ", 3738));
-assertEquals(null, res[1287].exec("abc1abc2xyzabc3", 3739));
-assertEquals("abc1", res[1288].exec("abc1abc2xyzabc3 "), 3740);
-assertEquals(null, res[1288].exec("XabcdY", 3741));
-assertEquals(null, res[1288].exec("*** Failers ", 3742));
-assertEquals(null, res[1288].exec("Xa b c d Y ", 3743));
-assertEquals("abcY", res[1288].exec("XabcY"), 3744);
-assertEquals(null, res[1288].exec("AxyzB ", 3745));
-assertEquals(null, res[1288].exec("XabCY", 3746));
-assertEquals(null, res[1288].exec("*** Failers", 3747));
-assertEquals("abcY", res[1288].exec("XabcY "), 3748);
-assertEquals(null, res[1288].exec("abCE", 3749));
-assertEquals(null, res[1288].exec("DE", 3750));
-assertEquals(null, res[1288].exec("*** Failers", 3751));
-assertEquals("abcE", res[1288].exec("abcE"), 3752);
-assertEquals(null, res[1288].exec("abCe ", 3753));
-assertEquals(null, res[1288].exec("dE", 3754));
-assertEquals(null, res[1288].exec("De ", 3755));
-assertEquals(null, res[1289].exec("z", 3756));
-assertEquals(null, res[1289].exec("a", 3757));
-assertEquals(null, res[1289].exec("-", 3758));
-assertEquals(null, res[1289].exec("d", 3759));
-assertEquals(null, res[1289].exec("] ", 3760));
-assertEquals(null, res[1289].exec("*** Failers", 3761));
-assertEquals(null, res[1289].exec("b ", 3762));
-assertEquals("z", res[1290].exec("z"), 3763);
-assertEquals("C", res[1290].exec("C "), 3764);
-assertEquals("M", res[1291].exec("M "), 3765);
-assertEquals(null, res[1292].exec("", 3766));
-assertEquals(null, res[1292].exec("REGular", 3767));
-assertEquals(null, res[1292].exec("regulaer", 3768));
-assertEquals(null, res[1292].exec("Regex ", 3769));
-assertEquals(null, res[1292].exec("regul\ufffdr ", 3770));
-assertEquals(null, res[1292].exec("\ufffd\ufffd\ufffd\ufffd\ufffd", 3771));
-assertEquals(null, res[1292].exec("\ufffd\ufffd\ufffd\ufffd\ufffd", 3772));
-assertEquals(null, res[1292].exec("\ufffd\ufffd\ufffd\ufffd\ufffd", 3773));
-assertEquals(null, res[1292].exec("\ufffd\ufffd\ufffd\ufffd\ufffd", 3774));
-assertEquals(null, res[1292].exec("\x84XAZXB", 3775));
-assertEquals(null, res[1292].exec("123a", 3776));
-assertEquals(null, res[1292].exec("ac", 3777));
-assertEquals("b,", res[1292].exec("bbbbc"), 3778);
-assertEquals("ab,a", res[1292].exec("abc"), 3779);
-assertEquals(null, res[1292].exec("*** Failers", 3780));
-assertEquals("b,", res[1292].exec("bca"), 3781);
-assertEquals(null, res[1292].exec("", 3782));
-assertEquals("ab,a", res[1292].exec("abc"), 3783);
-assertEquals(null, res[1292].exec("*** Failers", 3784));
-assertEquals("b,", res[1292].exec("bca"), 3785);
-assertEquals("ab,a", res[1292].exec("abc"), 3786);
-assertEquals(null, res[1292].exec("*** Failers", 3787));
-assertEquals(null, res[1292].exec("def ", 3788));
-assertEquals(null, res[1292].exec("", 3789));
-assertEquals("ab,a", res[1292].exec("abc"), 3790);
-assertEquals(null, res[1292].exec("*** Failers", 3791));
-assertEquals(null, res[1292].exec("def ", 3792));
-assertEquals(null, res[1292].exec("", 3793));
-assertEquals("line\nbreak", res[1293].exec("this is a line\nbreak"), 3794);
-assertEquals("line\nbreak", res[1293].exec("line one\nthis is a line\nbreak in the second line "), 3795);
-assertEquals("line\nbreak", res[1294].exec("this is a line\nbreak"), 3796);
-assertEquals(null, res[1294].exec("** Failers ", 3797));
-assertEquals("line\nbreak", res[1294].exec("line one\nthis is a line\nbreak in the second line "), 3798);
-assertEquals("line\nbreak", res[1295].exec("this is a line\nbreak"), 3799);
-assertEquals(null, res[1295].exec("** Failers ", 3800));
-assertEquals("line\nbreak", res[1295].exec("line one\nthis is a line\nbreak in the second line "), 3801);
-assertEquals(null, res[1296].exec("123P", 3802));
-assertEquals(null, res[1296].exec("a4PR", 3803));
-assertEquals(null, res[1297].exec("123P", 3804));
-assertEquals(null, res[1297].exec("4PR", 3805));
-assertEquals("", res[1298].exec("a\nb\nc\n"), 3806);
-assertEquals("", res[1298].exec(" "), 3807);
-assertEquals("", res[1298].exec("A\nC\nC\n "), 3808);
-assertEquals("", res[1298].exec("AB"), 3809);
-assertEquals("", res[1298].exec("aB "), 3810);
-assertEquals("", res[1298].exec("AB"), 3811);
-assertEquals("", res[1298].exec("aB "), 3812);
-assertEquals("", res[1298].exec("AB"), 3813);
-assertEquals("", res[1298].exec("aB "), 3814);
-assertEquals("", res[1298].exec("AB"), 3815);
-assertEquals("", res[1298].exec("aB "), 3816);
-assertEquals("Content-Type:xxxxxyyy ", res[1299].exec("Content-Type:xxxxxyyy "), 3817);
-assertEquals("Content-Type:xxxxxyyyz", res[1300].exec("Content-Type:xxxxxyyyz"), 3818);
-assertEquals("Content-Type:xxxyyy ", res[1301].exec("Content-Type:xxxyyy "), 3819);
-assertEquals("Content-Type:xxxyyyz", res[1302].exec("Content-Type:xxxyyyz"), 3820);
-assertEquals("abc", res[1303].exec("xyz\nabc"), 3821);
-assertEquals("abc", res[1303].exec("xyz\nabc<lf>"), 3822);
-assertEquals("abc", res[1303].exec("xyz\x0d\nabc<lf>"), 3823);
-assertEquals("abc", res[1303].exec("xyz\x0dabc<cr>"), 3824);
-assertEquals("abc", res[1303].exec("xyz\x0d\nabc<crlf>"), 3825);
-assertEquals(null, res[1303].exec("** Failers ", 3826));
-assertEquals("abc", res[1303].exec("xyz\nabc<cr>"), 3827);
-assertEquals("abc", res[1303].exec("xyz\x0d\nabc<cr>"), 3828);
-assertEquals("abc", res[1303].exec("xyz\nabc<crlf>"), 3829);
-assertEquals("abc", res[1303].exec("xyz\x0dabc<crlf>"), 3830);
-assertEquals("abc", res[1303].exec("xyz\x0dabc<lf>"), 3831);
-assertEquals("abc", res[1304].exec("xyzabc"), 3832);
-assertEquals("abc", res[1304].exec("xyzabc\n "), 3833);
-assertEquals("abc", res[1304].exec("xyzabc\npqr "), 3834);
-assertEquals("abc", res[1304].exec("xyzabc\x0d<cr> "), 3835);
-assertEquals("abc", res[1304].exec("xyzabc\x0dpqr<cr> "), 3836);
-assertEquals("abc", res[1304].exec("xyzabc\x0d\n<crlf> "), 3837);
-assertEquals("abc", res[1304].exec("xyzabc\x0d\npqr<crlf> "), 3838);
-assertEquals(null, res[1304].exec("** Failers", 3839));
-assertEquals("abc", res[1304].exec("xyzabc\x0d "), 3840);
-assertEquals("abc", res[1304].exec("xyzabc\x0dpqr "), 3841);
-assertEquals("abc", res[1304].exec("xyzabc\x0d\n "), 3842);
-assertEquals("abc", res[1304].exec("xyzabc\x0d\npqr "), 3843);
-assertEquals("abc", res[1305].exec("xyz\x0dabcdef"), 3844);
-assertEquals("abc", res[1305].exec("xyz\nabcdef<lf>"), 3845);
-assertEquals(null, res[1305].exec("** Failers ", 3846));
-assertEquals("abc", res[1305].exec("xyz\nabcdef"), 3847);
-assertEquals(null, res[1305].exec(" ", 3848));
-assertEquals("abc", res[1306].exec("xyz\nabcdef"), 3849);
-assertEquals("abc", res[1306].exec("xyz\x0dabcdef<cr>"), 3850);
-assertEquals(null, res[1306].exec("** Failers ", 3851));
-assertEquals("abc", res[1306].exec("xyz\x0dabcdef"), 3852);
-assertEquals(null, res[1306].exec(" ", 3853));
-assertEquals("abc", res[1307].exec("xyz\x0d\nabcdef"), 3854);
-assertEquals("abc", res[1307].exec("xyz\x0dabcdef<cr>"), 3855);
-assertEquals(null, res[1307].exec("** Failers ", 3856));
-assertEquals("abc", res[1307].exec("xyz\x0dabcdef"), 3857);
-assertEquals("abc", res[1308].exec("abc\ndef"), 3858);
-assertEquals("abc", res[1308].exec("abc\x0ddef"), 3859);
-assertEquals("abc", res[1308].exec("abc\x0d\ndef"), 3860);
-assertEquals("<cr>abc", res[1308].exec("<cr>abc\ndef"), 3861);
-assertEquals("<cr>abc", res[1308].exec("<cr>abc\x0ddef"), 3862);
-assertEquals("<cr>abc", res[1308].exec("<cr>abc\x0d\ndef"), 3863);
-assertEquals("<crlf>abc", res[1308].exec("<crlf>abc\ndef"), 3864);
-assertEquals("<crlf>abc", res[1308].exec("<crlf>abc\x0ddef"), 3865);
-assertEquals("<crlf>abc", res[1308].exec("<crlf>abc\x0d\ndef"), 3866);
-assertEquals(null, res[1309].exec("abc\ndef", 3867));
-assertEquals(null, res[1309].exec("abc\x0ddef", 3868));
-assertEquals(null, res[1309].exec("abc\x0d\ndef", 3869));
-assertEquals("abc=xyz\\,", res[1310].exec("abc=xyz\\\npqr"), 3870);
-assertEquals("aaaa,a,", res[1311].exec("aaaa"), 3871);
-assertEquals("aaaa", res[1312].exec("aaaa"), 3872);
-assertEquals("aaaa,a,", res[1313].exec("aaaa"), 3873);
-assertEquals("aaaa", res[1314].exec("aaaa"), 3874);
-assertEquals(null, res[1317].exec("a\x0db", 3875));
-assertEquals(null, res[1317].exec("a\nb<cr> ", 3876));
-assertEquals(null, res[1317].exec("** Failers", 3877));
-assertEquals(null, res[1317].exec("a\nb", 3878));
-assertEquals(null, res[1317].exec("a\nb<any>", 3879));
-assertEquals(null, res[1317].exec("a\x0db<cr> ", 3880));
-assertEquals(null, res[1317].exec("a\x0db<any> ", 3881));
-assertEquals("abc1", res[1318].exec("abc1 \nabc2 \x0babc3xx \x0cabc4 \x0dabc5xx \x0d\nabc6 \x85abc7 JUNK"), 3882);
-assertEquals("abc1", res[1319].exec("abc1\n abc2\x0b abc3\x0c abc4\x0d abc5\x0d\n abc6\x85 abc9"), 3883);
-assertEquals(null, res[1320].exec("a\nb", 3884));
-assertEquals(null, res[1320].exec("a\x0db", 3885));
-assertEquals(null, res[1320].exec("a\x0d\nb", 3886));
-assertEquals(null, res[1320].exec("a\x0bb", 3887));
-assertEquals(null, res[1320].exec("a\x0cb", 3888));
-assertEquals(null, res[1320].exec("a\x85b ", 3889));
-assertEquals(null, res[1320].exec("** Failers", 3890));
-assertEquals(null, res[1320].exec("a\n\x0db ", 3891));
-assertEquals("ab", res[1321].exec("ab"), 3892);
-assertEquals(null, res[1321].exec("a\nb", 3893));
-assertEquals(null, res[1321].exec("a\x0db", 3894));
-assertEquals(null, res[1321].exec("a\x0d\nb", 3895));
-assertEquals(null, res[1321].exec("a\x0bb", 3896));
-assertEquals(null, res[1321].exec("a\x0cb", 3897));
-assertEquals(null, res[1321].exec("a\x85b ", 3898));
-assertEquals(null, res[1321].exec("a\n\x0db ", 3899));
-assertEquals(null, res[1321].exec("a\n\x0d\x85\x0cb ", 3900));
-assertEquals(null, res[1322].exec("a\nb", 3901));
-assertEquals(null, res[1322].exec("a\x0db", 3902));
-assertEquals(null, res[1322].exec("a\x0d\nb", 3903));
-assertEquals(null, res[1322].exec("a\x0bb", 3904));
-assertEquals(null, res[1322].exec("a\x0cb", 3905));
-assertEquals(null, res[1322].exec("a\x85b ", 3906));
-assertEquals(null, res[1322].exec("a\n\x0db ", 3907));
-assertEquals(null, res[1322].exec("a\n\x0d\x85\x0cb ", 3908));
-assertEquals(null, res[1322].exec("** Failers", 3909));
-assertEquals(null, res[1322].exec("ab ", 3910));
-assertEquals(null, res[1323].exec("a\nb", 3911));
-assertEquals(null, res[1323].exec("a\n\x0db", 3912));
-assertEquals(null, res[1323].exec("a\n\x0d\x85b", 3913));
-assertEquals(null, res[1323].exec("a\x0d\n\x0d\nb ", 3914));
-assertEquals(null, res[1323].exec("a\x0d\n\x0d\n\x0d\nb ", 3915));
-assertEquals(null, res[1323].exec("a\n\x0d\n\x0db", 3916));
-assertEquals(null, res[1323].exec("a\n\n\x0d\nb ", 3917));
-assertEquals(null, res[1323].exec("** Failers", 3918));
-assertEquals(null, res[1323].exec("a\n\n\n\x0db", 3919));
-assertEquals(null, res[1323].exec("a\x0d", 3920));
-assertEquals("aRb", res[1324].exec("aRb"), 3921);
-assertEquals(null, res[1324].exec("** Failers", 3922));
-assertEquals(null, res[1324].exec("a\nb ", 3923));
-assertEquals("afoo", res[1325].exec("afoo"), 3924);
-assertEquals(null, res[1325].exec("** Failers ", 3925));
-assertEquals(null, res[1325].exec("\x0d\nfoo ", 3926));
-assertEquals(null, res[1325].exec("\nfoo ", 3927));
-assertEquals("afoo", res[1326].exec("afoo"), 3928);
-assertEquals(null, res[1326].exec("\nfoo ", 3929));
-assertEquals(null, res[1326].exec("** Failers ", 3930));
-assertEquals(null, res[1326].exec("\x0d\nfoo ", 3931));
-assertEquals("afoo", res[1327].exec("afoo"), 3932);
-assertEquals(null, res[1327].exec("** Failers ", 3933));
-assertEquals(null, res[1327].exec("\nfoo ", 3934));
-assertEquals(null, res[1327].exec("\x0d\nfoo ", 3935));
-assertEquals("afoo", res[1328].exec("afoo"), 3936);
-assertEquals(null, res[1328].exec("\x0d\nfoo ", 3937));
-assertEquals(null, res[1328].exec("\nfoo ", 3938));
-assertEquals("", res[1329].exec("abc\x0d\x0dxyz"), 3939);
-assertEquals("", res[1329].exec("abc\n\x0dxyz "), 3940);
-assertEquals(null, res[1329].exec("** Failers ", 3941));
-assertEquals("", res[1329].exec("abc\x0d\nxyz"), 3942);
-assertEquals("X", res[1330].exec("XABC"), 3943);
-assertEquals(null, res[1330].exec("** Failers ", 3944));
-assertEquals("X", res[1330].exec("XABCB"), 3945);
-assertEquals(null, res[1330].exec("abc\x0d\n\x0d\n", 3946));
-assertEquals(null, res[1330].exec("abc\x0d\n\x0d\n", 3947));
-assertEquals(null, res[1330].exec("abc\x0d\n\x0d\n", 3948));
+assertToStringEquals("", res[986].exec("abc"), 2982);
+assertToStringEquals("acb", res[988].exec("acb"), 2983);
+assertToStringEquals("a\nb", res[988].exec("a\nb"), 2984);
+assertToStringEquals("acb", res[989].exec("acb"), 2985);
+assertNull(res[989].exec("*** Failers ", 2986));
+assertNull(res[989].exec("a\nb ", 2987));
+assertToStringEquals("acb", res[990].exec("acb"), 2988);
+assertToStringEquals("a\nb", res[990].exec("a\nb "), 2989);
+assertToStringEquals("acb", res[991].exec("acb"), 2990);
+assertNull(res[991].exec("a\nb ", 2991));
+assertToStringEquals("bac,a", res[992].exec("bac"), 2992);
+assertToStringEquals("bbac,a", res[992].exec("bbac"), 2993);
+assertToStringEquals("bbbac,a", res[992].exec("bbbac"), 2994);
+assertToStringEquals("bbbbac,a", res[992].exec("bbbbac"), 2995);
+assertToStringEquals("bbbbbac,a", res[992].exec("bbbbbac "), 2996);
+assertToStringEquals("bac,a", res[993].exec("bac"), 2997);
+assertToStringEquals("bbac,a", res[993].exec("bbac"), 2998);
+assertToStringEquals("bbbac,a", res[993].exec("bbbac"), 2999);
+assertToStringEquals("bbbbac,a", res[993].exec("bbbbac"), 3000);
+assertToStringEquals("bbbbbac,a", res[993].exec("bbbbbac "), 3001);
+assertToStringEquals("x", res[994].exec("x\nb\n"), 3002);
+assertToStringEquals("x", res[994].exec("a\x08x\n "), 3003);
+assertNull(res[995].exec("\x00{ab} ", 3004));
+assertToStringEquals("CD,", res[996].exec("CD "), 3005);
+assertToStringEquals("CD,", res[997].exec("CD "), 3006);
+assertNull(res[997].exec("foo", 3007));
+assertNull(res[997].exec("catfood", 3008));
+assertNull(res[997].exec("arfootle", 3009));
+assertNull(res[997].exec("rfoosh", 3010));
+assertNull(res[997].exec("*** Failers", 3011));
+assertNull(res[997].exec("barfoo", 3012));
+assertNull(res[997].exec("towbarfoo", 3013));
+assertNull(res[997].exec("catfood", 3014));
+assertNull(res[997].exec("*** Failers", 3015));
+assertNull(res[997].exec("foo", 3016));
+assertNull(res[997].exec("barfoo", 3017));
+assertNull(res[997].exec("towbarfoo", 3018));
+assertNull(res[997].exec("fooabar", 3019));
+assertNull(res[997].exec("*** Failers", 3020));
+assertNull(res[997].exec("bar", 3021));
+assertNull(res[997].exec("foobbar", 3022));
+assertNull(res[997].exec(" ", 3023));
+assertNull(res[998].exec("abc", 3024));
+assertNull(res[998].exec("*** Failers", 3025));
+assertNull(res[998].exec("abc\n ", 3026));
+assertNull(res[998].exec("qqq\nabc", 3027));
+assertNull(res[998].exec("abc\nzzz", 3028));
+assertNull(res[998].exec("qqq\nabc\nzzz", 3029));
+assertNull(res[998].exec("/this/is/a/very/long/line/in/deed/with/very/many/slashes/in/it/you/see/", 3030));
+assertNull(res[998].exec("/this/is/a/very/long/line/in/deed/with/very/many/slashes/in/and/foo", 3031));
+assertNull(res[998].exec("1.230003938", 3032));
+assertNull(res[998].exec("1.875000282", 3033));
+assertNull(res[998].exec("*** Failers ", 3034));
+assertNull(res[998].exec("1.235 ", 3035));
+assertNull(res[998].exec("now is the time for all good men to come to the aid of the party", 3036));
+assertNull(res[998].exec("*** Failers", 3037));
+assertNull(res[998].exec("this is not a line with only words and spaces!", 3038));
+assertToStringEquals("12345a,12345,a", res[999].exec("12345a"), 3039);
+assertToStringEquals("12345,1234,5", res[999].exec("12345+ "), 3040);
+assertToStringEquals("12345a,12345,a", res[999].exec("12345a"), 3041);
+assertNull(res[999].exec("*** Failers", 3042));
+assertToStringEquals("12345,1234,5", res[999].exec("12345+ "), 3043);
+assertNull(res[999].exec("aaab", 3044));
+assertNull(res[999].exec("aaab", 3045));
+assertNull(res[999].exec("aaab", 3046));
+assertNull(res[999].exec("aaabbbccc", 3047));
+assertNull(res[999].exec("aaabbbbccccd", 3048));
+assertToStringEquals("aaabbbbcccc,ccc", res[1000].exec("aaabbbbccccd"), 3049);
+assertToStringEquals("abc,b", res[1000].exec("((abc(ade)ufh()()x"), 3050);
+assertNull(res[1000].exec("", 3051));
+assertToStringEquals("abc,b", res[1000].exec("(abc)"), 3052);
+assertToStringEquals("abc,b", res[1000].exec("(abc(def)xyz)"), 3053);
+assertNull(res[1000].exec("*** Failers", 3054));
+assertNull(res[1000].exec("ab", 3055));
+assertNull(res[1000].exec("Ab", 3056));
+assertNull(res[1000].exec("*** Failers ", 3057));
+assertNull(res[1000].exec("aB", 3058));
+assertNull(res[1000].exec("AB", 3059));
+assertNull(res[1000].exec(" ", 3060));
+assertToStringEquals("bc,b", res[1000].exec("a bcd e"), 3061);
+assertNull(res[1000].exec("*** Failers", 3062));
+assertToStringEquals("c,", res[1000].exec("a b cd e"), 3063);
+assertToStringEquals("abc,b", res[1000].exec("abcd e "), 3064);
+assertToStringEquals("bc,b", res[1000].exec("a bcde "), 3065);
+assertToStringEquals("bc,b", res[1000].exec("a bcde f"), 3066);
+assertNull(res[1000].exec("*** Failers", 3067));
+assertToStringEquals("abc,b", res[1000].exec("abcdef "), 3068);
+assertToStringEquals("abc,b", res[1000].exec("abc"), 3069);
+assertToStringEquals("c,", res[1000].exec("aBc"), 3070);
+assertNull(res[1000].exec("*** Failers", 3071));
+assertNull(res[1000].exec("abC", 3072));
+assertNull(res[1000].exec("aBC ", 3073));
+assertToStringEquals("bc,b", res[1000].exec("Abc"), 3074);
+assertToStringEquals("c,", res[1000].exec("ABc"), 3075);
+assertNull(res[1000].exec("ABC", 3076));
+assertNull(res[1000].exec("AbC", 3077));
+assertNull(res[1000].exec("", 3078));
+assertToStringEquals("abc,b", res[1000].exec("abc"), 3079);
+assertToStringEquals("c,", res[1000].exec("aBc"), 3080);
+assertNull(res[1000].exec("*** Failers ", 3081));
+assertNull(res[1000].exec("ABC", 3082));
+assertNull(res[1000].exec("abC", 3083));
+assertNull(res[1000].exec("aBC", 3084));
+assertNull(res[1000].exec("", 3085));
+assertToStringEquals("c,", res[1000].exec("aBc"), 3086);
+assertToStringEquals("c,", res[1000].exec("aBBc"), 3087);
+assertNull(res[1000].exec("*** Failers ", 3088));
+assertNull(res[1000].exec("aBC", 3089));
+assertNull(res[1000].exec("aBBC", 3090));
+assertNull(res[1000].exec("", 3091));
+assertToStringEquals("abc,b", res[1000].exec("abcd"), 3092);
+assertNull(res[1000].exec("abCd", 3093));
+assertNull(res[1000].exec("*** Failers", 3094));
+assertNull(res[1000].exec("aBCd", 3095));
+assertToStringEquals("abc,b", res[1000].exec("abcD "), 3096);
+assertNull(res[1000].exec("", 3097));
+assertNull(res[1000].exec("more than million", 3098));
+assertNull(res[1000].exec("more than MILLION", 3099));
+assertNull(res[1000].exec("more \n than Million ", 3100));
+assertNull(res[1000].exec("*** Failers", 3101));
+assertNull(res[1000].exec("MORE THAN MILLION ", 3102));
+assertNull(res[1000].exec("more \n than \n million ", 3103));
+assertNull(res[1000].exec("more than million", 3104));
+assertNull(res[1000].exec("more than MILLION", 3105));
+assertNull(res[1000].exec("more \n than Million ", 3106));
+assertNull(res[1000].exec("*** Failers", 3107));
+assertNull(res[1000].exec("MORE THAN MILLION ", 3108));
+assertNull(res[1000].exec("more \n than \n million ", 3109));
+assertNull(res[1000].exec("", 3110));
+assertToStringEquals("abc,b", res[1000].exec("abc"), 3111);
+assertToStringEquals("bc,b", res[1000].exec("aBbc"), 3112);
+assertToStringEquals("c,", res[1000].exec("aBBc "), 3113);
+assertNull(res[1000].exec("*** Failers", 3114));
+assertToStringEquals("bc,b", res[1000].exec("Abc"), 3115);
+assertNull(res[1000].exec("abAb ", 3116));
+assertNull(res[1000].exec("abbC ", 3117));
+assertNull(res[1000].exec("", 3118));
+assertToStringEquals("abc,b", res[1000].exec("abc"), 3119);
+assertToStringEquals("c,", res[1000].exec("aBc"), 3120);
+assertNull(res[1000].exec("*** Failers", 3121));
+assertNull(res[1000].exec("Ab ", 3122));
+assertNull(res[1000].exec("abC", 3123));
+assertNull(res[1000].exec("aBC ", 3124));
+assertNull(res[1000].exec("", 3125));
+assertToStringEquals("c,", res[1000].exec("abxxc"), 3126);
+assertToStringEquals("c,", res[1000].exec("aBxxc"), 3127);
+assertNull(res[1000].exec("*** Failers", 3128));
+assertToStringEquals("c,", res[1000].exec("Abxxc"), 3129);
+assertToStringEquals("c,", res[1000].exec("ABxxc"), 3130);
+assertNull(res[1000].exec("abxxC ", 3131));
+assertToStringEquals("abc,b", res[1000].exec("abc:"), 3132);
+assertNull(res[1000].exec("12", 3133));
+assertNull(res[1000].exec("*** Failers", 3134));
+assertNull(res[1000].exec("123", 3135));
+assertNull(res[1000].exec("xyz ", 3136));
+assertToStringEquals("abc,b", res[1000].exec("abc:"), 3137);
+assertNull(res[1000].exec("12", 3138));
+assertNull(res[1000].exec("*** Failers", 3139));
+assertNull(res[1000].exec("123", 3140));
+assertNull(res[1000].exec("xyz ", 3141));
+assertNull(res[1000].exec("", 3142));
+assertNull(res[1000].exec("foobar", 3143));
+assertToStringEquals("c,", res[1000].exec("cat"), 3144);
+assertToStringEquals("c,", res[1000].exec("fcat"), 3145);
+assertToStringEquals("c,", res[1000].exec("focat "), 3146);
+assertNull(res[1000].exec("*** Failers", 3147));
+assertToStringEquals("c,", res[1000].exec("foocat "), 3148);
+assertNull(res[1000].exec("foobar", 3149));
+assertToStringEquals("c,", res[1000].exec("cat"), 3150);
+assertToStringEquals("c,", res[1000].exec("fcat"), 3151);
+assertToStringEquals("c,", res[1000].exec("focat "), 3152);
+assertNull(res[1000].exec("*** Failers", 3153));
+assertToStringEquals("c,", res[1000].exec("foocat "), 3154);
+assertNull(res[1000].exec("a", 3155));
+assertNull(res[1000].exec("aa", 3156));
+assertNull(res[1000].exec("aaaa", 3157));
+assertNull(res[1000].exec("", 3158));
+assertToStringEquals("abc,abc", res[1001].exec("abc"), 3159);
+assertToStringEquals("abcabc,abc", res[1001].exec("abcabc"), 3160);
+assertToStringEquals("abcabcabc,abc", res[1001].exec("abcabcabc"), 3161);
+assertToStringEquals(",", res[1001].exec("xyz "), 3162);
+assertToStringEquals("a,a", res[1002].exec("a"), 3163);
+assertToStringEquals("aaaaa,aaaaa", res[1002].exec("aaaaa "), 3164);
+assertToStringEquals("a,a", res[1003].exec("a"), 3165);
+assertToStringEquals("b,b", res[1003].exec("b"), 3166);
+assertToStringEquals("ababab,ababab", res[1003].exec("ababab"), 3167);
+assertToStringEquals("aaaab,aaaab", res[1003].exec("aaaabcde"), 3168);
+assertToStringEquals("bbbb,bbbb", res[1003].exec("bbbb "), 3169);
+assertToStringEquals("b,b", res[1004].exec("b"), 3170);
+assertToStringEquals("bbbb,bbbb", res[1004].exec("bbbb"), 3171);
+assertToStringEquals(",", res[1004].exec("aaa "), 3172);
+assertToStringEquals("cccc,cccc", res[1005].exec("cccc"), 3173);
+assertToStringEquals(",", res[1005].exec("abab "), 3174);
+assertToStringEquals("a,a", res[1006].exec("a"), 3175);
+assertToStringEquals("aaaa,a", res[1006].exec("aaaa "), 3176);
+assertToStringEquals("a,a", res[1007].exec("a"), 3177);
+assertToStringEquals("b,b", res[1007].exec("b"), 3178);
+assertToStringEquals("abab,b", res[1007].exec("abab"), 3179);
+assertToStringEquals("baba,a", res[1007].exec("baba "), 3180);
+assertToStringEquals("b,b", res[1008].exec("b"), 3181);
+assertToStringEquals("bbbb,b", res[1008].exec("bbbb"), 3182);
+assertToStringEquals(",", res[1008].exec("aaa "), 3183);
+assertToStringEquals("c,c", res[1009].exec("c"), 3184);
+assertToStringEquals("cccc,c", res[1009].exec("cccc"), 3185);
+assertToStringEquals(",", res[1009].exec("baba "), 3186);
+assertToStringEquals(",", res[1009].exec("a"), 3187);
+assertToStringEquals(",", res[1009].exec("aaabcde "), 3188);
+assertToStringEquals(",", res[1009].exec("aaaaa"), 3189);
+assertToStringEquals(",", res[1009].exec("aabbaa "), 3190);
+assertToStringEquals(",", res[1009].exec("aaaaa"), 3191);
+assertToStringEquals(",", res[1009].exec("aabbaa "), 3192);
+assertToStringEquals("12-sep-98,8", res[1009].exec("12-sep-98"), 3193);
+assertToStringEquals("12-09-98,8", res[1009].exec("12-09-98"), 3194);
+assertToStringEquals("*** F,F", res[1009].exec("*** Failers"), 3195);
+assertToStringEquals("sep-12-98,8", res[1009].exec("sep-12-98"), 3196);
+assertToStringEquals(" , ", res[1009].exec(" "), 3197);
+assertToStringEquals("s,s", res[1009].exec("saturday"), 3198);
+assertToStringEquals("sund,d", res[1009].exec("sunday"), 3199);
+assertToStringEquals("S,S", res[1009].exec("Saturday"), 3200);
+assertToStringEquals("Sund,d", res[1009].exec("Sunday"), 3201);
+assertToStringEquals("SATURDAY,Y", res[1009].exec("SATURDAY"), 3202);
+assertToStringEquals("SUNDAY,Y", res[1009].exec("SUNDAY"), 3203);
+assertToStringEquals("SunD,D", res[1009].exec("SunDay"), 3204);
+assertToStringEquals(",", res[1009].exec("abcx"), 3205);
+assertToStringEquals(",", res[1009].exec("aBCx"), 3206);
+assertToStringEquals(",", res[1009].exec("bbx"), 3207);
+assertToStringEquals("BBx,x", res[1009].exec("BBx"), 3208);
+assertToStringEquals("*** F,F", res[1009].exec("*** Failers"), 3209);
+assertToStringEquals(",", res[1009].exec("abcX"), 3210);
+assertToStringEquals(",", res[1009].exec("aBCX"), 3211);
+assertToStringEquals(",", res[1009].exec("bbX"), 3212);
+assertToStringEquals("BBX , ", res[1009].exec("BBX "), 3213);
+assertToStringEquals(",", res[1009].exec("ac"), 3214);
+assertToStringEquals(",", res[1009].exec("aC"), 3215);
+assertToStringEquals(",", res[1009].exec("bD"), 3216);
+assertToStringEquals("eleph,h", res[1009].exec("elephant"), 3217);
+assertToStringEquals("Europe , ", res[1009].exec("Europe "), 3218);
+assertToStringEquals("frog,g", res[1009].exec("frog"), 3219);
+assertToStringEquals("Fr,r", res[1009].exec("France"), 3220);
+assertToStringEquals("*** F,F", res[1009].exec("*** Failers"), 3221);
+assertToStringEquals("Afric,c", res[1009].exec("Africa "), 3222);
+assertToStringEquals(",", res[1009].exec("ab"), 3223);
+assertToStringEquals(",", res[1009].exec("aBd"), 3224);
+assertToStringEquals("xy,y", res[1009].exec("xy"), 3225);
+assertToStringEquals("xY,Y", res[1009].exec("xY"), 3226);
+assertToStringEquals("ze,e", res[1009].exec("zebra"), 3227);
+assertToStringEquals("Z,Z", res[1009].exec("Zambesi"), 3228);
+assertToStringEquals("*** F,F", res[1009].exec("*** Failers"), 3229);
+assertToStringEquals(",", res[1009].exec("aCD "), 3230);
+assertToStringEquals("XY , ", res[1009].exec("XY "), 3231);
+assertToStringEquals("foo\n,\n", res[1009].exec("foo\nbar"), 3232);
+assertToStringEquals("*** F,F", res[1009].exec("*** Failers"), 3233);
+assertToStringEquals(",", res[1009].exec("bar"), 3234);
+assertToStringEquals(",", res[1009].exec("baz\nbar "), 3235);
+assertToStringEquals(",", res[1009].exec("barbaz"), 3236);
+assertToStringEquals(",", res[1009].exec("barbarbaz "), 3237);
+assertToStringEquals("koo,o", res[1009].exec("koobarbaz "), 3238);
+assertToStringEquals("*** F,F", res[1009].exec("*** Failers"), 3239);
+assertToStringEquals(",", res[1009].exec("baz"), 3240);
+assertToStringEquals("foo,o", res[1009].exec("foobarbaz "), 3241);
+assertToStringEquals("abc", res[1012].exec("abc"), 3242);
+assertToStringEquals("abc", res[1012].exec("xabcy"), 3243);
+assertToStringEquals("abc", res[1012].exec("ababc"), 3244);
+assertNull(res[1012].exec("*** Failers", 3245));
+assertNull(res[1012].exec("xbc", 3246));
+assertNull(res[1012].exec("axc", 3247));
+assertNull(res[1012].exec("abx", 3248));
+assertToStringEquals("abc", res[1013].exec("abc"), 3249);
+assertToStringEquals("abc", res[1014].exec("abc"), 3250);
+assertToStringEquals("abbc", res[1014].exec("abbc"), 3251);
+assertToStringEquals("abbbbc", res[1014].exec("abbbbc"), 3252);
+assertToStringEquals("a", res[1015].exec("abbbbc"), 3253);
+assertToStringEquals("abbb", res[1016].exec("abbbbc"), 3254);
+assertToStringEquals("abbbbc", res[1017].exec("abbbbc"), 3255);
+assertToStringEquals("abbc", res[1018].exec("abbc"), 3256);
+assertNull(res[1018].exec("*** Failers", 3257));
+assertNull(res[1018].exec("abc", 3258));
+assertNull(res[1018].exec("abq", 3259));
+assertToStringEquals("abbbbc", res[1020].exec("abbbbc"), 3260);
+assertToStringEquals("abbbbc", res[1021].exec("abbbbc"), 3261);
+assertToStringEquals("abbbbc", res[1022].exec("abbbbc"), 3262);
+assertToStringEquals("abbbbc", res[1023].exec("abbbbc"), 3263);
+assertNull(res[1024].exec("*** Failers", 3264));
+assertNull(res[1024].exec("abq", 3265));
+assertNull(res[1024].exec("abbbbc", 3266));
+assertToStringEquals("abbc", res[1025].exec("abbc"), 3267);
+assertToStringEquals("abc", res[1025].exec("abc"), 3268);
+assertToStringEquals("abc", res[1026].exec("abc"), 3269);
+assertToStringEquals("abc", res[1028].exec("abc"), 3270);
+assertToStringEquals("abc", res[1029].exec("abc"), 3271);
+assertToStringEquals("abc", res[1030].exec("abc"), 3272);
+assertNull(res[1030].exec("*** Failers", 3273));
+assertNull(res[1030].exec("abbbbc", 3274));
+assertNull(res[1030].exec("abcc", 3275));
+assertToStringEquals("abc", res[1031].exec("abcc"), 3276);
+assertToStringEquals("abc", res[1033].exec("aabc"), 3277);
+assertNull(res[1033].exec("*** Failers", 3278));
+assertToStringEquals("abc", res[1033].exec("aabc"), 3279);
+assertNull(res[1033].exec("aabcd", 3280));
+assertToStringEquals("", res[1034].exec("abc"), 3281);
+assertToStringEquals("", res[1035].exec("abc"), 3282);
+assertToStringEquals("abc", res[1036].exec("abc"), 3283);
+assertToStringEquals("axc", res[1036].exec("axc"), 3284);
+assertToStringEquals("axyzc", res[1037].exec("axyzc"), 3285);
+assertToStringEquals("abd", res[1038].exec("abd"), 3286);
+assertNull(res[1038].exec("*** Failers", 3287));
+assertNull(res[1038].exec("axyzd", 3288));
+assertNull(res[1038].exec("abc", 3289));
+assertToStringEquals("ace", res[1039].exec("ace"), 3290);
+assertToStringEquals("ac", res[1040].exec("aac"), 3291);
+assertToStringEquals("a-", res[1041].exec("a-"), 3292);
+assertToStringEquals("a-", res[1042].exec("a-"), 3293);
+assertToStringEquals("a]", res[1043].exec("a]"), 3294);
+assertNull(res[1044].exec("a]b", 3295));
+assertToStringEquals("aed", res[1045].exec("aed"), 3296);
+assertNull(res[1045].exec("*** Failers", 3297));
+assertNull(res[1045].exec("abd", 3298));
+assertNull(res[1045].exec("abd", 3299));
+assertToStringEquals("adc", res[1046].exec("adc"), 3300);
+assertNull(res[1047].exec("adc", 3301));
+assertNull(res[1047].exec("*** Failers", 3302));
+assertNull(res[1047].exec("a-c", 3303));
+assertNull(res[1047].exec("a]c", 3304));
+assertToStringEquals("a", res[1048].exec("a-"), 3305);
+assertToStringEquals("a", res[1048].exec("-a"), 3306);
+assertToStringEquals("a", res[1048].exec("-a-"), 3307);
+assertNull(res[1049].exec("*** Failers", 3308));
+assertNull(res[1049].exec("xy", 3309));
+assertNull(res[1049].exec("yz", 3310));
+assertNull(res[1049].exec("xyz", 3311));
+assertToStringEquals("a", res[1050].exec("*** Failers"), 3312);
+assertNull(res[1050].exec("a-", 3313));
+assertNull(res[1050].exec("-a", 3314));
+assertNull(res[1050].exec("-a-", 3315));
+assertToStringEquals("y", res[1051].exec("xy"), 3316);
+assertToStringEquals("y", res[1052].exec("yz"), 3317);
+assertToStringEquals("y", res[1053].exec("xyz"), 3318);
+assertToStringEquals("a", res[1054].exec("a"), 3319);
+assertToStringEquals("-", res[1055].exec("-"), 3320);
+assertToStringEquals("*", res[1055].exec("*** Failers"), 3321);
+assertToStringEquals("-", res[1055].exec("-"), 3322);
+assertNull(res[1055].exec("a", 3323));
+assertToStringEquals("a b", res[1056].exec("a b"), 3324);
+assertToStringEquals("a-b", res[1057].exec("a-b"), 3325);
+assertNull(res[1057].exec("*** Failers", 3326));
+assertToStringEquals("a-b", res[1057].exec("a-b"), 3327);
+assertNull(res[1057].exec("a b", 3328));
+assertToStringEquals("1", res[1058].exec("1"), 3329);
+assertToStringEquals("-", res[1059].exec("-"), 3330);
+assertToStringEquals("*", res[1059].exec("*** Failers"), 3331);
+assertToStringEquals("-", res[1059].exec("-"), 3332);
+assertNull(res[1059].exec("1", 3333));
+assertToStringEquals("a", res[1060].exec("a"), 3334);
+assertToStringEquals("-", res[1061].exec("-"), 3335);
+assertToStringEquals("*", res[1061].exec("*** Failers"), 3336);
+assertToStringEquals("-", res[1061].exec("-"), 3337);
+assertNull(res[1061].exec("a", 3338));
+assertToStringEquals("a b", res[1062].exec("a b"), 3339);
+assertToStringEquals("a-b", res[1063].exec("a-b"), 3340);
+assertNull(res[1063].exec("*** Failers", 3341));
+assertToStringEquals("a-b", res[1063].exec("a-b"), 3342);
+assertNull(res[1063].exec("a b", 3343));
+assertToStringEquals("1", res[1064].exec("1"), 3344);
+assertToStringEquals("-", res[1065].exec("-"), 3345);
+assertToStringEquals("*", res[1065].exec("*** Failers"), 3346);
+assertToStringEquals("-", res[1065].exec("-"), 3347);
+assertNull(res[1065].exec("1", 3348));
+assertToStringEquals("ab", res[1066].exec("abc"), 3349);
+assertToStringEquals("ab", res[1066].exec("abcd"), 3350);
+assertToStringEquals("ef,", res[1067].exec("def"), 3351);
+assertToStringEquals("a(b", res[1069].exec("a(b"), 3352);
+assertNull(res[1069].exec("ab", 3353));
+assertNull(res[1069].exec("a((b", 3354));
+assertNull(res[1070].exec("a\x08", 3355));
+assertToStringEquals("a,a,a", res[1071].exec("abc"), 3356);
+assertToStringEquals("abc,a,c", res[1072].exec("abc"), 3357);
+assertToStringEquals("abc", res[1073].exec("aabbabc"), 3358);
+assertToStringEquals("abc", res[1074].exec("aabbabc"), 3359);
+assertToStringEquals("abc", res[1075].exec("abcabc"), 3360);
+assertToStringEquals("ab,b", res[1076].exec("ab"), 3361);
+assertToStringEquals("ab,b", res[1077].exec("ab"), 3362);
+assertToStringEquals("ab,b", res[1078].exec("ab"), 3363);
+assertToStringEquals("ab,b", res[1079].exec("ab"), 3364);
+assertToStringEquals("a,a", res[1080].exec("ab"), 3365);
+assertToStringEquals("a,a", res[1081].exec("ab"), 3366);
+assertToStringEquals("cde", res[1082].exec("cde"), 3367);
+assertNull(res[1083].exec("*** Failers", 3368));
+assertNull(res[1083].exec("b", 3369));
+assertToStringEquals("abbbcd,c", res[1085].exec("abbbcd"), 3370);
+assertToStringEquals("abcd,a", res[1086].exec("abcd"), 3371);
+assertToStringEquals("e", res[1087].exec("e"), 3372);
+assertToStringEquals("ef,e", res[1088].exec("ef"), 3373);
+assertToStringEquals("abcdefg", res[1089].exec("abcdefg"), 3374);
+assertToStringEquals("ab", res[1090].exec("xabyabbbz"), 3375);
+assertToStringEquals("a", res[1090].exec("xayabbbz"), 3376);
+assertToStringEquals("cde,cd", res[1091].exec("abcde"), 3377);
+assertToStringEquals("hij", res[1092].exec("hij"), 3378);
+assertToStringEquals("ef,", res[1094].exec("abcdef"), 3379);
+assertToStringEquals("bcd,b", res[1095].exec("abcd"), 3380);
+assertToStringEquals("abc,a", res[1096].exec("abc"), 3381);
+assertToStringEquals("abc,bc", res[1097].exec("abc"), 3382);
+assertToStringEquals("abcd,bc,d", res[1098].exec("abcd"), 3383);
+assertToStringEquals("abcd,bc,d", res[1099].exec("abcd"), 3384);
+assertToStringEquals("abcd,b,cd", res[1100].exec("abcd"), 3385);
+assertToStringEquals("adcdcde", res[1101].exec("adcdcde"), 3386);
+assertNull(res[1102].exec("*** Failers", 3387));
+assertNull(res[1102].exec("abcde", 3388));
+assertNull(res[1102].exec("adcdcde", 3389));
+assertToStringEquals("abc,ab", res[1103].exec("abc"), 3390);
+assertToStringEquals("abcd,abc,a,b,d", res[1104].exec("abcd"), 3391);
+assertToStringEquals("alpha", res[1105].exec("alpha"), 3392);
+assertToStringEquals("bh,", res[1106].exec("abh"), 3393);
+assertToStringEquals("effgz,effgz,", res[1107].exec("effgz"), 3394);
+assertToStringEquals("ij,ij,j", res[1107].exec("ij"), 3395);
+assertToStringEquals("effgz,effgz,", res[1107].exec("reffgz"), 3396);
+assertNull(res[1107].exec("*** Failers", 3397));
+assertNull(res[1107].exec("effg", 3398));
+assertNull(res[1107].exec("bcdd", 3399));
+assertToStringEquals("a,a,a,a,a,a,a,a,a,a,a", res[1108].exec("a"), 3400);
+assertToStringEquals("a,a,a,a,a,a,a,a,a,a", res[1109].exec("a"), 3401);
+assertNull(res[1110].exec("*** Failers", 3402));
+assertNull(res[1110].exec("aa", 3403));
+assertNull(res[1110].exec("uh-uh", 3404));
+assertToStringEquals("multiple words", res[1111].exec("multiple words, yeah"), 3405);
+assertToStringEquals("abcde,ab,de", res[1112].exec("abcde"), 3406);
+assertToStringEquals("(a, b),a,b", res[1113].exec("(a, b)"), 3407);
+assertToStringEquals("abcd", res[1115].exec("abcd"), 3408);
+assertToStringEquals("abcd,bc", res[1116].exec("abcd"), 3409);
+assertToStringEquals("ac", res[1117].exec("ac"), 3410);
+assertToStringEquals("ABC", res[1118].exec("ABC"), 3411);
+assertToStringEquals("ABC", res[1118].exec("XABCY"), 3412);
+assertToStringEquals("ABC", res[1118].exec("ABABC"), 3413);
+assertNull(res[1118].exec("*** Failers", 3414));
+assertNull(res[1118].exec("aaxabxbaxbbx", 3415));
+assertNull(res[1118].exec("XBC", 3416));
+assertNull(res[1118].exec("AXC", 3417));
+assertNull(res[1118].exec("ABX", 3418));
+assertToStringEquals("ABC", res[1119].exec("ABC"), 3419);
+assertToStringEquals("ABC", res[1120].exec("ABC"), 3420);
+assertToStringEquals("ABBC", res[1120].exec("ABBC"), 3421);
+assertToStringEquals("ABBBBC", res[1121].exec("ABBBBC"), 3422);
+assertToStringEquals("ABBBBC", res[1122].exec("ABBBBC"), 3423);
+assertToStringEquals("ABBC", res[1123].exec("ABBC"), 3424);
+assertNull(res[1124].exec("*** Failers", 3425));
+assertNull(res[1124].exec("ABC", 3426));
+assertNull(res[1124].exec("ABQ", 3427));
+assertToStringEquals("ABBBBC", res[1126].exec("ABBBBC"), 3428);
+assertToStringEquals("ABBBBC", res[1127].exec("ABBBBC"), 3429);
+assertToStringEquals("ABBBBC", res[1128].exec("ABBBBC"), 3430);
+assertToStringEquals("ABBBBC", res[1129].exec("ABBBBC"), 3431);
+assertNull(res[1130].exec("*** Failers", 3432));
+assertNull(res[1130].exec("ABQ", 3433));
+assertNull(res[1130].exec("ABBBBC", 3434));
+assertToStringEquals("ABBC", res[1131].exec("ABBC"), 3435);
+assertToStringEquals("ABC", res[1131].exec("ABC"), 3436);
+assertToStringEquals("ABC", res[1132].exec("ABC"), 3437);
+assertToStringEquals("ABC", res[1134].exec("ABC"), 3438);
+assertToStringEquals("ABC", res[1135].exec("ABC"), 3439);
+assertToStringEquals("ABC", res[1136].exec("ABC"), 3440);
+assertNull(res[1136].exec("*** Failers", 3441));
+assertNull(res[1136].exec("ABBBBC", 3442));
+assertNull(res[1136].exec("ABCC", 3443));
+assertToStringEquals("ABC", res[1137].exec("ABCC"), 3444);
+assertToStringEquals("ABC", res[1139].exec("AABC"), 3445);
+assertToStringEquals("", res[1140].exec("ABC"), 3446);
+assertToStringEquals("", res[1141].exec("ABC"), 3447);
+assertToStringEquals("ABC", res[1142].exec("ABC"), 3448);
+assertToStringEquals("AXC", res[1142].exec("AXC"), 3449);
+assertToStringEquals("AXYZC", res[1143].exec("AXYZC"), 3450);
+assertNull(res[1144].exec("*** Failers", 3451));
+assertToStringEquals("AABC", res[1144].exec("AABC"), 3452);
+assertNull(res[1144].exec("AXYZD", 3453));
+assertToStringEquals("ABD", res[1145].exec("ABD"), 3454);
+assertToStringEquals("ACE", res[1146].exec("ACE"), 3455);
+assertNull(res[1146].exec("*** Failers", 3456));
+assertNull(res[1146].exec("ABC", 3457));
+assertNull(res[1146].exec("ABD", 3458));
+assertToStringEquals("AC", res[1147].exec("AAC"), 3459);
+assertToStringEquals("A-", res[1148].exec("A-"), 3460);
+assertToStringEquals("A-", res[1149].exec("A-"), 3461);
+assertToStringEquals("A]", res[1150].exec("A]"), 3462);
+assertNull(res[1151].exec("A]B", 3463));
+assertToStringEquals("AED", res[1152].exec("AED"), 3464);
+assertToStringEquals("ADC", res[1153].exec("ADC"), 3465);
+assertNull(res[1153].exec("*** Failers", 3466));
+assertNull(res[1153].exec("ABD", 3467));
+assertNull(res[1153].exec("A-C", 3468));
+assertNull(res[1154].exec("ADC", 3469));
+assertToStringEquals("AB", res[1155].exec("ABC"), 3470);
+assertToStringEquals("AB", res[1155].exec("ABCD"), 3471);
+assertToStringEquals("EF,", res[1156].exec("DEF"), 3472);
+assertNull(res[1157].exec("*** Failers", 3473));
+assertNull(res[1157].exec("A]C", 3474));
+assertNull(res[1157].exec("B", 3475));
+assertToStringEquals("A(B", res[1158].exec("A(B"), 3476);
+assertNull(res[1158].exec("AB", 3477));
+assertNull(res[1158].exec("A((B", 3478));
+assertNull(res[1159].exec("AB", 3479));
+assertToStringEquals("A,A,A", res[1160].exec("ABC"), 3480);
+assertToStringEquals("ABC,A,C", res[1161].exec("ABC"), 3481);
+assertToStringEquals("ABC", res[1162].exec("AABBABC"), 3482);
+assertToStringEquals("ABC", res[1163].exec("AABBABC"), 3483);
+assertToStringEquals("ABC", res[1164].exec("ABCABC"), 3484);
+assertToStringEquals("ABC", res[1165].exec("ABCABC"), 3485);
+assertToStringEquals("ABC", res[1166].exec("ABCABC"), 3486);
+assertToStringEquals("AB,B", res[1167].exec("AB"), 3487);
+assertToStringEquals("AB,B", res[1168].exec("AB"), 3488);
+assertToStringEquals("AB,B", res[1169].exec("AB"), 3489);
+assertToStringEquals("AB,B", res[1170].exec("AB"), 3490);
+assertToStringEquals("A,A", res[1171].exec("AB"), 3491);
+assertToStringEquals("A,A", res[1172].exec("AB"), 3492);
+assertToStringEquals(",", res[1173].exec("AB"), 3493);
+assertToStringEquals("CDE", res[1174].exec("CDE"), 3494);
+assertToStringEquals("ABBBCD,C", res[1177].exec("ABBBCD"), 3495);
+assertToStringEquals("ABCD,A", res[1178].exec("ABCD"), 3496);
+assertToStringEquals("E", res[1179].exec("E"), 3497);
+assertToStringEquals("EF,E", res[1180].exec("EF"), 3498);
+assertToStringEquals("ABCDEFG", res[1181].exec("ABCDEFG"), 3499);
+assertToStringEquals("AB", res[1182].exec("XABYABBBZ"), 3500);
+assertToStringEquals("A", res[1182].exec("XAYABBBZ"), 3501);
+assertToStringEquals("CDE,CD", res[1183].exec("ABCDE"), 3502);
+assertToStringEquals("HIJ", res[1184].exec("HIJ"), 3503);
+assertNull(res[1185].exec("ABCDE", 3504));
+assertToStringEquals("EF,", res[1186].exec("ABCDEF"), 3505);
+assertToStringEquals("BCD,B", res[1187].exec("ABCD"), 3506);
+assertToStringEquals("ABC,A", res[1188].exec("ABC"), 3507);
+assertToStringEquals("ABC,BC", res[1189].exec("ABC"), 3508);
+assertToStringEquals("ABCD,BC,D", res[1190].exec("ABCD"), 3509);
+assertToStringEquals("ABCD,BC,D", res[1191].exec("ABCD"), 3510);
+assertToStringEquals("ABCD,B,CD", res[1192].exec("ABCD"), 3511);
+assertToStringEquals("ADCDCDE", res[1193].exec("ADCDCDE"), 3512);
+assertToStringEquals("ABC,AB", res[1195].exec("ABC"), 3513);
+assertToStringEquals("ABCD,ABC,A,B,D", res[1196].exec("ABCD"), 3514);
+assertToStringEquals("ALPHA", res[1197].exec("ALPHA"), 3515);
+assertToStringEquals("BH,", res[1198].exec("ABH"), 3516);
+assertToStringEquals("EFFGZ,EFFGZ,", res[1199].exec("EFFGZ"), 3517);
+assertToStringEquals("IJ,IJ,J", res[1199].exec("IJ"), 3518);
+assertToStringEquals("EFFGZ,EFFGZ,", res[1199].exec("REFFGZ"), 3519);
+assertNull(res[1199].exec("*** Failers", 3520));
+assertNull(res[1199].exec("ADCDCDE", 3521));
+assertNull(res[1199].exec("EFFG", 3522));
+assertNull(res[1199].exec("BCDD", 3523));
+assertToStringEquals("A,A,A,A,A,A,A,A,A,A,A", res[1200].exec("A"), 3524);
+assertToStringEquals("A,A,A,A,A,A,A,A,A,A", res[1201].exec("A"), 3525);
+assertToStringEquals("A,A", res[1202].exec("A"), 3526);
+assertToStringEquals("C,C", res[1203].exec("C"), 3527);
+assertNull(res[1204].exec("*** Failers", 3528));
+assertNull(res[1204].exec("AA", 3529));
+assertNull(res[1204].exec("UH-UH", 3530));
+assertToStringEquals("MULTIPLE WORDS", res[1205].exec("MULTIPLE WORDS, YEAH"), 3531);
+assertToStringEquals("ABCDE,AB,DE", res[1206].exec("ABCDE"), 3532);
+assertToStringEquals("(A, B),A,B", res[1207].exec("(A, B)"), 3533);
+assertToStringEquals("ABCD", res[1209].exec("ABCD"), 3534);
+assertToStringEquals("ABCD,BC", res[1210].exec("ABCD"), 3535);
+assertToStringEquals("AC", res[1211].exec("AC"), 3536);
+assertToStringEquals("ad", res[1212].exec("abad"), 3537);
+assertToStringEquals("ad", res[1213].exec("abad"), 3538);
+assertToStringEquals("ad", res[1214].exec("abad"), 3539);
+assertToStringEquals("ace,e", res[1215].exec("ace"), 3540);
+assertToStringEquals("ace,e", res[1216].exec("ace"), 3541);
+assertToStringEquals("ace,e", res[1217].exec("ace"), 3542);
+assertToStringEquals("acd,d", res[1217].exec("acdbcdbe"), 3543);
+assertToStringEquals("acdbcdbe,e", res[1218].exec("acdbcdbe"), 3544);
+assertToStringEquals("acdb,b", res[1219].exec("acdbcdbe"), 3545);
+assertToStringEquals("acdbcdb,b", res[1220].exec("acdbcdbe"), 3546);
+assertToStringEquals("acdbcd,d", res[1221].exec("acdbcdbe"), 3547);
+assertToStringEquals("foobar,bar,,bar", res[1222].exec("foobar"), 3548);
+assertToStringEquals("acdbcdbe,e", res[1223].exec("acdbcdbe"), 3549);
+assertToStringEquals("acdbcdbe,e", res[1224].exec("acdbcdbe"), 3550);
+assertToStringEquals("acdbcdbe,e", res[1225].exec("acdbcdbe"), 3551);
+assertToStringEquals("acdbcdb,b", res[1226].exec("acdbcdbe"), 3552);
+assertToStringEquals("acdbcdbe,e", res[1227].exec("acdbcdbe"), 3553);
+assertToStringEquals("acdbcdb,b", res[1228].exec("acdbcdbe"), 3554);
+assertToStringEquals("ace,c,e", res[1229].exec("ace"), 3555);
+assertToStringEquals("AB,A", res[1230].exec("AB"), 3556);
+assertToStringEquals(".,.,", res[1231].exec("."), 3557);
+assertToStringEquals("<&", res[1232].exec("<&OUT"), 3558);
+assertToStringEquals("foobar,,,,b,a,r", res[1233].exec("foobar"), 3559);
+assertToStringEquals(",,,,,,", res[1233].exec("ab"), 3560);
+assertToStringEquals(",,,,,,", res[1233].exec("*** Failers"), 3561);
+assertToStringEquals(",,,,,,", res[1233].exec("cb"), 3562);
+assertToStringEquals(",,,,,,", res[1233].exec("b"), 3563);
+assertToStringEquals(",,,,,,", res[1233].exec("ab"), 3564);
+assertToStringEquals(",,,,,,", res[1233].exec("b"), 3565);
+assertToStringEquals(",,,,,,", res[1233].exec("b"), 3566);
+assertToStringEquals("aba", res[1234].exec("aba"), 3567);
+assertToStringEquals("a", res[1235].exec("aba"), 3568);
+assertToStringEquals(",", res[1236].exec("abc"), 3569);
+assertToStringEquals("aax,a", res[1237].exec("aax"), 3570);
+assertToStringEquals("aax,a,a", res[1238].exec("aax"), 3571);
+assertToStringEquals("aax,a,a", res[1239].exec("aax"), 3572);
+assertToStringEquals("ab,", res[1240].exec("cab"), 3573);
+assertToStringEquals("ab,", res[1241].exec("cab"), 3574);
+assertToStringEquals("ab,", res[1241].exec("ab"), 3575);
+assertToStringEquals("ab,", res[1241].exec("ab"), 3576);
+assertNull(res[1241].exec("Ab", 3577));
+assertNull(res[1241].exec("Ab", 3578));
+assertNull(res[1241].exec("*** Failers", 3579));
+assertNull(res[1241].exec("cb", 3580));
+assertNull(res[1241].exec("aB", 3581));
+assertToStringEquals("ab,", res[1241].exec("ab"), 3582);
+assertToStringEquals("ab,", res[1241].exec("ab"), 3583);
+assertNull(res[1241].exec("Ab", 3584));
+assertNull(res[1241].exec("Ab", 3585));
+assertNull(res[1241].exec("*** Failers", 3586));
+assertNull(res[1241].exec("aB", 3587));
+assertNull(res[1241].exec("aB", 3588));
+assertToStringEquals("ab,", res[1241].exec("ab"), 3589);
+assertToStringEquals("ab,", res[1241].exec("ab"), 3590);
+assertNull(res[1241].exec("aB", 3591));
+assertNull(res[1241].exec("aB", 3592));
+assertNull(res[1241].exec("*** Failers", 3593));
+assertNull(res[1241].exec("aB", 3594));
+assertNull(res[1241].exec("Ab", 3595));
+assertNull(res[1241].exec("aB", 3596));
+assertNull(res[1241].exec("aB", 3597));
+assertNull(res[1241].exec("*** Failers", 3598));
+assertNull(res[1241].exec("Ab", 3599));
+assertNull(res[1241].exec("AB", 3600));
+assertToStringEquals("ab,", res[1241].exec("ab"), 3601);
+assertToStringEquals("ab,", res[1241].exec("ab"), 3602);
+assertNull(res[1241].exec("aB", 3603));
+assertNull(res[1241].exec("aB", 3604));
+assertNull(res[1241].exec("*** Failers", 3605));
+assertNull(res[1241].exec("AB", 3606));
+assertNull(res[1241].exec("Ab", 3607));
+assertNull(res[1241].exec("aB", 3608));
+assertNull(res[1241].exec("aB", 3609));
+assertNull(res[1241].exec("*** Failers", 3610));
+assertNull(res[1241].exec("Ab", 3611));
+assertNull(res[1241].exec("AB", 3612));
+assertNull(res[1241].exec("*** Failers", 3613));
+assertNull(res[1241].exec("AB", 3614));
+assertNull(res[1241].exec("a\nB", 3615));
+assertNull(res[1241].exec("a\nB", 3616));
+assertToStringEquals("cabbbb", res[1242].exec("cabbbb"), 3617);
+assertToStringEquals("caaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", res[1243].exec("caaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), 3618);
+assertToStringEquals("foobar1234baz", res[1244].exec("foobar1234baz"), 3619);
+assertToStringEquals("x~~,~~", res[1245].exec("x~~"), 3620);
+assertToStringEquals("aaac", res[1246].exec("aaac"), 3621);
+assertToStringEquals("aaac", res[1247].exec("aaac"), 3622);
+assertNull(res[1247].exec("*** Failers", 3623));
+assertNull(res[1247].exec("B\nB", 3624));
+assertNull(res[1247].exec("dbcb", 3625));
+assertNull(res[1247].exec("dbaacb", 3626));
+assertNull(res[1247].exec("dbaacb", 3627));
+assertNull(res[1247].exec("cdaccb", 3628));
+assertNull(res[1248].exec("*** Failers", 3629));
+assertNull(res[1248].exec("dbcb", 3630));
+assertNull(res[1248].exec("a--", 3631));
+assertNull(res[1248].exec("a\nb\nc\n", 3632));
+assertNull(res[1248].exec("a\nb\nc\n", 3633));
+assertNull(res[1248].exec("a\nb\n", 3634));
+assertNull(res[1248].exec("a\nb\n", 3635));
+assertNull(res[1248].exec("a\nb\n", 3636));
+assertNull(res[1248].exec("a\nb\n", 3637));
+assertNull(res[1248].exec("a\nb\nc\n", 3638));
+assertNull(res[1248].exec("a\nb\nc\n", 3639));
+assertNull(res[1248].exec("a\nb\nc\n", 3640));
+assertNull(res[1248].exec("a\nb\nc\n", 3641));
+assertNull(res[1250].exec("*** Failers", 3642));
+assertNull(res[1250].exec("a\nb\nc\n", 3643));
+assertNull(res[1250].exec("a\nb\nc\n", 3644));
+assertNull(res[1250].exec("a\nb\nc\n", 3645));
+assertNull(res[1250].exec("a", 3646));
+assertNull(res[1250].exec("*** Failers", 3647));
+assertNull(res[1250].exec("a", 3648));
+assertNull(res[1250].exec("a", 3649));
+assertNull(res[1250].exec("a", 3650));
+assertToStringEquals("one:,one:", res[1251].exec("one:"), 3651);
+assertNull(res[1251].exec("a", 3652));
+assertToStringEquals("abcd,,abcd", res[1252].exec("abcd"), 3653);
+assertToStringEquals("xy:z:::abcd,xy:z:::,abcd", res[1252].exec("xy:z:::abcd"), 3654);
+assertToStringEquals("aexyc,c", res[1253].exec("aexycd"), 3655);
+assertToStringEquals("aab,aa", res[1254].exec("caab"), 3656);
+assertToStringEquals("abcd,,abcd", res[1255].exec("abcd"), 3657);
+assertToStringEquals("xy:z:::abcd,xy:z:::,abcd", res[1255].exec("xy:z:::abcd"), 3658);
+assertToStringEquals("Failers,,Failers", res[1255].exec("*** Failers"), 3659);
+assertNull(res[1255].exec("abcd:", 3660));
+assertNull(res[1255].exec("abcd:", 3661));
+assertToStringEquals("aexyc,c", res[1256].exec("aexycd"), 3662);
+assertNull(res[1257].exec("aaab", 3663));
+assertToStringEquals(":[,:[", res[1258].exec("a:[b]:"), 3664);
+assertToStringEquals("=[,=[", res[1259].exec("a=[b]="), 3665);
+assertToStringEquals(".[,.[", res[1260].exec("a.[b]."), 3666);
+assertNull(res[1260].exec("aaab", 3667));
+assertNull(res[1260].exec("aaab", 3668));
+assertNull(res[1260].exec("((abc(ade)ufh()()x", 3669));
+assertNull(res[1261].exec("*** Failers", 3670));
+assertNull(res[1261].exec("aaab", 3671));
+assertNull(res[1261].exec("a\nb\n", 3672));
+assertNull(res[1262].exec("a\nb\n", 3673));
+assertNull(res[1264].exec("a\nb", 3674));
+assertNull(res[1265].exec("a\nb", 3675));
+assertNull(res[1265].exec("*** Failers", 3676));
+assertNull(res[1265].exec("alphabetabcd", 3677));
+assertNull(res[1265].exec("endingwxyz", 3678));
+assertNull(res[1265].exec("*** Failers", 3679));
+assertNull(res[1265].exec("a rather long string that doesn't end with one of them", 3680));
+assertNull(res[1265].exec("word cat dog elephant mussel cow horse canary baboon snake shark otherword", 3681));
+assertNull(res[1265].exec("word cat dog elephant mussel cow horse canary baboon snake shark", 3682));
+assertNull(res[1265].exec("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope", 3683));
+assertNull(res[1265].exec("999foo", 3684));
+assertNull(res[1265].exec("123999foo ", 3685));
+assertNull(res[1265].exec("*** Failers", 3686));
+assertNull(res[1265].exec("123abcfoo", 3687));
+assertNull(res[1265].exec("999foo", 3688));
+assertNull(res[1265].exec("123999foo ", 3689));
+assertNull(res[1265].exec("*** Failers", 3690));
+assertNull(res[1265].exec("123abcfoo", 3691));
+assertNull(res[1265].exec("123abcfoo", 3692));
+assertNull(res[1265].exec("123456foo ", 3693));
+assertNull(res[1265].exec("*** Failers", 3694));
+assertNull(res[1265].exec("123999foo ", 3695));
+assertNull(res[1265].exec("123abcfoo ", 3696));
+assertNull(res[1265].exec("123456foo ", 3697));
+assertNull(res[1265].exec("*** Failers", 3698));
+assertNull(res[1265].exec("123999foo ", 3699));
+assertToStringEquals("ZA,A,", res[1266].exec("ZABCDEFG"), 3700);
+assertToStringEquals("ZA,A,", res[1267].exec("ZABCDEFG"), 3701);
+assertToStringEquals("ZA,A,,", res[1268].exec("ZABCDEFG"), 3702);
+assertToStringEquals("ZA,A,,", res[1268].exec("ZABCDEFG"), 3703);
+assertToStringEquals("ZA,A,,", res[1268].exec("ZABCDEFG"), 3704);
+assertToStringEquals("a", res[1269].exec("abbab"), 3705);
+assertToStringEquals("", res[1269].exec("abcde"), 3706);
+assertToStringEquals("", res[1269].exec("-things"), 3707);
+assertToStringEquals("", res[1269].exec("0digit"), 3708);
+assertToStringEquals("", res[1269].exec("*** Failers"), 3709);
+assertToStringEquals("", res[1269].exec("bcdef "), 3710);
+assertToStringEquals("a", res[1270].exec("abcde"), 3711);
+assertToStringEquals("-", res[1270].exec("-things"), 3712);
+assertToStringEquals("0", res[1270].exec("0digit"), 3713);
+assertNull(res[1270].exec("*** Failers", 3714));
+assertNull(res[1270].exec("bcdef ", 3715));
+assertNull(res[1271].exec("> \x09\n\x0c\x0d\x0b<", 3716));
+assertNull(res[1271].exec(" ", 3717));
+assertNull(res[1272].exec("> \x09\n\x0c\x0d\x0b<", 3718));
+assertNull(res[1272].exec(" ", 3719));
+assertToStringEquals(" \x09\n\x0c\x0d\x0b", res[1273].exec("> \x09\n\x0c\x0d\x0b<"), 3720);
+assertToStringEquals(" ", res[1273].exec(" "), 3721);
+assertToStringEquals(" \x09\n\x0c\x0d\x0b", res[1274].exec("> \x09\n\x0c\x0d\x0b<"), 3722);
+assertToStringEquals(" ", res[1274].exec(" "), 3723);
+assertNull(res[1275].exec("ab", 3724));
+assertNull(res[1278].exec("abcabcabc", 3725));
+assertNull(res[1278].exec("abc(*+|abc ", 3726));
+assertNull(res[1279].exec("abc abcabc", 3727));
+assertNull(res[1279].exec("*** Failers", 3728));
+assertNull(res[1279].exec("abcabcabc ", 3729));
+assertNull(res[1280].exec("abc#not comment\n literal ", 3730));
+assertNull(res[1281].exec("abc#not comment\n literal ", 3731));
+assertNull(res[1282].exec("abc#not comment\n literal ", 3732));
+assertNull(res[1283].exec("abc#not comment\n literal ", 3733));
+assertNull(res[1284].exec("abc\\$xyz", 3734));
+assertNull(res[1285].exec("abc$xyz", 3735));
+assertNull(res[1286].exec("abc", 3736));
+assertNull(res[1286].exec("*** Failers", 3737));
+assertNull(res[1286].exec("xyzabc ", 3738));
+assertNull(res[1287].exec("abc1abc2xyzabc3", 3739));
+assertToStringEquals("abc1", res[1288].exec("abc1abc2xyzabc3 "), 3740);
+assertNull(res[1288].exec("XabcdY", 3741));
+assertNull(res[1288].exec("*** Failers ", 3742));
+assertNull(res[1288].exec("Xa b c d Y ", 3743));
+assertToStringEquals("abcY", res[1288].exec("XabcY"), 3744);
+assertNull(res[1288].exec("AxyzB ", 3745));
+assertNull(res[1288].exec("XabCY", 3746));
+assertNull(res[1288].exec("*** Failers", 3747));
+assertToStringEquals("abcY", res[1288].exec("XabcY "), 3748);
+assertNull(res[1288].exec("abCE", 3749));
+assertNull(res[1288].exec("DE", 3750));
+assertNull(res[1288].exec("*** Failers", 3751));
+assertToStringEquals("abcE", res[1288].exec("abcE"), 3752);
+assertNull(res[1288].exec("abCe ", 3753));
+assertNull(res[1288].exec("dE", 3754));
+assertNull(res[1288].exec("De ", 3755));
+assertNull(res[1289].exec("z", 3756));
+assertNull(res[1289].exec("a", 3757));
+assertNull(res[1289].exec("-", 3758));
+assertNull(res[1289].exec("d", 3759));
+assertNull(res[1289].exec("] ", 3760));
+assertNull(res[1289].exec("*** Failers", 3761));
+assertNull(res[1289].exec("b ", 3762));
+assertToStringEquals("z", res[1290].exec("z"), 3763);
+assertToStringEquals("C", res[1290].exec("C "), 3764);
+assertToStringEquals("M", res[1291].exec("M "), 3765);
+assertNull(res[1292].exec("", 3766));
+assertNull(res[1292].exec("REGular", 3767));
+assertNull(res[1292].exec("regulaer", 3768));
+assertNull(res[1292].exec("Regex ", 3769));
+assertNull(res[1292].exec("regul\ufffdr ", 3770));
+assertNull(res[1292].exec("\ufffd\ufffd\ufffd\ufffd\ufffd", 3771));
+assertNull(res[1292].exec("\ufffd\ufffd\ufffd\ufffd\ufffd", 3772));
+assertNull(res[1292].exec("\ufffd\ufffd\ufffd\ufffd\ufffd", 3773));
+assertNull(res[1292].exec("\ufffd\ufffd\ufffd\ufffd\ufffd", 3774));
+assertNull(res[1292].exec("\x84XAZXB", 3775));
+assertNull(res[1292].exec("123a", 3776));
+assertNull(res[1292].exec("ac", 3777));
+assertToStringEquals("b,", res[1292].exec("bbbbc"), 3778);
+assertToStringEquals("ab,a", res[1292].exec("abc"), 3779);
+assertNull(res[1292].exec("*** Failers", 3780));
+assertToStringEquals("b,", res[1292].exec("bca"), 3781);
+assertNull(res[1292].exec("", 3782));
+assertToStringEquals("ab,a", res[1292].exec("abc"), 3783);
+assertNull(res[1292].exec("*** Failers", 3784));
+assertToStringEquals("b,", res[1292].exec("bca"), 3785);
+assertToStringEquals("ab,a", res[1292].exec("abc"), 3786);
+assertNull(res[1292].exec("*** Failers", 3787));
+assertNull(res[1292].exec("def ", 3788));
+assertNull(res[1292].exec("", 3789));
+assertToStringEquals("ab,a", res[1292].exec("abc"), 3790);
+assertNull(res[1292].exec("*** Failers", 3791));
+assertNull(res[1292].exec("def ", 3792));
+assertNull(res[1292].exec("", 3793));
+assertToStringEquals("line\nbreak", res[1293].exec("this is a line\nbreak"), 3794);
+assertToStringEquals("line\nbreak", res[1293].exec("line one\nthis is a line\nbreak in the second line "), 3795);
+assertToStringEquals("line\nbreak", res[1294].exec("this is a line\nbreak"), 3796);
+assertNull(res[1294].exec("** Failers ", 3797));
+assertToStringEquals("line\nbreak", res[1294].exec("line one\nthis is a line\nbreak in the second line "), 3798);
+assertToStringEquals("line\nbreak", res[1295].exec("this is a line\nbreak"), 3799);
+assertNull(res[1295].exec("** Failers ", 3800));
+assertToStringEquals("line\nbreak", res[1295].exec("line one\nthis is a line\nbreak in the second line "), 3801);
+assertNull(res[1296].exec("123P", 3802));
+assertNull(res[1296].exec("a4PR", 3803));
+assertNull(res[1297].exec("123P", 3804));
+assertNull(res[1297].exec("4PR", 3805));
+assertToStringEquals("", res[1298].exec("a\nb\nc\n"), 3806);
+assertToStringEquals("", res[1298].exec(" "), 3807);
+assertToStringEquals("", res[1298].exec("A\nC\nC\n "), 3808);
+assertToStringEquals("", res[1298].exec("AB"), 3809);
+assertToStringEquals("", res[1298].exec("aB "), 3810);
+assertToStringEquals("", res[1298].exec("AB"), 3811);
+assertToStringEquals("", res[1298].exec("aB "), 3812);
+assertToStringEquals("", res[1298].exec("AB"), 3813);
+assertToStringEquals("", res[1298].exec("aB "), 3814);
+assertToStringEquals("", res[1298].exec("AB"), 3815);
+assertToStringEquals("", res[1298].exec("aB "), 3816);
+assertToStringEquals("Content-Type:xxxxxyyy ", res[1299].exec("Content-Type:xxxxxyyy "), 3817);
+assertToStringEquals("Content-Type:xxxxxyyyz", res[1300].exec("Content-Type:xxxxxyyyz"), 3818);
+assertToStringEquals("Content-Type:xxxyyy ", res[1301].exec("Content-Type:xxxyyy "), 3819);
+assertToStringEquals("Content-Type:xxxyyyz", res[1302].exec("Content-Type:xxxyyyz"), 3820);
+assertToStringEquals("abc", res[1303].exec("xyz\nabc"), 3821);
+assertToStringEquals("abc", res[1303].exec("xyz\nabc<lf>"), 3822);
+assertToStringEquals("abc", res[1303].exec("xyz\x0d\nabc<lf>"), 3823);
+assertToStringEquals("abc", res[1303].exec("xyz\x0dabc<cr>"), 3824);
+assertToStringEquals("abc", res[1303].exec("xyz\x0d\nabc<crlf>"), 3825);
+assertNull(res[1303].exec("** Failers ", 3826));
+assertToStringEquals("abc", res[1303].exec("xyz\nabc<cr>"), 3827);
+assertToStringEquals("abc", res[1303].exec("xyz\x0d\nabc<cr>"), 3828);
+assertToStringEquals("abc", res[1303].exec("xyz\nabc<crlf>"), 3829);
+assertToStringEquals("abc", res[1303].exec("xyz\x0dabc<crlf>"), 3830);
+assertToStringEquals("abc", res[1303].exec("xyz\x0dabc<lf>"), 3831);
+assertToStringEquals("abc", res[1304].exec("xyzabc"), 3832);
+assertToStringEquals("abc", res[1304].exec("xyzabc\n "), 3833);
+assertToStringEquals("abc", res[1304].exec("xyzabc\npqr "), 3834);
+assertToStringEquals("abc", res[1304].exec("xyzabc\x0d<cr> "), 3835);
+assertToStringEquals("abc", res[1304].exec("xyzabc\x0dpqr<cr> "), 3836);
+assertToStringEquals("abc", res[1304].exec("xyzabc\x0d\n<crlf> "), 3837);
+assertToStringEquals("abc", res[1304].exec("xyzabc\x0d\npqr<crlf> "), 3838);
+assertNull(res[1304].exec("** Failers", 3839));
+assertToStringEquals("abc", res[1304].exec("xyzabc\x0d "), 3840);
+assertToStringEquals("abc", res[1304].exec("xyzabc\x0dpqr "), 3841);
+assertToStringEquals("abc", res[1304].exec("xyzabc\x0d\n "), 3842);
+assertToStringEquals("abc", res[1304].exec("xyzabc\x0d\npqr "), 3843);
+assertToStringEquals("abc", res[1305].exec("xyz\x0dabcdef"), 3844);
+assertToStringEquals("abc", res[1305].exec("xyz\nabcdef<lf>"), 3845);
+assertNull(res[1305].exec("** Failers ", 3846));
+assertToStringEquals("abc", res[1305].exec("xyz\nabcdef"), 3847);
+assertNull(res[1305].exec(" ", 3848));
+assertToStringEquals("abc", res[1306].exec("xyz\nabcdef"), 3849);
+assertToStringEquals("abc", res[1306].exec("xyz\x0dabcdef<cr>"), 3850);
+assertNull(res[1306].exec("** Failers ", 3851));
+assertToStringEquals("abc", res[1306].exec("xyz\x0dabcdef"), 3852);
+assertNull(res[1306].exec(" ", 3853));
+assertToStringEquals("abc", res[1307].exec("xyz\x0d\nabcdef"), 3854);
+assertToStringEquals("abc", res[1307].exec("xyz\x0dabcdef<cr>"), 3855);
+assertNull(res[1307].exec("** Failers ", 3856));
+assertToStringEquals("abc", res[1307].exec("xyz\x0dabcdef"), 3857);
+assertToStringEquals("abc", res[1308].exec("abc\ndef"), 3858);
+assertToStringEquals("abc", res[1308].exec("abc\x0ddef"), 3859);
+assertToStringEquals("abc", res[1308].exec("abc\x0d\ndef"), 3860);
+assertToStringEquals("<cr>abc", res[1308].exec("<cr>abc\ndef"), 3861);
+assertToStringEquals("<cr>abc", res[1308].exec("<cr>abc\x0ddef"), 3862);
+assertToStringEquals("<cr>abc", res[1308].exec("<cr>abc\x0d\ndef"), 3863);
+assertToStringEquals("<crlf>abc", res[1308].exec("<crlf>abc\ndef"), 3864);
+assertToStringEquals("<crlf>abc", res[1308].exec("<crlf>abc\x0ddef"), 3865);
+assertToStringEquals("<crlf>abc", res[1308].exec("<crlf>abc\x0d\ndef"), 3866);
+assertNull(res[1309].exec("abc\ndef", 3867));
+assertNull(res[1309].exec("abc\x0ddef", 3868));
+assertNull(res[1309].exec("abc\x0d\ndef", 3869));
+assertToStringEquals("abc=xyz\\,", res[1310].exec("abc=xyz\\\npqr"), 3870);
+assertToStringEquals("aaaa,a,", res[1311].exec("aaaa"), 3871);
+assertToStringEquals("aaaa", res[1312].exec("aaaa"), 3872);
+assertToStringEquals("aaaa,a,", res[1313].exec("aaaa"), 3873);
+assertToStringEquals("aaaa", res[1314].exec("aaaa"), 3874);
+assertNull(res[1317].exec("a\x0db", 3875));
+assertNull(res[1317].exec("a\nb<cr> ", 3876));
+assertNull(res[1317].exec("** Failers", 3877));
+assertNull(res[1317].exec("a\nb", 3878));
+assertNull(res[1317].exec("a\nb<any>", 3879));
+assertNull(res[1317].exec("a\x0db<cr> ", 3880));
+assertNull(res[1317].exec("a\x0db<any> ", 3881));
+assertToStringEquals("abc1", res[1318].exec("abc1 \nabc2 \x0babc3xx \x0cabc4 \x0dabc5xx \x0d\nabc6 \x85abc7 JUNK"), 3882);
+assertToStringEquals("abc1", res[1319].exec("abc1\n abc2\x0b abc3\x0c abc4\x0d abc5\x0d\n abc6\x85 abc9"), 3883);
+assertNull(res[1320].exec("a\nb", 3884));
+assertNull(res[1320].exec("a\x0db", 3885));
+assertNull(res[1320].exec("a\x0d\nb", 3886));
+assertNull(res[1320].exec("a\x0bb", 3887));
+assertNull(res[1320].exec("a\x0cb", 3888));
+assertNull(res[1320].exec("a\x85b ", 3889));
+assertNull(res[1320].exec("** Failers", 3890));
+assertNull(res[1320].exec("a\n\x0db ", 3891));
+assertToStringEquals("ab", res[1321].exec("ab"), 3892);
+assertNull(res[1321].exec("a\nb", 3893));
+assertNull(res[1321].exec("a\x0db", 3894));
+assertNull(res[1321].exec("a\x0d\nb", 3895));
+assertNull(res[1321].exec("a\x0bb", 3896));
+assertNull(res[1321].exec("a\x0cb", 3897));
+assertNull(res[1321].exec("a\x85b ", 3898));
+assertNull(res[1321].exec("a\n\x0db ", 3899));
+assertNull(res[1321].exec("a\n\x0d\x85\x0cb ", 3900));
+assertNull(res[1322].exec("a\nb", 3901));
+assertNull(res[1322].exec("a\x0db", 3902));
+assertNull(res[1322].exec("a\x0d\nb", 3903));
+assertNull(res[1322].exec("a\x0bb", 3904));
+assertNull(res[1322].exec("a\x0cb", 3905));
+assertNull(res[1322].exec("a\x85b ", 3906));
+assertNull(res[1322].exec("a\n\x0db ", 3907));
+assertNull(res[1322].exec("a\n\x0d\x85\x0cb ", 3908));
+assertNull(res[1322].exec("** Failers", 3909));
+assertNull(res[1322].exec("ab ", 3910));
+assertNull(res[1323].exec("a\nb", 3911));
+assertNull(res[1323].exec("a\n\x0db", 3912));
+assertNull(res[1323].exec("a\n\x0d\x85b", 3913));
+assertNull(res[1323].exec("a\x0d\n\x0d\nb ", 3914));
+assertNull(res[1323].exec("a\x0d\n\x0d\n\x0d\nb ", 3915));
+assertNull(res[1323].exec("a\n\x0d\n\x0db", 3916));
+assertNull(res[1323].exec("a\n\n\x0d\nb ", 3917));
+assertNull(res[1323].exec("** Failers", 3918));
+assertNull(res[1323].exec("a\n\n\n\x0db", 3919));
+assertNull(res[1323].exec("a\x0d", 3920));
+assertToStringEquals("aRb", res[1324].exec("aRb"), 3921);
+assertNull(res[1324].exec("** Failers", 3922));
+assertNull(res[1324].exec("a\nb ", 3923));
+assertToStringEquals("afoo", res[1325].exec("afoo"), 3924);
+assertNull(res[1325].exec("** Failers ", 3925));
+assertNull(res[1325].exec("\x0d\nfoo ", 3926));
+assertNull(res[1325].exec("\nfoo ", 3927));
+assertToStringEquals("afoo", res[1326].exec("afoo"), 3928);
+assertNull(res[1326].exec("\nfoo ", 3929));
+assertNull(res[1326].exec("** Failers ", 3930));
+assertNull(res[1326].exec("\x0d\nfoo ", 3931));
+assertToStringEquals("afoo", res[1327].exec("afoo"), 3932);
+assertNull(res[1327].exec("** Failers ", 3933));
+assertNull(res[1327].exec("\nfoo ", 3934));
+assertNull(res[1327].exec("\x0d\nfoo ", 3935));
+assertToStringEquals("afoo", res[1328].exec("afoo"), 3936);
+assertNull(res[1328].exec("\x0d\nfoo ", 3937));
+assertNull(res[1328].exec("\nfoo ", 3938));
+assertToStringEquals("", res[1329].exec("abc\x0d\x0dxyz"), 3939);
+assertToStringEquals("", res[1329].exec("abc\n\x0dxyz "), 3940);
+assertNull(res[1329].exec("** Failers ", 3941));
+assertToStringEquals("", res[1329].exec("abc\x0d\nxyz"), 3942);
+assertToStringEquals("X", res[1330].exec("XABC"), 3943);
+assertNull(res[1330].exec("** Failers ", 3944));
+assertToStringEquals("X", res[1330].exec("XABCB"), 3945);
+assertNull(res[1330].exec("abc\x0d\n\x0d\n", 3946));
+assertNull(res[1330].exec("abc\x0d\n\x0d\n", 3947));
+assertNull(res[1330].exec("abc\x0d\n\x0d\n", 3948));
assertThrows("var re = /(?|(abc)|(xyz))/;", 3949);
assertThrows("var re = /(x)(?|(abc)|(xyz))(x)/;", 3950);
-assertEquals(null, res[1330].exec("xabcx", 3951));
-assertEquals(null, res[1330].exec("xxyzx ", 3952));
+assertNull(res[1330].exec("xabcx", 3951));
+assertNull(res[1330].exec("xxyzx ", 3952));
assertThrows("var re = /(x)(?|(abc)(pqr)|(xyz))(x)/;", 3953);
-assertEquals(null, res[1330].exec("xabcpqrx", 3954));
-assertEquals(null, res[1330].exec("xxyzx ", 3955));
-assertEquals(null, res[1330].exec("abcabc", 3956));
-assertEquals(null, res[1330].exec("xyzabc ", 3957));
-assertEquals(null, res[1330].exec("** Failers ", 3958));
-assertEquals(null, res[1330].exec("xyzxyz ", 3959));
-assertEquals(null, res[1331].exec("X X\n", 3960));
-assertEquals(null, res[1331].exec("X\x09X\x0b", 3961));
-assertEquals(null, res[1331].exec("** Failers", 3962));
-assertEquals(null, res[1331].exec("\xa0 X\n ", 3963));
-assertEquals(null, res[1332].exec("\x09 \xa0X\n\x0b\x0c\x0d\n", 3964));
-assertEquals(null, res[1332].exec("\x09 \xa0\n\x0b\x0c\x0d\n", 3965));
-assertEquals(null, res[1332].exec("\x09 \xa0\n\x0b\x0c", 3966));
-assertEquals(null, res[1332].exec("** Failers ", 3967));
-assertEquals(null, res[1332].exec("\x09 \xa0\n\x0b", 3968));
-assertEquals(null, res[1332].exec(" ", 3969));
-assertEquals(null, res[1333].exec("XY ABCDE", 3970));
-assertEquals(null, res[1333].exec("XY PQR ST ", 3971));
-assertEquals(null, res[1334].exec("XY AB PQRS", 3972));
-assertEquals(null, res[1335].exec(">XNNNYZ", 3973));
-assertEquals(null, res[1335].exec("> X NYQZ", 3974));
-assertEquals(null, res[1335].exec("** Failers", 3975));
-assertEquals(null, res[1335].exec(">XYZ ", 3976));
-assertEquals(null, res[1335].exec("> X NY Z", 3977));
-assertEquals(null, res[1336].exec(">XY\nZ\nA\x0bNN\x0c", 3978));
-assertEquals(null, res[1336].exec(">\n\x0dX\nY\n\x0bZZZ\nAAA\x0bNNN\x0c", 3979));
-assertEquals(null, res[1337].exec("\x0d\nA", 3980));
-assertEquals("\nA", res[1338].exec("\x0d\nA "), 3981);
-assertEquals("\nA", res[1339].exec("\x0d\nA "), 3982);
-assertEquals("\nA,\n", res[1340].exec("\x0d\nA "), 3983);
-assertEquals(null, res[1341].exec("a\x0db", 3984));
-assertEquals(null, res[1341].exec("a\nb", 3985));
-assertEquals(null, res[1341].exec("a\x0d\nb", 3986));
-assertEquals(null, res[1341].exec("** Failers", 3987));
-assertEquals(null, res[1341].exec("a\x85b", 3988));
-assertEquals(null, res[1341].exec("a\x0bb ", 3989));
-assertEquals(null, res[1342].exec("a\x0db", 3990));
-assertEquals(null, res[1342].exec("a\nb", 3991));
-assertEquals(null, res[1342].exec("a\x0d\nb", 3992));
-assertEquals(null, res[1342].exec("a\x85b", 3993));
-assertEquals(null, res[1342].exec("a\x0bb ", 3994));
-assertEquals(null, res[1342].exec("** Failers ", 3995));
-assertEquals(null, res[1342].exec("a\x85b<bsr_anycrlf>", 3996));
-assertEquals(null, res[1342].exec("a\x0bb<bsr_anycrlf>", 3997));
-assertEquals(null, res[1343].exec("a\x0db", 3998));
-assertEquals(null, res[1343].exec("a\nb", 3999));
-assertEquals(null, res[1343].exec("a\x0d\nb", 4000));
-assertEquals(null, res[1343].exec("** Failers", 4001));
-assertEquals(null, res[1343].exec("a\x85b", 4002));
-assertEquals(null, res[1343].exec("a\x0bb ", 4003));
-assertEquals(null, res[1344].exec("a\x0db", 4004));
-assertEquals(null, res[1344].exec("a\nb", 4005));
-assertEquals(null, res[1344].exec("a\x0d\nb", 4006));
-assertEquals(null, res[1344].exec("a\x85b", 4007));
-assertEquals(null, res[1344].exec("a\x0bb ", 4008));
-assertEquals(null, res[1344].exec("** Failers ", 4009));
-assertEquals(null, res[1344].exec("a\x85b<bsr_anycrlf>", 4010));
-assertEquals(null, res[1344].exec("a\x0bb<bsr_anycrlf>", 4011));
-assertEquals(null, res[1345].exec("a\x0d\n\nb", 4012));
-assertEquals(null, res[1345].exec("a\n\x0d\x0db", 4013));
-assertEquals(null, res[1345].exec("a\x0d\n\x0d\n\x0d\n\x0d\nb", 4014));
-assertEquals(null, res[1345].exec("** Failers", 4015));
-assertEquals(null, res[1345].exec("a\x8585b", 4016));
-assertEquals(null, res[1345].exec("a\x0b\x00bb ", 4017));
-assertEquals(null, res[1346].exec("a\x0d\x0db", 4018));
-assertEquals(null, res[1346].exec("a\n\n\nb", 4019));
-assertEquals(null, res[1346].exec("a\x0d\n\n\x0d\x0db", 4020));
-assertEquals(null, res[1346].exec("a\x8585b", 4021));
-assertEquals(null, res[1346].exec("a\x0b\x00bb ", 4022));
-assertEquals(null, res[1346].exec("** Failers ", 4023));
-assertEquals(null, res[1346].exec("a\x0d\x0d\x0d\x0d\x0db ", 4024));
-assertEquals(null, res[1346].exec("a\x8585b<bsr_anycrlf>", 4025));
-assertEquals(null, res[1346].exec("a\x0b\x00bb<bsr_anycrlf>", 4026));
-assertEquals("abc", res[1347].exec("abc "), 4027);
-assertEquals(null, res[1348].exec("** Failers", 4028));
-assertEquals(null, res[1348].exec("ab", 4029));
-assertEquals(null, res[1349].exec("** Failers", 4030));
-assertEquals(null, res[1349].exec("ab ", 4031));
-assertEquals(null, res[1349].exec("** Failers", 4032));
-assertEquals(null, res[1349].exec("ab ", 4033));
-assertEquals("aXb", res[1350].exec("aXb"), 4034);
-assertEquals("a\nb", res[1350].exec("a\nb "), 4035);
-assertEquals(null, res[1350].exec("** Failers", 4036));
-assertEquals(null, res[1350].exec("ab ", 4037));
-assertEquals("aXb", res[1351].exec("aXb"), 4038);
-assertEquals("a\nX\nXb", res[1351].exec("a\nX\nXb "), 4039);
-assertEquals(null, res[1351].exec("** Failers", 4040));
-assertEquals(null, res[1351].exec("ab ", 4041));
-assertEquals(null, res[1352].exec("ab", 4042));
-assertEquals(null, res[1352].exec("ax{100}b ", 4043));
-assertEquals(null, res[1352].exec("ax{100}x{100}b ", 4044));
-assertEquals(null, res[1352].exec("ax{100}b ", 4045));
-assertEquals(null, res[1352].exec("ax{100}x{100}b ", 4046));
-assertEquals(null, res[1352].exec("*** Failers ", 4047));
-assertEquals(null, res[1352].exec("ab", 4048));
-assertEquals(null, res[1352].exec(" ", 4049));
-assertEquals("X", res[1353].exec("Xoanon"), 4050);
-assertEquals("X", res[1353].exec("+Xoanon"), 4051);
-assertEquals("X", res[1353].exec("x{300}Xoanon "), 4052);
-assertEquals(null, res[1353].exec("*** Failers ", 4053));
-assertEquals(null, res[1353].exec("YXoanon ", 4054));
-assertEquals("X", res[1354].exec("YXoanon"), 4055);
-assertEquals(null, res[1354].exec("*** Failers", 4056));
-assertEquals(null, res[1354].exec("Xoanon", 4057));
-assertEquals(null, res[1354].exec("+Xoanon ", 4058));
-assertEquals(null, res[1354].exec("x{300}Xoanon ", 4059));
-assertEquals("X", res[1355].exec("X+oanon"), 4060);
-assertEquals(null, res[1355].exec("ZXx{300}oanon ", 4061));
-assertEquals("X", res[1355].exec("FAX "), 4062);
-assertEquals(null, res[1355].exec("*** Failers ", 4063));
-assertEquals(null, res[1355].exec("Xoanon ", 4064));
-assertEquals("X", res[1356].exec("Xoanon "), 4065);
-assertEquals(null, res[1356].exec("*** Failers", 4066));
-assertEquals(null, res[1356].exec("X+oanon", 4067));
-assertEquals("X", res[1356].exec("ZXx{300}oanon "), 4068);
-assertEquals(null, res[1356].exec("FAX ", 4069));
-assertEquals("b", res[1357].exec("abcd"), 4070);
-assertEquals("x", res[1357].exec("ax{100} "), 4071);
-assertEquals("b", res[1357].exec("ab99"), 4072);
-assertEquals("x", res[1357].exec("x{123}x{123}45"), 4073);
-assertEquals("x", res[1357].exec("x{400}x{401}x{402}6 "), 4074);
-assertEquals("*", res[1357].exec("*** Failers"), 4075);
-assertEquals("d", res[1357].exec("d99"), 4076);
-assertEquals("x", res[1357].exec("x{123}x{122}4 "), 4077);
-assertEquals("x", res[1357].exec("x{400}x{403}6 "), 4078);
-assertEquals("x", res[1357].exec("x{400}x{401}x{402}x{402}6 "), 4079);
-assertEquals(null, res[1358].exec("\ufffd]", 4080));
-assertEquals(null, res[1358].exec("\ufffd", 4081));
-assertEquals(null, res[1358].exec("\ufffd\ufffd\ufffd", 4082));
-assertEquals(null, res[1358].exec("\ufffd\ufffd\ufffd?", 4083));
-assertEquals("acb", res[1359].exec("acb"), 4084);
-assertEquals("ab", res[1359].exec("ab"), 4085);
-assertEquals(null, res[1359].exec("ax{100}b ", 4086));
-assertEquals(null, res[1359].exec("*** Failers", 4087));
-assertEquals(null, res[1359].exec("a\nb ", 4088));
-assertEquals(null, res[1360].exec("ax{4000}xyb ", 4089));
-assertEquals(null, res[1360].exec("ax{4000}yb ", 4090));
-assertEquals(null, res[1360].exec("ax{4000}x{100}yb ", 4091));
-assertEquals(null, res[1360].exec("*** Failers", 4092));
-assertEquals(null, res[1360].exec("ax{4000}b ", 4093));
-assertEquals(null, res[1360].exec("ac\ncb ", 4094));
-assertEquals("a\xc0,,\xc0", res[1361].exec("a\xc0\x88b"), 4095);
-assertEquals("ax,,x", res[1362].exec("ax{100}b"), 4096);
-assertEquals("a\xc0\x88b,\xc0\x88,b", res[1363].exec("a\xc0\x88b"), 4097);
-assertEquals("ax{100}b,x{100},b", res[1364].exec("ax{100}b"), 4098);
-assertEquals("a\xc0\x92,\xc0,\x92", res[1365].exec("a\xc0\x92bcd"), 4099);
-assertEquals("ax{,x,{", res[1366].exec("ax{240}bcd"), 4100);
-assertEquals("a\xc0\x92,\xc0,\x92", res[1367].exec("a\xc0\x92bcd"), 4101);
-assertEquals("ax{,x,{", res[1368].exec("ax{240}bcd"), 4102);
-assertEquals("a\xc0,,\xc0", res[1369].exec("a\xc0\x92bcd"), 4103);
-assertEquals("ax,,x", res[1370].exec("ax{240}bcd"), 4104);
-assertEquals(null, res[1371].exec("ax{1234}xyb ", 4105));
-assertEquals(null, res[1371].exec("ax{1234}x{4321}yb ", 4106));
-assertEquals(null, res[1371].exec("ax{1234}x{4321}x{3412}b ", 4107));
-assertEquals(null, res[1371].exec("*** Failers", 4108));
-assertEquals(null, res[1371].exec("ax{1234}b ", 4109));
-assertEquals(null, res[1371].exec("ac\ncb ", 4110));
-assertEquals("ax{1234}xyb,x{1234}xy", res[1372].exec("ax{1234}xyb "), 4111);
-assertEquals("ax{1234}x{4321}yb,x{1234}x{4321}y", res[1372].exec("ax{1234}x{4321}yb "), 4112);
-assertEquals("ax{1234}x{4321}x{3412}b,x{1234}x{4321}x{3412}", res[1372].exec("ax{1234}x{4321}x{3412}b "), 4113);
-assertEquals("axxxxbcdefghijb,xxxxbcdefghij", res[1372].exec("axxxxbcdefghijb "), 4114);
-assertEquals("ax{1234}x{4321}x{3412}x{3421}b,x{1234}x{4321}x{3412}x{3421}", res[1372].exec("ax{1234}x{4321}x{3412}x{3421}b "), 4115);
-assertEquals(null, res[1372].exec("*** Failers", 4116));
-assertEquals("ax{1234}b,x{1234}", res[1372].exec("ax{1234}b "), 4117);
-assertEquals("ax{1234}xyb,x{1234}xy", res[1373].exec("ax{1234}xyb "), 4118);
-assertEquals("ax{1234}x{4321}yb,x{1234}x{4321}y", res[1373].exec("ax{1234}x{4321}yb "), 4119);
-assertEquals("ax{1234}x{4321}x{3412}b,x{1234}x{4321}x{3412}", res[1373].exec("ax{1234}x{4321}x{3412}b "), 4120);
-assertEquals("axxxxb,xxxx", res[1373].exec("axxxxbcdefghijb "), 4121);
-assertEquals("ax{1234}x{4321}x{3412}x{3421}b,x{1234}x{4321}x{3412}x{3421}", res[1373].exec("ax{1234}x{4321}x{3412}x{3421}b "), 4122);
-assertEquals(null, res[1373].exec("*** Failers", 4123));
-assertEquals("ax{1234}b,x{1234}", res[1373].exec("ax{1234}b "), 4124);
-assertEquals(null, res[1374].exec("ax{1234}xyb ", 4125));
-assertEquals(null, res[1374].exec("ax{1234}x{4321}yb ", 4126));
-assertEquals(null, res[1374].exec("ax{1234}x{4321}x{3412}b ", 4127));
-assertEquals("axxxxb,xxxx", res[1374].exec("axxxxbcdefghijb "), 4128);
-assertEquals(null, res[1374].exec("ax{1234}x{4321}x{3412}x{3421}b ", 4129));
-assertEquals("axbxxb,xbxx", res[1374].exec("axbxxbcdefghijb "), 4130);
-assertEquals("axxxxxb,xxxxx", res[1374].exec("axxxxxbcdefghijb "), 4131);
-assertEquals(null, res[1374].exec("*** Failers", 4132));
-assertEquals(null, res[1374].exec("ax{1234}b ", 4133));
-assertEquals(null, res[1374].exec("axxxxxxbcdefghijb ", 4134));
-assertEquals(null, res[1375].exec("ax{1234}xyb ", 4135));
-assertEquals(null, res[1375].exec("ax{1234}x{4321}yb ", 4136));
-assertEquals(null, res[1375].exec("ax{1234}x{4321}x{3412}b ", 4137));
-assertEquals("axxxxb,xxxx", res[1375].exec("axxxxbcdefghijb "), 4138);
-assertEquals(null, res[1375].exec("ax{1234}x{4321}x{3412}x{3421}b ", 4139));
-assertEquals("axbxxb,xbxx", res[1375].exec("axbxxbcdefghijb "), 4140);
-assertEquals("axxxxxb,xxxxx", res[1375].exec("axxxxxbcdefghijb "), 4141);
-assertEquals(null, res[1375].exec("*** Failers", 4142));
-assertEquals(null, res[1375].exec("ax{1234}b ", 4143));
-assertEquals(null, res[1375].exec("axxxxxxbcdefghijb ", 4144));
-assertEquals(null, res[1375].exec("*** Failers", 4145));
-assertEquals(null, res[1375].exec("x{100}", 4146));
-assertEquals(null, res[1375].exec("aXbcd", 4147));
-assertEquals(null, res[1375].exec("ax{100}bcd", 4148));
-assertEquals(null, res[1375].exec("ax{100000}bcd", 4149));
-assertEquals(null, res[1375].exec("x{100}x{100}x{100}b", 4150));
-assertEquals(null, res[1375].exec("*** Failers ", 4151));
-assertEquals(null, res[1375].exec("x{100}x{100}b", 4152));
-assertEquals(null, res[1375].exec("x{ab} ", 4153));
-assertEquals(null, res[1375].exec("\xc2\xab", 4154));
-assertEquals(null, res[1375].exec("*** Failers ", 4155));
-assertEquals(null, res[1375].exec("\x00{ab}", 4156));
-assertEquals(null, res[1375].exec("WXYZ", 4157));
-assertEquals(null, res[1375].exec("x{256}XYZ ", 4158));
-assertEquals(null, res[1375].exec("*** Failers", 4159));
-assertEquals(null, res[1375].exec("XYZ ", 4160));
-assertEquals("bcd", res[1376].exec("bcd"), 4161);
-assertEquals("00}", res[1376].exec("x{100}aYx{256}Z "), 4162);
-assertEquals("x{", res[1377].exec("x{100}bc"), 4163);
-assertEquals("x{100}bcA", res[1378].exec("x{100}bcAa"), 4164);
-assertEquals("x{", res[1379].exec("x{100}bca"), 4165);
-assertEquals("bcd", res[1380].exec("bcd"), 4166);
-assertEquals("00}", res[1380].exec("x{100}aYx{256}Z "), 4167);
-assertEquals("x{", res[1381].exec("x{100}bc"), 4168);
-assertEquals("x{100}bc", res[1382].exec("x{100}bcAa"), 4169);
-assertEquals("x{", res[1383].exec("x{100}bca"), 4170);
-assertEquals(null, res[1383].exec("abcd", 4171));
-assertEquals(null, res[1383].exec("abcd", 4172));
-assertEquals("x{", res[1383].exec("x{100}x{100} "), 4173);
-assertEquals("x{", res[1383].exec("x{100}x{100} "), 4174);
-assertEquals("x{", res[1383].exec("x{100}x{100}x{100}x{100} "), 4175);
-assertEquals(null, res[1383].exec("abce", 4176));
-assertEquals("x{", res[1383].exec("x{100}x{100}x{100}x{100} "), 4177);
-assertEquals(null, res[1383].exec("abcdx{100}x{100}x{100}x{100} ", 4178));
-assertEquals(null, res[1383].exec("abcdx{100}x{100}x{100}x{100} ", 4179));
-assertEquals(null, res[1383].exec("abcdx{100}x{100}x{100}x{100} ", 4180));
-assertEquals(null, res[1383].exec("abcdx{100}x{100}x{100}XX", 4181));
-assertEquals(null, res[1383].exec("abcdx{100}x{100}x{100}x{100}x{100}x{100}x{100}XX", 4182));
-assertEquals(null, res[1383].exec("abcdx{100}x{100}x{100}x{100}x{100}x{100}x{100}XX", 4183));
-assertEquals("Xy", res[1383].exec("Xyyyax{100}x{100}bXzzz"), 4184);
-assertEquals("X", res[1386].exec("1X2"), 4185);
-assertEquals("x", res[1386].exec("1x{100}2 "), 4186);
-assertEquals(">X", res[1387].exec("> >X Y"), 4187);
-assertEquals(">x", res[1387].exec("> >x{100} Y"), 4188);
-assertEquals("1", res[1388].exec("x{100}3"), 4189);
-assertEquals(" ", res[1389].exec("x{100} X"), 4190);
-assertEquals("abcd", res[1390].exec("12abcd34"), 4191);
-assertEquals("*** Failers", res[1390].exec("*** Failers"), 4192);
-assertEquals(" ", res[1390].exec("1234 "), 4193);
-assertEquals("abc", res[1391].exec("12abcd34"), 4194);
-assertEquals("ab", res[1391].exec("12ab34"), 4195);
-assertEquals("***", res[1391].exec("*** Failers "), 4196);
-assertEquals(null, res[1391].exec("1234", 4197));
-assertEquals(" ", res[1391].exec("12a34 "), 4198);
-assertEquals("ab", res[1392].exec("12abcd34"), 4199);
-assertEquals("ab", res[1392].exec("12ab34"), 4200);
-assertEquals("**", res[1392].exec("*** Failers "), 4201);
-assertEquals(null, res[1392].exec("1234", 4202));
-assertEquals(" ", res[1392].exec("12a34 "), 4203);
-assertEquals("12", res[1393].exec("12abcd34"), 4204);
-assertEquals(null, res[1393].exec("*** Failers", 4205));
-assertEquals("12", res[1394].exec("12abcd34"), 4206);
-assertEquals("123", res[1394].exec("1234abcd"), 4207);
-assertEquals(null, res[1394].exec("*** Failers ", 4208));
-assertEquals(null, res[1394].exec("1.4 ", 4209));
-assertEquals("12", res[1395].exec("12abcd34"), 4210);
-assertEquals("12", res[1395].exec("1234abcd"), 4211);
-assertEquals(null, res[1395].exec("*** Failers ", 4212));
-assertEquals(null, res[1395].exec("1.4 ", 4213));
-assertEquals("12abcd34", res[1396].exec("12abcd34"), 4214);
-assertEquals("***", res[1396].exec("*** Failers"), 4215);
-assertEquals(null, res[1396].exec(" ", 4216));
-assertEquals("12a", res[1397].exec("12abcd34"), 4217);
-assertEquals("123", res[1397].exec("1234abcd"), 4218);
-assertEquals("***", res[1397].exec("*** Failers"), 4219);
-assertEquals(null, res[1397].exec(" ", 4220));
-assertEquals("12", res[1398].exec("12abcd34"), 4221);
-assertEquals("12", res[1398].exec("1234abcd"), 4222);
-assertEquals("**", res[1398].exec("*** Failers"), 4223);
-assertEquals(null, res[1398].exec(" ", 4224));
-assertEquals("> <", res[1399].exec("12> <34"), 4225);
-assertEquals(null, res[1399].exec("*** Failers", 4226));
-assertEquals("> <", res[1400].exec("ab> <cd"), 4227);
-assertEquals("> <", res[1400].exec("ab> <ce"), 4228);
-assertEquals(null, res[1400].exec("*** Failers", 4229));
-assertEquals(null, res[1400].exec("ab> <cd ", 4230));
-assertEquals("> <", res[1401].exec("ab> <cd"), 4231);
-assertEquals("> <", res[1401].exec("ab> <ce"), 4232);
-assertEquals(null, res[1401].exec("*** Failers", 4233));
-assertEquals(null, res[1401].exec("ab> <cd ", 4234));
-assertEquals("12", res[1402].exec("12 34"), 4235);
-assertEquals("Failers", res[1402].exec("*** Failers"), 4236);
-assertEquals(null, res[1402].exec("+++=*! ", 4237));
-assertEquals("ab", res[1403].exec("ab cd"), 4238);
-assertEquals("abc", res[1403].exec("abcd ce"), 4239);
-assertEquals("Fai", res[1403].exec("*** Failers"), 4240);
-assertEquals(null, res[1403].exec("a.b.c", 4241));
-assertEquals("ab", res[1404].exec("ab cd"), 4242);
-assertEquals("ab", res[1404].exec("abcd ce"), 4243);
-assertEquals("Fa", res[1404].exec("*** Failers"), 4244);
-assertEquals(null, res[1404].exec("a.b.c", 4245));
-assertEquals("====", res[1405].exec("12====34"), 4246);
-assertEquals("*** ", res[1405].exec("*** Failers"), 4247);
-assertEquals(" ", res[1405].exec("abcd "), 4248);
-assertEquals("===", res[1406].exec("ab====cd"), 4249);
-assertEquals("==", res[1406].exec("ab==cd"), 4250);
-assertEquals("***", res[1406].exec("*** Failers"), 4251);
-assertEquals(null, res[1406].exec("a.b.c", 4252));
-assertEquals("==", res[1407].exec("ab====cd"), 4253);
-assertEquals("==", res[1407].exec("ab==cd"), 4254);
-assertEquals("**", res[1407].exec("*** Failers"), 4255);
-assertEquals(null, res[1407].exec("a.b.c", 4256));
-assertEquals(null, res[1407].exec("x{100}", 4257));
-assertEquals(null, res[1407].exec("Zx{100}", 4258));
-assertEquals(null, res[1407].exec("x{100}Z", 4259));
-assertEquals("**", res[1407].exec("*** Failers "), 4260);
-assertEquals(null, res[1407].exec("Zx{100}", 4261));
-assertEquals(null, res[1407].exec("x{100}", 4262));
-assertEquals(null, res[1407].exec("x{100}Z", 4263));
-assertEquals("**", res[1407].exec("*** Failers "), 4264);
-assertEquals(null, res[1407].exec("abcx{200}X", 4265));
-assertEquals(null, res[1407].exec("abcx{100}X ", 4266));
-assertEquals("**", res[1407].exec("*** Failers"), 4267);
-assertEquals(" ", res[1407].exec("X "), 4268);
-assertEquals(null, res[1407].exec("abcx{200}X", 4269));
-assertEquals(null, res[1407].exec("abcx{100}X ", 4270));
-assertEquals(null, res[1407].exec("abQX ", 4271));
-assertEquals("**", res[1407].exec("*** Failers"), 4272);
-assertEquals(" ", res[1407].exec("X "), 4273);
-assertEquals(null, res[1407].exec("abcx{100}x{200}x{100}X", 4274));
-assertEquals("**", res[1407].exec("*** Failers"), 4275);
-assertEquals(null, res[1407].exec("abcx{200}X", 4276));
-assertEquals(" ", res[1407].exec("X "), 4277);
-assertEquals(null, res[1407].exec("AX", 4278));
-assertEquals(null, res[1407].exec("x{150}X", 4279));
-assertEquals(null, res[1407].exec("x{500}X ", 4280));
-assertEquals("**", res[1407].exec("*** Failers"), 4281);
-assertEquals(null, res[1407].exec("x{100}X", 4282));
-assertEquals(" ", res[1407].exec("x{200}X "), 4283);
-assertEquals(null, res[1407].exec("AX", 4284));
-assertEquals(null, res[1407].exec("x{150}X", 4285));
-assertEquals(null, res[1407].exec("x{500}X ", 4286));
-assertEquals("**", res[1407].exec("*** Failers"), 4287);
-assertEquals(null, res[1407].exec("x{100}X", 4288));
-assertEquals(" ", res[1407].exec("x{200}X "), 4289);
-assertEquals(null, res[1407].exec("QX ", 4290));
-assertEquals(null, res[1407].exec("AX", 4291));
-assertEquals(null, res[1407].exec("x{500}X ", 4292));
-assertEquals("**", res[1407].exec("*** Failers"), 4293);
-assertEquals(null, res[1407].exec("x{100}X", 4294));
-assertEquals(null, res[1407].exec("x{150}X", 4295));
-assertEquals(" ", res[1407].exec("x{200}X "), 4296);
-assertEquals(null, res[1407].exec("z", 4297));
-assertEquals(null, res[1407].exec("Z ", 4298));
-assertEquals(null, res[1407].exec("x{100}", 4299));
-assertEquals("**", res[1407].exec("*** Failers"), 4300);
-assertEquals(null, res[1407].exec("x{102}", 4301));
-assertEquals(" ", res[1407].exec("y "), 4302);
-assertEquals("\xff", res[1408].exec(">\xff<"), 4303);
-assertEquals(null, res[1409].exec(">x{ff}<", 4304));
-assertEquals("X", res[1410].exec("XYZ"), 4305);
-assertEquals("X", res[1411].exec("XYZ"), 4306);
-assertEquals("x", res[1411].exec("x{123} "), 4307);
-assertEquals(",", res[1416].exec("catac"), 4308);
-assertEquals(",", res[1416].exec("ax{256}a "), 4309);
-assertEquals(",", res[1416].exec("x{85}"), 4310);
-assertEquals("abc1", res[1417].exec("abc1 \nabc2 \x0babc3xx \x0cabc4 \x0dabc5xx \x0d\nabc6 x{0085}abc7 x{2028}abc8 x{2029}abc9 JUNK"), 4311);
-assertEquals("abc1", res[1418].exec("abc1\n abc2\x0b abc3\x0c abc4\x0d abc5\x0d\n abc6x{0085} abc7x{2028} abc8x{2029} abc9"), 4312);
-assertEquals(null, res[1419].exec("a\nb", 4313));
-assertEquals(null, res[1419].exec("a\x0db", 4314));
-assertEquals(null, res[1419].exec("a\x0d\nb", 4315));
-assertEquals(null, res[1419].exec("a\x0bb", 4316));
-assertEquals(null, res[1419].exec("a\x0cb", 4317));
-assertEquals(null, res[1419].exec("ax{85}b ", 4318));
-assertEquals(null, res[1419].exec("ax{2028}b ", 4319));
-assertEquals(null, res[1419].exec("ax{2029}b ", 4320));
-assertEquals(null, res[1419].exec("** Failers", 4321));
-assertEquals(null, res[1419].exec("a\n\x0db ", 4322));
-assertEquals("ab", res[1420].exec("ab"), 4323);
-assertEquals(null, res[1420].exec("a\nb", 4324));
-assertEquals(null, res[1420].exec("a\x0db", 4325));
-assertEquals(null, res[1420].exec("a\x0d\nb", 4326));
-assertEquals(null, res[1420].exec("a\x0bb", 4327));
-assertEquals(null, res[1420].exec("a\x0cx{2028}x{2029}b", 4328));
-assertEquals(null, res[1420].exec("ax{85}b ", 4329));
-assertEquals(null, res[1420].exec("a\n\x0db ", 4330));
-assertEquals(null, res[1420].exec("a\n\x0dx{85}\x0cb ", 4331));
-assertEquals(null, res[1421].exec("a\nb", 4332));
-assertEquals(null, res[1421].exec("a\x0db", 4333));
-assertEquals(null, res[1421].exec("a\x0d\nb", 4334));
-assertEquals(null, res[1421].exec("a\x0bb", 4335));
-assertEquals(null, res[1421].exec("a\x0cx{2028}x{2029}b", 4336));
-assertEquals(null, res[1421].exec("ax{85}b ", 4337));
-assertEquals(null, res[1421].exec("a\n\x0db ", 4338));
-assertEquals(null, res[1421].exec("a\n\x0dx{85}\x0cb ", 4339));
-assertEquals(null, res[1421].exec("** Failers", 4340));
-assertEquals(null, res[1421].exec("ab ", 4341));
-assertEquals(null, res[1422].exec("a\nb", 4342));
-assertEquals(null, res[1422].exec("a\n\x0db", 4343));
-assertEquals(null, res[1422].exec("a\n\x0dx{85}b", 4344));
-assertEquals(null, res[1422].exec("a\x0d\n\x0d\nb ", 4345));
-assertEquals(null, res[1422].exec("a\x0d\n\x0d\n\x0d\nb ", 4346));
-assertEquals(null, res[1422].exec("a\n\x0d\n\x0db", 4347));
-assertEquals(null, res[1422].exec("a\n\n\x0d\nb ", 4348));
-assertEquals(null, res[1422].exec("** Failers", 4349));
-assertEquals(null, res[1422].exec("a\n\n\n\x0db", 4350));
-assertEquals(null, res[1422].exec("a\x0d", 4351));
-assertEquals(null, res[1423].exec("\x09 x{a0}X\n\x0b\x0c\x0d\n", 4352));
-assertEquals(null, res[1424].exec(" x{a0}X\n\x0b\x0c\x0d\n", 4353));
-assertEquals(null, res[1425].exec(">\x09 x{a0}X\n\n\n<", 4354));
-assertEquals(null, res[1426].exec(">\x09 x{a0}X\n\n\n<", 4355));
-assertEquals(null, res[1427].exec("X X\n", 4356));
-assertEquals(null, res[1427].exec("X\x09X\x0b", 4357));
-assertEquals(null, res[1427].exec("** Failers", 4358));
-assertEquals(null, res[1427].exec("x{a0} X\n ", 4359));
-assertEquals(null, res[1428].exec("\x09 x{a0}X\n\x0b\x0c\x0d\n", 4360));
-assertEquals(null, res[1428].exec("\x09 x{a0}\n\x0b\x0c\x0d\n", 4361));
-assertEquals(null, res[1428].exec("\x09 x{a0}\n\x0b\x0c", 4362));
-assertEquals(null, res[1428].exec("** Failers ", 4363));
-assertEquals(null, res[1428].exec("\x09 x{a0}\n\x0b", 4364));
-assertEquals(null, res[1428].exec(" ", 4365));
-assertEquals(null, res[1429].exec("x{3001}x{3000}x{2030}x{2028}", 4366));
-assertEquals(null, res[1429].exec("Xx{180e}Xx{85}", 4367));
-assertEquals(null, res[1429].exec("** Failers", 4368));
-assertEquals(null, res[1429].exec("x{2009} X\n ", 4369));
-assertEquals(null, res[1430].exec("x{1680}x{180e}x{2007}Xx{2028}x{2029}\x0c\x0d\n", 4370));
-assertEquals(null, res[1430].exec("\x09x{205f}x{a0}\nx{2029}\x0cx{2028}\n", 4371));
-assertEquals(null, res[1430].exec("\x09 x{202f}\n\x0b\x0c", 4372));
-assertEquals(null, res[1430].exec("** Failers ", 4373));
-assertEquals(null, res[1430].exec("\x09x{200a}x{a0}x{2028}\x0b", 4374));
-assertEquals(null, res[1430].exec(" ", 4375));
-assertEquals(null, res[1431].exec("a\x0db", 4376));
-assertEquals(null, res[1431].exec("a\nb", 4377));
-assertEquals(null, res[1431].exec("a\x0d\nb", 4378));
-assertEquals(null, res[1431].exec("** Failers", 4379));
-assertEquals(null, res[1431].exec("ax{85}b", 4380));
-assertEquals(null, res[1431].exec("a\x0bb ", 4381));
-assertEquals(null, res[1432].exec("a\x0db", 4382));
-assertEquals(null, res[1432].exec("a\nb", 4383));
-assertEquals(null, res[1432].exec("a\x0d\nb", 4384));
-assertEquals(null, res[1432].exec("ax{85}b", 4385));
-assertEquals(null, res[1432].exec("a\x0bb ", 4386));
-assertEquals(null, res[1432].exec("** Failers ", 4387));
-assertEquals(null, res[1432].exec("ax{85}b<bsr_anycrlf>", 4388));
-assertEquals(null, res[1432].exec("a\x0bb<bsr_anycrlf>", 4389));
-assertEquals(null, res[1433].exec("a\x0db", 4390));
-assertEquals(null, res[1433].exec("a\nb", 4391));
-assertEquals(null, res[1433].exec("a\x0d\nb", 4392));
-assertEquals(null, res[1433].exec("** Failers", 4393));
-assertEquals(null, res[1433].exec("ax{85}b", 4394));
-assertEquals(null, res[1433].exec("a\x0bb ", 4395));
-assertEquals(null, res[1434].exec("a\x0db", 4396));
-assertEquals(null, res[1434].exec("a\nb", 4397));
-assertEquals(null, res[1434].exec("a\x0d\nb", 4398));
-assertEquals(null, res[1434].exec("ax{85}b", 4399));
-assertEquals(null, res[1434].exec("a\x0bb ", 4400));
-assertEquals(null, res[1434].exec("** Failers ", 4401));
-assertEquals(null, res[1434].exec("ax{85}b<bsr_anycrlf>", 4402));
-assertEquals(null, res[1434].exec("a\x0bb<bsr_anycrlf>", 4403));
-assertEquals("X", res[1435].exec("Ax{1ec5}ABCXYZ"), 4404);
-assertEquals(null, res[1437].exec("AB", 4405));
-assertEquals(null, res[1437].exec("*** Failers", 4406));
-assertEquals(null, res[1437].exec("A0", 4407));
-assertEquals(null, res[1437].exec("00 ", 4408));
-assertEquals(null, res[1438].exec("AB", 4409));
-assertEquals(null, res[1438].exec("Ax{300}BC ", 4410));
-assertEquals(null, res[1438].exec("Ax{300}x{301}x{302}BC ", 4411));
-assertEquals(null, res[1438].exec("*** Failers", 4412));
-assertEquals(null, res[1438].exec("x{300} ", 4413));
-assertEquals(null, res[1439].exec("ABC", 4414));
-assertEquals(null, res[1439].exec("Ax{300}Bx{300}x{301}C ", 4415));
-assertEquals(null, res[1439].exec("Ax{300}x{301}x{302}BC ", 4416));
-assertEquals(null, res[1439].exec("*** Failers", 4417));
-assertEquals(null, res[1439].exec("x{300} ", 4418));
-assertEquals(null, res[1440].exec("abcd", 4419));
-assertEquals(null, res[1440].exec("a ", 4420));
-assertEquals(null, res[1440].exec("*** Failers ", 4421));
-assertEquals(null, res[1441].exec("1234", 4422));
-assertEquals(null, res[1441].exec("= ", 4423));
-assertEquals(null, res[1441].exec("*** Failers ", 4424));
-assertEquals(null, res[1441].exec("abcd ", 4425));
-assertEquals(null, res[1442].exec("abcdAx{300}x{301}x{302}", 4426));
-assertEquals(null, res[1442].exec("Ax{300}x{301}x{302}", 4427));
-assertEquals(null, res[1442].exec("Ax{300}x{301}x{302}Ax{300}x{301}x{302}", 4428));
-assertEquals(null, res[1442].exec("a ", 4429));
-assertEquals(null, res[1442].exec("*** Failers ", 4430));
-assertEquals(null, res[1442].exec("x{300}x{301}x{302}", 4431));
-assertEquals("abc", res[1443].exec("abc"), 4432);
-assertEquals("abc", res[1443].exec("Ax{300}abc"), 4433);
-assertEquals("abc", res[1443].exec("Ax{300}x{301}x{302}Ax{300}Ax{300}Ax{300}abcxyz"), 4434);
-assertEquals("abc", res[1443].exec("x{300}abc "), 4435);
-assertEquals(null, res[1443].exec("*** Failers", 4436));
-assertEquals("abc", res[1444].exec("abc"), 4437);
-assertEquals(null, res[1444].exec("Ax{300}abc", 4438));
-assertEquals(null, res[1444].exec("*** Failers", 4439));
-assertEquals(null, res[1444].exec("Ax{300}x{301}x{302}Ax{300}Ax{300}Ax{300}abcxyz", 4440));
-assertEquals(null, res[1444].exec("x{300}abc ", 4441));
-assertEquals("abc", res[1445].exec("abc"), 4442);
-assertEquals("abc", res[1445].exec("Ax{300}abc"), 4443);
-assertEquals("abc", res[1445].exec("Ax{300}x{301}x{302}Ax{300}Ax{300}Ax{300}abcxyz"), 4444);
-assertEquals("abc", res[1445].exec("x{300}abc "), 4445);
-assertEquals(null, res[1445].exec("*** Failers", 4446));
-assertEquals("abc", res[1446].exec("abc"), 4447);
-assertEquals(null, res[1446].exec("Ax{300}abc", 4448));
-assertEquals(null, res[1446].exec("Ax{300}x{301}x{302}Ax{300}Ax{300}Ax{300}abcxyz", 4449));
-assertEquals(null, res[1446].exec("*** Failers", 4450));
-assertEquals(null, res[1446].exec("x{300}abc ", 4451));
-assertEquals(null, res[1447].exec("A=b", 4452));
-assertEquals(null, res[1447].exec("=c ", 4453));
-assertEquals(null, res[1447].exec("*** Failers", 4454));
-assertEquals(null, res[1447].exec("1=2 ", 4455));
-assertEquals(null, res[1447].exec("AAAA=b ", 4456));
-assertEquals(null, res[1448].exec("AAAA=b", 4457));
-assertEquals(null, res[1448].exec("=c ", 4458));
-assertEquals(null, res[1448].exec("*** Failers", 4459));
-assertEquals(null, res[1448].exec("1=2 ", 4460));
-assertEquals(null, res[1449].exec("Ax{300}x{301}x{302}Ax{300}x{301}x{302}X", 4461));
-assertEquals(null, res[1449].exec("Ax{300}x{301}x{302}Ax{300}x{301}x{302}Ax{300}x{301}x{302}X ", 4462));
-assertEquals(null, res[1449].exec("*** Failers", 4463));
-assertEquals(null, res[1449].exec("X", 4464));
-assertEquals(null, res[1449].exec("Ax{300}x{301}x{302}X", 4465));
-assertEquals(null, res[1449].exec("Ax{300}x{301}x{302}Ax{300}x{301}x{302}Ax{300}x{301}x{302}Ax{300}x{301}x{302}X", 4466));
-assertEquals(null, res[1450].exec("x{c0}x{30f}x{660}x{66c}x{f01}x{1680}<", 4467));
-assertEquals(null, res[1450].exec("\npx{300}9!$ < ", 4468));
-assertEquals(null, res[1450].exec("** Failers ", 4469));
-assertEquals(null, res[1450].exec("apx{300}9!$ < ", 4470));
-assertEquals(null, res[1451].exec("X", 4471));
-assertEquals(null, res[1451].exec("** Failers ", 4472));
-assertEquals(null, res[1451].exec("", 4473));
-assertEquals(null, res[1452].exec("9", 4474));
-assertEquals(null, res[1452].exec("** Failers ", 4475));
-assertEquals(null, res[1452].exec("x{c0}", 4476));
-assertEquals(null, res[1453].exec("X", 4477));
-assertEquals(null, res[1453].exec("** Failers ", 4478));
-assertEquals(null, res[1453].exec("x{30f}", 4479));
-assertEquals(null, res[1454].exec("X", 4480));
-assertEquals(null, res[1454].exec("** Failers ", 4481));
-assertEquals(null, res[1454].exec("x{660}", 4482));
-assertEquals(null, res[1455].exec("X", 4483));
-assertEquals(null, res[1455].exec("** Failers ", 4484));
-assertEquals(null, res[1455].exec("x{66c}", 4485));
-assertEquals(null, res[1456].exec("X", 4486));
-assertEquals(null, res[1456].exec("** Failers ", 4487));
-assertEquals(null, res[1456].exec("x{f01}", 4488));
-assertEquals(null, res[1457].exec("X", 4489));
-assertEquals(null, res[1457].exec("** Failers ", 4490));
-assertEquals(null, res[1457].exec("x{1680}", 4491));
-assertEquals(null, res[1458].exec("x{017}", 4492));
-assertEquals(null, res[1458].exec("x{09f} ", 4493));
-assertEquals(null, res[1458].exec("** Failers", 4494));
-assertEquals(null, res[1458].exec("x{0600} ", 4495));
-assertEquals(null, res[1459].exec("x{601}", 4496));
-assertEquals(null, res[1459].exec("** Failers", 4497));
-assertEquals(null, res[1459].exec("x{09f} ", 4498));
-assertEquals(null, res[1460].exec("** Failers", 4499));
-assertEquals(null, res[1460].exec("x{09f} ", 4500));
-assertEquals(null, res[1461].exec("x{f8ff}", 4501));
-assertEquals(null, res[1461].exec("** Failers", 4502));
-assertEquals(null, res[1461].exec("x{09f} ", 4503));
-assertEquals(null, res[1462].exec("?x{dfff}", 4504));
-assertEquals(null, res[1462].exec("** Failers", 4505));
-assertEquals(null, res[1462].exec("x{09f} ", 4506));
-assertEquals(null, res[1463].exec("a", 4507));
-assertEquals(null, res[1463].exec("** Failers ", 4508));
-assertEquals(null, res[1463].exec("Z", 4509));
-assertEquals(null, res[1463].exec("x{e000} ", 4510));
-assertEquals(null, res[1464].exec("x{2b0}", 4511));
-assertEquals(null, res[1464].exec("** Failers", 4512));
-assertEquals(null, res[1464].exec("a ", 4513));
-assertEquals(null, res[1465].exec("x{1bb}", 4514));
-assertEquals(null, res[1465].exec("** Failers", 4515));
-assertEquals(null, res[1465].exec("a ", 4516));
-assertEquals(null, res[1465].exec("x{2b0}", 4517));
-assertEquals(null, res[1466].exec("x{1c5}", 4518));
-assertEquals(null, res[1466].exec("** Failers", 4519));
-assertEquals(null, res[1466].exec("a ", 4520));
-assertEquals(null, res[1466].exec("x{2b0}", 4521));
-assertEquals(null, res[1467].exec("A", 4522));
-assertEquals(null, res[1467].exec("** Failers", 4523));
-assertEquals(null, res[1467].exec("x{2b0}", 4524));
-assertEquals(null, res[1468].exec("x{903}", 4525));
-assertEquals(null, res[1468].exec("** Failers", 4526));
-assertEquals(null, res[1468].exec("X", 4527));
-assertEquals(null, res[1468].exec("x{300}", 4528));
-assertEquals(null, res[1468].exec(" ", 4529));
-assertEquals(null, res[1469].exec("x{488}", 4530));
-assertEquals(null, res[1469].exec("** Failers", 4531));
-assertEquals(null, res[1469].exec("X", 4532));
-assertEquals(null, res[1469].exec("x{903}", 4533));
-assertEquals(null, res[1469].exec("x{300}", 4534));
-assertEquals(null, res[1470].exec("x{300}", 4535));
-assertEquals(null, res[1470].exec("** Failers", 4536));
-assertEquals(null, res[1470].exec("X", 4537));
-assertEquals(null, res[1470].exec("x{903}", 4538));
-assertEquals(null, res[1470].exec("0123456789x{660}x{661}x{662}x{663}x{664}x{665}x{666}x{667}x{668}x{669}x{66a}", 4539));
-assertEquals(null, res[1470].exec("x{6f0}x{6f1}x{6f2}x{6f3}x{6f4}x{6f5}x{6f6}x{6f7}x{6f8}x{6f9}x{6fa}", 4540));
-assertEquals(null, res[1470].exec("x{966}x{967}x{968}x{969}x{96a}x{96b}x{96c}x{96d}x{96e}x{96f}x{970}", 4541));
-assertEquals(null, res[1470].exec("** Failers", 4542));
-assertEquals(null, res[1470].exec("X", 4543));
-assertEquals(null, res[1471].exec("x{16ee}", 4544));
-assertEquals(null, res[1471].exec("** Failers", 4545));
-assertEquals(null, res[1471].exec("X", 4546));
-assertEquals(null, res[1471].exec("x{966}", 4547));
-assertEquals(null, res[1472].exec("x{b2}", 4548));
-assertEquals(null, res[1472].exec("x{b3}", 4549));
-assertEquals(null, res[1472].exec("** Failers", 4550));
-assertEquals(null, res[1472].exec("X", 4551));
-assertEquals(null, res[1472].exec("x{16ee}", 4552));
-assertEquals(null, res[1473].exec("_", 4553));
-assertEquals(null, res[1473].exec("x{203f}", 4554));
-assertEquals(null, res[1473].exec("** Failers", 4555));
-assertEquals(null, res[1473].exec("X", 4556));
-assertEquals(null, res[1473].exec("-", 4557));
-assertEquals(null, res[1473].exec("x{58a}", 4558));
-assertEquals(null, res[1474].exec("-", 4559));
-assertEquals(null, res[1474].exec("x{58a}", 4560));
-assertEquals(null, res[1474].exec("** Failers", 4561));
-assertEquals(null, res[1474].exec("X", 4562));
-assertEquals(null, res[1474].exec("x{203f}", 4563));
-assertEquals(null, res[1475].exec(")", 4564));
-assertEquals(null, res[1475].exec("]", 4565));
-assertEquals(null, res[1475].exec("}", 4566));
-assertEquals(null, res[1475].exec("x{f3b}", 4567));
-assertEquals(null, res[1475].exec("** Failers", 4568));
-assertEquals(null, res[1475].exec("X", 4569));
-assertEquals(null, res[1475].exec("x{203f}", 4570));
-assertEquals(null, res[1475].exec("(", 4571));
-assertEquals(null, res[1475].exec("[", 4572));
-assertEquals(null, res[1475].exec("{", 4573));
-assertEquals(null, res[1475].exec("x{f3c}", 4574));
-assertEquals(null, res[1476].exec("x{bb}", 4575));
-assertEquals(null, res[1476].exec("x{2019}", 4576));
-assertEquals(null, res[1476].exec("** Failers", 4577));
-assertEquals(null, res[1476].exec("X", 4578));
-assertEquals(null, res[1476].exec("x{203f}", 4579));
-assertEquals(null, res[1477].exec("x{ab}", 4580));
-assertEquals(null, res[1477].exec("x{2018}", 4581));
-assertEquals(null, res[1477].exec("** Failers", 4582));
-assertEquals(null, res[1477].exec("X", 4583));
-assertEquals(null, res[1477].exec("x{203f}", 4584));
-assertEquals(null, res[1478].exec("!", 4585));
-assertEquals(null, res[1478].exec("x{37e}", 4586));
-assertEquals(null, res[1478].exec("** Failers", 4587));
-assertEquals(null, res[1478].exec("X", 4588));
-assertEquals(null, res[1478].exec("x{203f}", 4589));
-assertEquals(null, res[1479].exec("(", 4590));
-assertEquals(null, res[1479].exec("[", 4591));
-assertEquals(null, res[1479].exec("{", 4592));
-assertEquals(null, res[1479].exec("x{f3c}", 4593));
-assertEquals(null, res[1479].exec("** Failers", 4594));
-assertEquals(null, res[1479].exec("X", 4595));
-assertEquals(null, res[1479].exec(")", 4596));
-assertEquals(null, res[1479].exec("]", 4597));
-assertEquals(null, res[1479].exec("}", 4598));
-assertEquals(null, res[1479].exec("x{f3b}", 4599));
-assertEquals(null, res[1479].exec("$x{a2}x{a3}x{a4}x{a5}x{a6}", 4600));
-assertEquals(null, res[1479].exec("x{9f2}", 4601));
-assertEquals(null, res[1479].exec("** Failers", 4602));
-assertEquals(null, res[1479].exec("X", 4603));
-assertEquals(null, res[1479].exec("x{2c2}", 4604));
-assertEquals(null, res[1480].exec("x{2c2}", 4605));
-assertEquals(null, res[1480].exec("** Failers", 4606));
-assertEquals(null, res[1480].exec("X", 4607));
-assertEquals(null, res[1480].exec("x{9f2}", 4608));
-assertEquals(null, res[1480].exec("+<|~x{ac}x{2044}", 4609));
-assertEquals(null, res[1480].exec("** Failers", 4610));
-assertEquals(null, res[1480].exec("X", 4611));
-assertEquals(null, res[1480].exec("x{9f2}", 4612));
-assertEquals(null, res[1481].exec("x{a6}", 4613));
-assertEquals(null, res[1481].exec("x{482} ", 4614));
-assertEquals(null, res[1481].exec("** Failers", 4615));
-assertEquals(null, res[1481].exec("X", 4616));
-assertEquals(null, res[1481].exec("x{9f2}", 4617));
-assertEquals(null, res[1482].exec("x{2028}", 4618));
-assertEquals(null, res[1482].exec("** Failers", 4619));
-assertEquals(null, res[1482].exec("X", 4620));
-assertEquals(null, res[1482].exec("x{2029}", 4621));
-assertEquals(null, res[1483].exec("x{2029}", 4622));
-assertEquals(null, res[1483].exec("** Failers", 4623));
-assertEquals(null, res[1483].exec("X", 4624));
-assertEquals(null, res[1483].exec("x{2028}", 4625));
-assertEquals(null, res[1484].exec("\\ \\", 4626));
-assertEquals(null, res[1484].exec("x{a0}", 4627));
-assertEquals(null, res[1484].exec("x{1680}", 4628));
-assertEquals(null, res[1484].exec("x{180e}", 4629));
-assertEquals(null, res[1484].exec("x{2000}", 4630));
-assertEquals(null, res[1484].exec("x{2001} ", 4631));
-assertEquals(null, res[1484].exec("** Failers", 4632));
-assertEquals(null, res[1484].exec("x{2028}", 4633));
-assertEquals(null, res[1484].exec("x{200d} ", 4634));
-assertEquals(null, res[1484].exec(" x{660}x{661}x{662}ABC", 4635));
-assertEquals(null, res[1484].exec(" x{660}x{661}x{662}ABC", 4636));
-assertEquals(null, res[1485].exec(" x{660}x{661}x{662}ABC", 4637));
-assertEquals(null, res[1486].exec(" x{660}x{661}x{662}ABC", 4638));
-assertEquals(null, res[1487].exec(" x{660}x{661}x{662}ABC", 4639));
-assertEquals(null, res[1488].exec(" x{660}x{661}x{662}ABC", 4640));
-assertEquals(null, res[1489].exec(" x{660}x{661}x{662}ABC", 4641));
-assertEquals(null, res[1490].exec(" x{660}x{661}x{662}ABC", 4642));
-assertEquals(null, res[1491].exec(" x{660}x{661}x{662}ABC", 4643));
-assertEquals(null, res[1492].exec(" x{660}x{661}x{662}ABC", 4644));
-assertEquals(null, res[1493].exec(" x{660}x{661}x{662}ABC", 4645));
-assertEquals(null, res[1493].exec(" x{660}x{661}x{662}ABC", 4646));
-assertEquals(null, res[1493].exec(" x{660}x{661}x{662}ABC", 4647));
-assertEquals(null, res[1493].exec(" ** Failers", 4648));
-assertEquals(null, res[1493].exec(" x{660}x{661}x{662}ABC", 4649));
-assertEquals(null, res[1494].exec("A", 4650));
-assertEquals(null, res[1494].exec("ax{10a0}B ", 4651));
-assertEquals(null, res[1494].exec("** Failers ", 4652));
-assertEquals(null, res[1494].exec("a", 4653));
-assertEquals(null, res[1494].exec("x{1d00} ", 4654));
-assertEquals(null, res[1495].exec("1234", 4655));
-assertEquals(null, res[1495].exec("** Failers", 4656));
-assertEquals(null, res[1495].exec("ABC ", 4657));
-assertEquals(null, res[1496].exec("1234", 4658));
-assertEquals(null, res[1496].exec("** Failers", 4659));
-assertEquals(null, res[1496].exec("ABC ", 4660));
-assertEquals(null, res[1496].exec("A2XYZ", 4661));
-assertEquals(null, res[1496].exec("123A5XYZPQR", 4662));
-assertEquals(null, res[1496].exec("ABAx{660}XYZpqr", 4663));
-assertEquals(null, res[1496].exec("** Failers", 4664));
-assertEquals(null, res[1496].exec("AXYZ", 4665));
-assertEquals(null, res[1496].exec("XYZ ", 4666));
-assertEquals(null, res[1496].exec("1XYZ", 4667));
-assertEquals(null, res[1496].exec("AB=XYZ.. ", 4668));
-assertEquals(null, res[1496].exec("XYZ ", 4669));
-assertEquals(null, res[1496].exec("** Failers", 4670));
-assertEquals(null, res[1496].exec("WXYZ ", 4671));
-assertEquals(null, res[1497].exec("1234", 4672));
-assertEquals(null, res[1497].exec("1234", 4673));
-assertEquals(null, res[1497].exec("12-34", 4674));
-assertEquals("{", res[1497].exec("12+x{661}-34 "), 4675);
-assertEquals(null, res[1497].exec("** Failers", 4676));
-assertEquals("d", res[1497].exec("abcd "), 4677);
-assertEquals("d", res[1498].exec("abcd"), 4678);
-assertEquals(null, res[1498].exec("** Failers", 4679));
-assertEquals(null, res[1498].exec("1234", 4680));
-assertEquals(null, res[1499].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 4681));
-assertEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[1499].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 4682);
-assertEquals(" ", res[1499].exec(" "), 4683);
-assertEquals(null, res[1499].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 4684));
-assertEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[1499].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 4685);
-assertEquals(null, res[1500].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 4686));
-assertEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[1500].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 4687);
-assertEquals(null, res[1501].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 4688));
-assertEquals(null, res[1501].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 4689));
-assertEquals(null, res[1502].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 4690));
-assertEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[1502].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 4691);
-assertEquals(null, res[1503].exec("a", 4692));
-assertEquals(null, res[1503].exec("A ", 4693));
-assertEquals(null, res[1504].exec("a", 4694));
-assertEquals(null, res[1504].exec("A ", 4695));
-assertEquals(null, res[1505].exec("A", 4696));
-assertEquals(null, res[1505].exec("aZ", 4697));
-assertEquals(null, res[1505].exec("** Failers", 4698));
-assertEquals(null, res[1505].exec("abc ", 4699));
-assertEquals(null, res[1506].exec("A", 4700));
-assertEquals(null, res[1506].exec("aZ", 4701));
-assertEquals(null, res[1506].exec("** Failers", 4702));
-assertEquals(null, res[1506].exec("abc ", 4703));
-assertEquals(null, res[1507].exec("a", 4704));
-assertEquals(null, res[1507].exec("Az", 4705));
-assertEquals(null, res[1507].exec("** Failers", 4706));
-assertEquals(null, res[1507].exec("ABC ", 4707));
-assertEquals(null, res[1508].exec("a", 4708));
-assertEquals(null, res[1508].exec("Az", 4709));
-assertEquals(null, res[1508].exec("** Failers", 4710));
-assertEquals(null, res[1508].exec("ABC ", 4711));
-assertEquals(null, res[1508].exec("x{c0}", 4712));
-assertEquals(null, res[1508].exec("x{e0} ", 4713));
-assertEquals(null, res[1508].exec("x{c0}", 4714));
-assertEquals(null, res[1508].exec("x{e0} ", 4715));
-assertEquals(null, res[1508].exec("Ax{391}x{10427}x{ff3a}x{1fb0}", 4716));
-assertEquals(null, res[1508].exec("** Failers", 4717));
-assertEquals(null, res[1508].exec("ax{391}x{10427}x{ff3a}x{1fb0} ", 4718));
-assertEquals(null, res[1508].exec("Ax{3b1}x{10427}x{ff3a}x{1fb0}", 4719));
-assertEquals(null, res[1508].exec("Ax{391}x{1044F}x{ff3a}x{1fb0}", 4720));
-assertEquals(null, res[1508].exec("Ax{391}x{10427}x{ff5a}x{1fb0}", 4721));
-assertEquals(null, res[1508].exec("Ax{391}x{10427}x{ff3a}x{1fb8}", 4722));
-assertEquals(null, res[1508].exec("Ax{391}x{10427}x{ff3a}x{1fb0}", 4723));
-assertEquals(null, res[1508].exec("ax{391}x{10427}x{ff3a}x{1fb0} ", 4724));
-assertEquals(null, res[1508].exec("Ax{3b1}x{10427}x{ff3a}x{1fb0}", 4725));
-assertEquals(null, res[1508].exec("Ax{391}x{1044F}x{ff3a}x{1fb0}", 4726));
-assertEquals(null, res[1508].exec("Ax{391}x{10427}x{ff5a}x{1fb0}", 4727));
-assertEquals(null, res[1508].exec("Ax{391}x{10427}x{ff3a}x{1fb8}", 4728));
-assertEquals(null, res[1508].exec("x{391}x{3b1}x{3b1}x{3b1}x{391}", 4729));
-assertEquals(null, res[1508].exec("x{391}x{3b1}x{3b1}x{3b1}x{391}X", 4730));
-assertEquals(null, res[1508].exec("x{391}x{3b1}x{3b1}x{3b1}x{391}X", 4731));
-assertEquals(null, res[1508].exec("x{391}", 4732));
-assertEquals(null, res[1508].exec("x{ff3a}", 4733));
-assertEquals(null, res[1508].exec("x{3b1}", 4734));
-assertEquals(null, res[1508].exec("x{ff5a} ", 4735));
-assertEquals(null, res[1508].exec("x{c0}", 4736));
-assertEquals(null, res[1508].exec("x{e0} ", 4737));
-assertEquals(null, res[1508].exec("x{104}", 4738));
-assertEquals(null, res[1508].exec("x{105}", 4739));
-assertEquals(null, res[1508].exec("x{109} ", 4740));
-assertEquals(null, res[1508].exec("** Failers", 4741));
-assertEquals(null, res[1508].exec("x{100}", 4742));
-assertEquals(null, res[1508].exec("x{10a} ", 4743));
-assertEquals(null, res[1508].exec("Z", 4744));
-assertEquals(null, res[1508].exec("z", 4745));
-assertEquals(null, res[1508].exec("x{39c}", 4746));
-assertEquals(null, res[1508].exec("x{178}", 4747));
-assertEquals(null, res[1508].exec("|", 4748));
-assertEquals(null, res[1508].exec("x{80}", 4749));
-assertEquals(null, res[1508].exec("x{ff}", 4750));
-assertEquals(null, res[1508].exec("x{100}", 4751));
-assertEquals(null, res[1508].exec("x{101} ", 4752));
-assertEquals(null, res[1508].exec("** Failers", 4753));
-assertEquals(null, res[1508].exec("x{102}", 4754));
-assertEquals(null, res[1508].exec("Y", 4755));
-assertEquals(null, res[1508].exec("y ", 4756));
-assertEquals(null, res[1509].exec("A", 4757));
-assertEquals(null, res[1509].exec("Ax{300}BC ", 4758));
-assertEquals(null, res[1509].exec("Ax{300}x{301}x{302}BC ", 4759));
-assertEquals(null, res[1509].exec("*** Failers", 4760));
-assertEquals(null, res[1509].exec("x{300} ", 4761));
-assertEquals("X", res[1510].exec("X123"), 4762);
-assertEquals(null, res[1510].exec("*** Failers", 4763));
-assertEquals(null, res[1510].exec("AXYZ", 4764));
-assertEquals(null, res[1511].exec("Ax{300}x{301}x{302}BCAx{300}x{301} ", 4765));
-assertEquals(null, res[1511].exec("Ax{300}x{301}x{302}BCAx{300}x{301}C ", 4766));
-assertEquals(null, res[1512].exec("Ax{300}x{301}x{302}BCAx{300}x{301} ", 4767));
-assertEquals(null, res[1512].exec("Ax{300}x{301}x{302}BCAx{300}x{301}C ", 4768));
-assertEquals("A,,A", res[1513].exec("Ax{300}x{301}x{302}BCAx{300}x{301} "), 4769);
-assertEquals("A,,A", res[1513].exec("Ax{300}x{301}x{302}BCAx{300}x{301}C "), 4770);
-assertEquals("A,,A", res[1514].exec("Ax{300}x{301}x{302}BCAx{300}x{301} "), 4771);
-assertEquals("A,,A", res[1514].exec("Ax{300}x{301}x{302}BCAx{300}x{301}C "), 4772);
-assertEquals(null, res[1515].exec("*** Failers", 4773));
-assertEquals(null, res[1515].exec("Ax{300}x{301}x{302}", 4774));
-assertEquals(null, res[1516].exec("Ax{300}x{301}Bx{300}X", 4775));
-assertEquals(null, res[1516].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}", 4776));
-assertEquals(null, res[1516].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}X", 4777));
-assertEquals(null, res[1516].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}DAx{300}X", 4778));
-assertEquals(null, res[1517].exec("Ax{300}x{301}Bx{300}X", 4779));
-assertEquals(null, res[1517].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}", 4780));
-assertEquals(null, res[1517].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}X", 4781));
-assertEquals(null, res[1517].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}DAx{300}X", 4782));
-assertEquals(null, res[1518].exec("12X", 4783));
-assertEquals(null, res[1518].exec("123X", 4784));
-assertEquals(null, res[1518].exec("*** Failers", 4785));
-assertEquals(null, res[1518].exec("X", 4786));
-assertEquals(null, res[1518].exec("1X", 4787));
-assertEquals(null, res[1518].exec("1234X ", 4788));
-assertEquals(null, res[1518].exec("x{100} ", 4789));
-assertEquals(null, res[1518].exec("x{101} ", 4790));
-assertEquals(null, res[1518].exec("x{2e81}x{3007}x{2f804}x{31a0}", 4791));
-assertEquals(null, res[1518].exec("** Failers", 4792));
-assertEquals(null, res[1518].exec("x{2e7f} ", 4793));
-assertEquals(null, res[1518].exec("x{3105}", 4794));
-assertEquals(null, res[1518].exec("** Failers", 4795));
-assertEquals(null, res[1518].exec("x{30ff} ", 4796));
-assertEquals(null, res[1519].exec("x{06e9}", 4797));
-assertEquals(null, res[1519].exec("x{060b}", 4798));
-assertEquals(null, res[1519].exec("** Failers", 4799));
-assertEquals(null, res[1519].exec("Xx{06e9} ", 4800));
-assertEquals(null, res[1520].exec("x{2f800}", 4801));
-assertEquals(null, res[1520].exec("** Failers", 4802));
-assertEquals(null, res[1520].exec("x{a014}", 4803));
-assertEquals(null, res[1520].exec("x{a4c6} ", 4804));
-assertEquals(null, res[1521].exec("AXYZ", 4805));
-assertEquals(null, res[1521].exec("x{1234}XYZ ", 4806));
-assertEquals(null, res[1521].exec("** Failers", 4807));
-assertEquals(null, res[1521].exec("X ", 4808));
-assertEquals(null, res[1522].exec("** Failers", 4809));
-assertEquals(null, res[1522].exec("AX", 4810));
-assertEquals(null, res[1523].exec("XYZ", 4811));
-assertEquals(null, res[1523].exec("AXYZ", 4812));
-assertEquals(null, res[1523].exec("x{1234}XYZ ", 4813));
-assertEquals(null, res[1523].exec("** Failers", 4814));
-assertEquals(null, res[1523].exec("ABXYZ ", 4815));
-assertEquals(null, res[1524].exec("XYZ", 4816));
-assertEquals(null, res[1524].exec("** Failers", 4817));
-assertEquals(null, res[1524].exec("AXYZ", 4818));
-assertEquals(null, res[1524].exec("x{1234}XYZ ", 4819));
-assertEquals(null, res[1524].exec("ABXYZ ", 4820));
-assertEquals(null, res[1524].exec("AXYZ", 4821));
-assertEquals(null, res[1524].exec("x{1234}XYZ", 4822));
-assertEquals(null, res[1524].exec("Ax{1234}XYZ", 4823));
-assertEquals(null, res[1524].exec("** Failers", 4824));
-assertEquals(null, res[1524].exec("XYZ", 4825));
-assertEquals(null, res[1524].exec("** Failers", 4826));
-assertEquals(null, res[1524].exec("AXYZ", 4827));
-assertEquals(null, res[1524].exec("x{1234}XYZ", 4828));
-assertEquals(null, res[1524].exec("Ax{1234}XYZ", 4829));
-assertEquals(null, res[1524].exec("XYZ", 4830));
-assertEquals(null, res[1525].exec("XYZ", 4831));
-assertEquals(null, res[1525].exec("AXYZ", 4832));
-assertEquals(null, res[1525].exec("x{1234}XYZ", 4833));
-assertEquals(null, res[1525].exec("Ax{1234}XYZ", 4834));
-assertEquals(null, res[1525].exec("** Failers", 4835));
-assertEquals(null, res[1526].exec("XYZ", 4836));
-assertEquals(null, res[1526].exec("** Failers", 4837));
-assertEquals(null, res[1526].exec("AXYZ", 4838));
-assertEquals(null, res[1526].exec("x{1234}XYZ", 4839));
-assertEquals(null, res[1526].exec("Ax{1234}XYZ", 4840));
-assertEquals("AX", res[1527].exec("AXYZ"), 4841);
-assertEquals(null, res[1527].exec("x{1234}XYZ ", 4842));
-assertEquals(null, res[1527].exec("** Failers", 4843));
-assertEquals(null, res[1527].exec("X ", 4844));
-assertEquals(null, res[1528].exec("** Failers", 4845));
-assertEquals("AX", res[1528].exec("AX"), 4846);
-assertEquals("X", res[1529].exec("XYZ"), 4847);
-assertEquals("AX", res[1529].exec("AXYZ"), 4848);
-assertEquals(null, res[1529].exec("x{1234}XYZ ", 4849));
-assertEquals(null, res[1529].exec("** Failers", 4850));
-assertEquals(null, res[1529].exec("ABXYZ ", 4851));
-assertEquals("X", res[1530].exec("XYZ"), 4852);
-assertEquals(null, res[1530].exec("** Failers", 4853));
-assertEquals("AX", res[1530].exec("AXYZ"), 4854);
-assertEquals(null, res[1530].exec("x{1234}XYZ ", 4855));
-assertEquals(null, res[1530].exec("ABXYZ ", 4856));
-assertEquals("AX", res[1531].exec("AXYZ"), 4857);
-assertEquals(null, res[1531].exec("x{1234}XYZ", 4858));
-assertEquals(null, res[1531].exec("Ax{1234}XYZ", 4859));
-assertEquals(null, res[1531].exec("** Failers", 4860));
-assertEquals(null, res[1531].exec("XYZ", 4861));
-assertEquals(null, res[1532].exec("** Failers", 4862));
-assertEquals("AX", res[1532].exec("AXYZ"), 4863);
-assertEquals(null, res[1532].exec("x{1234}XYZ", 4864));
-assertEquals(null, res[1532].exec("Ax{1234}XYZ", 4865));
-assertEquals(null, res[1532].exec("XYZ", 4866));
-assertEquals("X", res[1533].exec("XYZ"), 4867);
-assertEquals("AX", res[1533].exec("AXYZ"), 4868);
-assertEquals(null, res[1533].exec("x{1234}XYZ", 4869));
-assertEquals(null, res[1533].exec("Ax{1234}XYZ", 4870));
-assertEquals(null, res[1533].exec("** Failers", 4871));
-assertEquals("X", res[1534].exec("XYZ"), 4872);
-assertEquals(null, res[1534].exec("** Failers", 4873));
-assertEquals("AX", res[1534].exec("AXYZ"), 4874);
-assertEquals(null, res[1534].exec("x{1234}XYZ", 4875));
-assertEquals(null, res[1534].exec("Ax{1234}XYZ", 4876));
-assertEquals(null, res[1535].exec("abcdefgh", 4877));
-assertEquals(null, res[1535].exec("x{1234}\n\x0dx{3456}xyz ", 4878));
-assertEquals(null, res[1536].exec("abcdefgh", 4879));
-assertEquals(null, res[1536].exec("x{1234}\n\x0dx{3456}xyz ", 4880));
-assertEquals(null, res[1537].exec("** Failers", 4881));
-assertEquals(null, res[1537].exec("abcdefgh", 4882));
-assertEquals(null, res[1537].exec("x{1234}\n\x0dx{3456}xyz ", 4883));
-assertEquals(null, res[1538].exec(" AXY", 4884));
-assertEquals(null, res[1538].exec(" aXY", 4885));
-assertEquals(null, res[1538].exec(" x{1c5}XY", 4886));
-assertEquals(null, res[1538].exec(" ** Failers", 4887));
-assertEquals(null, res[1538].exec(" x{1bb}XY", 4888));
-assertEquals(null, res[1538].exec(" x{2b0}XY", 4889));
-assertEquals(null, res[1538].exec(" !XY ", 4890));
-assertEquals(null, res[1539].exec(" AXY", 4891));
-assertEquals(null, res[1539].exec(" aXY", 4892));
-assertEquals(null, res[1539].exec(" x{1c5}XY", 4893));
-assertEquals(null, res[1539].exec(" ** Failers", 4894));
-assertEquals(null, res[1539].exec(" x{1bb}XY", 4895));
-assertEquals(null, res[1539].exec(" x{2b0}XY", 4896));
-assertEquals(null, res[1539].exec(" !XY ", 4897));
-assertEquals(null, res[1539].exec(" AXY", 4898));
-assertEquals(null, res[1539].exec(" aXY", 4899));
-assertEquals(null, res[1539].exec(" AbcdeXyz ", 4900));
-assertEquals(null, res[1539].exec(" x{1c5}AbXY", 4901));
-assertEquals(null, res[1539].exec(" abcDEXypqreXlmn ", 4902));
-assertEquals(null, res[1539].exec(" ** Failers", 4903));
-assertEquals(null, res[1539].exec(" x{1bb}XY", 4904));
-assertEquals(null, res[1539].exec(" x{2b0}XY", 4905));
-assertEquals(null, res[1539].exec(" !XY ", 4906));
-assertEquals(null, res[1540].exec(" AXY", 4907));
-assertEquals(null, res[1540].exec(" aXY", 4908));
-assertEquals(null, res[1540].exec(" AbcdeXyz ", 4909));
-assertEquals(null, res[1540].exec(" x{1c5}AbXY", 4910));
-assertEquals(null, res[1540].exec(" abcDEXypqreXlmn ", 4911));
-assertEquals(null, res[1540].exec(" ** Failers", 4912));
-assertEquals(null, res[1540].exec(" x{1bb}XY", 4913));
-assertEquals(null, res[1540].exec(" x{2b0}XY", 4914));
-assertEquals(null, res[1540].exec(" !XY ", 4915));
-assertEquals(null, res[1540].exec(" AXY", 4916));
-assertEquals(null, res[1540].exec(" aXY", 4917));
-assertEquals(null, res[1540].exec(" AbcdeXyz ", 4918));
-assertEquals(null, res[1540].exec(" x{1c5}AbXY", 4919));
-assertEquals(null, res[1540].exec(" abcDEXypqreXlmn ", 4920));
-assertEquals(null, res[1540].exec(" ** Failers", 4921));
-assertEquals(null, res[1540].exec(" x{1bb}XY", 4922));
-assertEquals(null, res[1540].exec(" x{2b0}XY", 4923));
-assertEquals(null, res[1540].exec(" !XY ", 4924));
-assertEquals(null, res[1541].exec(" AXY", 4925));
-assertEquals(null, res[1541].exec(" aXY", 4926));
-assertEquals(null, res[1541].exec(" AbcdeXyz ", 4927));
-assertEquals(null, res[1541].exec(" x{1c5}AbXY", 4928));
-assertEquals(null, res[1541].exec(" abcDEXypqreXlmn ", 4929));
-assertEquals(null, res[1541].exec(" ** Failers", 4930));
-assertEquals(null, res[1541].exec(" x{1bb}XY", 4931));
-assertEquals(null, res[1541].exec(" x{2b0}XY", 4932));
-assertEquals(null, res[1541].exec(" !XY ", 4933));
-assertEquals(null, res[1542].exec(" !XY", 4934));
-assertEquals(null, res[1542].exec(" x{1bb}XY", 4935));
-assertEquals(null, res[1542].exec(" x{2b0}XY", 4936));
-assertEquals(null, res[1542].exec(" ** Failers", 4937));
-assertEquals(null, res[1542].exec(" x{1c5}XY", 4938));
-assertEquals(null, res[1542].exec(" AXY ", 4939));
-assertEquals(null, res[1543].exec(" !XY", 4940));
-assertEquals(null, res[1543].exec(" x{1bb}XY", 4941));
-assertEquals(null, res[1543].exec(" x{2b0}XY", 4942));
-assertEquals(null, res[1543].exec(" ** Failers", 4943));
-assertEquals(null, res[1543].exec(" x{1c5}XY", 4944));
-assertEquals(null, res[1543].exec(" AXY ", 4945));
-assertEquals(null, res[1543].exec("x{c0}x{e0}x{116}x{117}", 4946));
-assertEquals(null, res[1543].exec("x{c0}x{e0}x{116}x{117}", 4947));
-assertEquals(null, res[1545].exec("123abcdefg", 4948));
-assertEquals(null, res[1545].exec("123abc\xc4\xc5zz", 4949));
-assertEquals(null, res[1546].exec("x{102A4}x{AA52}x{A91D}x{1C46}x{10283}x{1092E}x{1C6B}x{A93B}x{A8BF}x{1BA0}x{A50A}====", 4950));
-assertEquals(null, res[1546].exec("x{a77d}x{1d79}", 4951));
-assertEquals(null, res[1546].exec("x{1d79}x{a77d} ", 4952));
-assertEquals(null, res[1546].exec("x{a77d}x{1d79}", 4953));
-assertEquals(null, res[1546].exec("** Failers ", 4954));
-assertEquals(null, res[1546].exec("x{1d79}x{a77d} ", 4955));
+assertNull(res[1330].exec("xabcpqrx", 3954));
+assertNull(res[1330].exec("xxyzx ", 3955));
+assertNull(res[1330].exec("abcabc", 3956));
+assertNull(res[1330].exec("xyzabc ", 3957));
+assertNull(res[1330].exec("** Failers ", 3958));
+assertNull(res[1330].exec("xyzxyz ", 3959));
+assertNull(res[1331].exec("X X\n", 3960));
+assertNull(res[1331].exec("X\x09X\x0b", 3961));
+assertNull(res[1331].exec("** Failers", 3962));
+assertNull(res[1331].exec("\xa0 X\n ", 3963));
+assertNull(res[1332].exec("\x09 \xa0X\n\x0b\x0c\x0d\n", 3964));
+assertNull(res[1332].exec("\x09 \xa0\n\x0b\x0c\x0d\n", 3965));
+assertNull(res[1332].exec("\x09 \xa0\n\x0b\x0c", 3966));
+assertNull(res[1332].exec("** Failers ", 3967));
+assertNull(res[1332].exec("\x09 \xa0\n\x0b", 3968));
+assertNull(res[1332].exec(" ", 3969));
+assertNull(res[1333].exec("XY ABCDE", 3970));
+assertNull(res[1333].exec("XY PQR ST ", 3971));
+assertNull(res[1334].exec("XY AB PQRS", 3972));
+assertNull(res[1335].exec(">XNNNYZ", 3973));
+assertNull(res[1335].exec("> X NYQZ", 3974));
+assertNull(res[1335].exec("** Failers", 3975));
+assertNull(res[1335].exec(">XYZ ", 3976));
+assertNull(res[1335].exec("> X NY Z", 3977));
+assertNull(res[1336].exec(">XY\nZ\nA\x0bNN\x0c", 3978));
+assertNull(res[1336].exec(">\n\x0dX\nY\n\x0bZZZ\nAAA\x0bNNN\x0c", 3979));
+assertNull(res[1337].exec("\x0d\nA", 3980));
+assertToStringEquals("\nA", res[1338].exec("\x0d\nA "), 3981);
+assertToStringEquals("\nA", res[1339].exec("\x0d\nA "), 3982);
+assertToStringEquals("\nA,\n", res[1340].exec("\x0d\nA "), 3983);
+assertNull(res[1341].exec("a\x0db", 3984));
+assertNull(res[1341].exec("a\nb", 3985));
+assertNull(res[1341].exec("a\x0d\nb", 3986));
+assertNull(res[1341].exec("** Failers", 3987));
+assertNull(res[1341].exec("a\x85b", 3988));
+assertNull(res[1341].exec("a\x0bb ", 3989));
+assertNull(res[1342].exec("a\x0db", 3990));
+assertNull(res[1342].exec("a\nb", 3991));
+assertNull(res[1342].exec("a\x0d\nb", 3992));
+assertNull(res[1342].exec("a\x85b", 3993));
+assertNull(res[1342].exec("a\x0bb ", 3994));
+assertNull(res[1342].exec("** Failers ", 3995));
+assertNull(res[1342].exec("a\x85b<bsr_anycrlf>", 3996));
+assertNull(res[1342].exec("a\x0bb<bsr_anycrlf>", 3997));
+assertNull(res[1343].exec("a\x0db", 3998));
+assertNull(res[1343].exec("a\nb", 3999));
+assertNull(res[1343].exec("a\x0d\nb", 4000));
+assertNull(res[1343].exec("** Failers", 4001));
+assertNull(res[1343].exec("a\x85b", 4002));
+assertNull(res[1343].exec("a\x0bb ", 4003));
+assertNull(res[1344].exec("a\x0db", 4004));
+assertNull(res[1344].exec("a\nb", 4005));
+assertNull(res[1344].exec("a\x0d\nb", 4006));
+assertNull(res[1344].exec("a\x85b", 4007));
+assertNull(res[1344].exec("a\x0bb ", 4008));
+assertNull(res[1344].exec("** Failers ", 4009));
+assertNull(res[1344].exec("a\x85b<bsr_anycrlf>", 4010));
+assertNull(res[1344].exec("a\x0bb<bsr_anycrlf>", 4011));
+assertNull(res[1345].exec("a\x0d\n\nb", 4012));
+assertNull(res[1345].exec("a\n\x0d\x0db", 4013));
+assertNull(res[1345].exec("a\x0d\n\x0d\n\x0d\n\x0d\nb", 4014));
+assertNull(res[1345].exec("** Failers", 4015));
+assertNull(res[1345].exec("a\x8585b", 4016));
+assertNull(res[1345].exec("a\x0b\x00bb ", 4017));
+assertNull(res[1346].exec("a\x0d\x0db", 4018));
+assertNull(res[1346].exec("a\n\n\nb", 4019));
+assertNull(res[1346].exec("a\x0d\n\n\x0d\x0db", 4020));
+assertNull(res[1346].exec("a\x8585b", 4021));
+assertNull(res[1346].exec("a\x0b\x00bb ", 4022));
+assertNull(res[1346].exec("** Failers ", 4023));
+assertNull(res[1346].exec("a\x0d\x0d\x0d\x0d\x0db ", 4024));
+assertNull(res[1346].exec("a\x8585b<bsr_anycrlf>", 4025));
+assertNull(res[1346].exec("a\x0b\x00bb<bsr_anycrlf>", 4026));
+assertToStringEquals("abc", res[1347].exec("abc "), 4027);
+assertNull(res[1348].exec("** Failers", 4028));
+assertNull(res[1348].exec("ab", 4029));
+assertNull(res[1349].exec("** Failers", 4030));
+assertNull(res[1349].exec("ab ", 4031));
+assertNull(res[1349].exec("** Failers", 4032));
+assertNull(res[1349].exec("ab ", 4033));
+assertToStringEquals("aXb", res[1350].exec("aXb"), 4034);
+assertToStringEquals("a\nb", res[1350].exec("a\nb "), 4035);
+assertNull(res[1350].exec("** Failers", 4036));
+assertNull(res[1350].exec("ab ", 4037));
+assertToStringEquals("aXb", res[1351].exec("aXb"), 4038);
+assertToStringEquals("a\nX\nXb", res[1351].exec("a\nX\nXb "), 4039);
+assertNull(res[1351].exec("** Failers", 4040));
+assertNull(res[1351].exec("ab ", 4041));
+assertNull(res[1352].exec("ab", 4042));
+assertNull(res[1352].exec("ax{100}b ", 4043));
+assertNull(res[1352].exec("ax{100}x{100}b ", 4044));
+assertNull(res[1352].exec("ax{100}b ", 4045));
+assertNull(res[1352].exec("ax{100}x{100}b ", 4046));
+assertNull(res[1352].exec("*** Failers ", 4047));
+assertNull(res[1352].exec("ab", 4048));
+assertNull(res[1352].exec(" ", 4049));
+assertToStringEquals("X", res[1353].exec("Xoanon"), 4050);
+assertToStringEquals("X", res[1353].exec("+Xoanon"), 4051);
+assertToStringEquals("X", res[1353].exec("x{300}Xoanon "), 4052);
+assertNull(res[1353].exec("*** Failers ", 4053));
+assertNull(res[1353].exec("YXoanon ", 4054));
+assertToStringEquals("X", res[1354].exec("YXoanon"), 4055);
+assertNull(res[1354].exec("*** Failers", 4056));
+assertNull(res[1354].exec("Xoanon", 4057));
+assertNull(res[1354].exec("+Xoanon ", 4058));
+assertNull(res[1354].exec("x{300}Xoanon ", 4059));
+assertToStringEquals("X", res[1355].exec("X+oanon"), 4060);
+assertNull(res[1355].exec("ZXx{300}oanon ", 4061));
+assertToStringEquals("X", res[1355].exec("FAX "), 4062);
+assertNull(res[1355].exec("*** Failers ", 4063));
+assertNull(res[1355].exec("Xoanon ", 4064));
+assertToStringEquals("X", res[1356].exec("Xoanon "), 4065);
+assertNull(res[1356].exec("*** Failers", 4066));
+assertNull(res[1356].exec("X+oanon", 4067));
+assertToStringEquals("X", res[1356].exec("ZXx{300}oanon "), 4068);
+assertNull(res[1356].exec("FAX ", 4069));
+assertToStringEquals("b", res[1357].exec("abcd"), 4070);
+assertToStringEquals("x", res[1357].exec("ax{100} "), 4071);
+assertToStringEquals("b", res[1357].exec("ab99"), 4072);
+assertToStringEquals("x", res[1357].exec("x{123}x{123}45"), 4073);
+assertToStringEquals("x", res[1357].exec("x{400}x{401}x{402}6 "), 4074);
+assertToStringEquals("*", res[1357].exec("*** Failers"), 4075);
+assertToStringEquals("d", res[1357].exec("d99"), 4076);
+assertToStringEquals("x", res[1357].exec("x{123}x{122}4 "), 4077);
+assertToStringEquals("x", res[1357].exec("x{400}x{403}6 "), 4078);
+assertToStringEquals("x", res[1357].exec("x{400}x{401}x{402}x{402}6 "), 4079);
+assertNull(res[1358].exec("\ufffd]", 4080));
+assertNull(res[1358].exec("\ufffd", 4081));
+assertNull(res[1358].exec("\ufffd\ufffd\ufffd", 4082));
+assertNull(res[1358].exec("\ufffd\ufffd\ufffd?", 4083));
+assertToStringEquals("acb", res[1359].exec("acb"), 4084);
+assertToStringEquals("ab", res[1359].exec("ab"), 4085);
+assertNull(res[1359].exec("ax{100}b ", 4086));
+assertNull(res[1359].exec("*** Failers", 4087));
+assertNull(res[1359].exec("a\nb ", 4088));
+assertNull(res[1360].exec("ax{4000}xyb ", 4089));
+assertNull(res[1360].exec("ax{4000}yb ", 4090));
+assertNull(res[1360].exec("ax{4000}x{100}yb ", 4091));
+assertNull(res[1360].exec("*** Failers", 4092));
+assertNull(res[1360].exec("ax{4000}b ", 4093));
+assertNull(res[1360].exec("ac\ncb ", 4094));
+assertToStringEquals("a\xc0,,\xc0", res[1361].exec("a\xc0\x88b"), 4095);
+assertToStringEquals("ax,,x", res[1362].exec("ax{100}b"), 4096);
+assertToStringEquals("a\xc0\x88b,\xc0\x88,b", res[1363].exec("a\xc0\x88b"), 4097);
+assertToStringEquals("ax{100}b,x{100},b", res[1364].exec("ax{100}b"), 4098);
+assertToStringEquals("a\xc0\x92,\xc0,\x92", res[1365].exec("a\xc0\x92bcd"), 4099);
+assertToStringEquals("ax{,x,{", res[1366].exec("ax{240}bcd"), 4100);
+assertToStringEquals("a\xc0\x92,\xc0,\x92", res[1367].exec("a\xc0\x92bcd"), 4101);
+assertToStringEquals("ax{,x,{", res[1368].exec("ax{240}bcd"), 4102);
+assertToStringEquals("a\xc0,,\xc0", res[1369].exec("a\xc0\x92bcd"), 4103);
+assertToStringEquals("ax,,x", res[1370].exec("ax{240}bcd"), 4104);
+assertNull(res[1371].exec("ax{1234}xyb ", 4105));
+assertNull(res[1371].exec("ax{1234}x{4321}yb ", 4106));
+assertNull(res[1371].exec("ax{1234}x{4321}x{3412}b ", 4107));
+assertNull(res[1371].exec("*** Failers", 4108));
+assertNull(res[1371].exec("ax{1234}b ", 4109));
+assertNull(res[1371].exec("ac\ncb ", 4110));
+assertToStringEquals("ax{1234}xyb,x{1234}xy", res[1372].exec("ax{1234}xyb "), 4111);
+assertToStringEquals("ax{1234}x{4321}yb,x{1234}x{4321}y", res[1372].exec("ax{1234}x{4321}yb "), 4112);
+assertToStringEquals("ax{1234}x{4321}x{3412}b,x{1234}x{4321}x{3412}", res[1372].exec("ax{1234}x{4321}x{3412}b "), 4113);
+assertToStringEquals("axxxxbcdefghijb,xxxxbcdefghij", res[1372].exec("axxxxbcdefghijb "), 4114);
+assertToStringEquals("ax{1234}x{4321}x{3412}x{3421}b,x{1234}x{4321}x{3412}x{3421}", res[1372].exec("ax{1234}x{4321}x{3412}x{3421}b "), 4115);
+assertNull(res[1372].exec("*** Failers", 4116));
+assertToStringEquals("ax{1234}b,x{1234}", res[1372].exec("ax{1234}b "), 4117);
+assertToStringEquals("ax{1234}xyb,x{1234}xy", res[1373].exec("ax{1234}xyb "), 4118);
+assertToStringEquals("ax{1234}x{4321}yb,x{1234}x{4321}y", res[1373].exec("ax{1234}x{4321}yb "), 4119);
+assertToStringEquals("ax{1234}x{4321}x{3412}b,x{1234}x{4321}x{3412}", res[1373].exec("ax{1234}x{4321}x{3412}b "), 4120);
+assertToStringEquals("axxxxb,xxxx", res[1373].exec("axxxxbcdefghijb "), 4121);
+assertToStringEquals("ax{1234}x{4321}x{3412}x{3421}b,x{1234}x{4321}x{3412}x{3421}", res[1373].exec("ax{1234}x{4321}x{3412}x{3421}b "), 4122);
+assertNull(res[1373].exec("*** Failers", 4123));
+assertToStringEquals("ax{1234}b,x{1234}", res[1373].exec("ax{1234}b "), 4124);
+assertNull(res[1374].exec("ax{1234}xyb ", 4125));
+assertNull(res[1374].exec("ax{1234}x{4321}yb ", 4126));
+assertNull(res[1374].exec("ax{1234}x{4321}x{3412}b ", 4127));
+assertToStringEquals("axxxxb,xxxx", res[1374].exec("axxxxbcdefghijb "), 4128);
+assertNull(res[1374].exec("ax{1234}x{4321}x{3412}x{3421}b ", 4129));
+assertToStringEquals("axbxxb,xbxx", res[1374].exec("axbxxbcdefghijb "), 4130);
+assertToStringEquals("axxxxxb,xxxxx", res[1374].exec("axxxxxbcdefghijb "), 4131);
+assertNull(res[1374].exec("*** Failers", 4132));
+assertNull(res[1374].exec("ax{1234}b ", 4133));
+assertNull(res[1374].exec("axxxxxxbcdefghijb ", 4134));
+assertNull(res[1375].exec("ax{1234}xyb ", 4135));
+assertNull(res[1375].exec("ax{1234}x{4321}yb ", 4136));
+assertNull(res[1375].exec("ax{1234}x{4321}x{3412}b ", 4137));
+assertToStringEquals("axxxxb,xxxx", res[1375].exec("axxxxbcdefghijb "), 4138);
+assertNull(res[1375].exec("ax{1234}x{4321}x{3412}x{3421}b ", 4139));
+assertToStringEquals("axbxxb,xbxx", res[1375].exec("axbxxbcdefghijb "), 4140);
+assertToStringEquals("axxxxxb,xxxxx", res[1375].exec("axxxxxbcdefghijb "), 4141);
+assertNull(res[1375].exec("*** Failers", 4142));
+assertNull(res[1375].exec("ax{1234}b ", 4143));
+assertNull(res[1375].exec("axxxxxxbcdefghijb ", 4144));
+assertNull(res[1375].exec("*** Failers", 4145));
+assertNull(res[1375].exec("x{100}", 4146));
+assertNull(res[1375].exec("aXbcd", 4147));
+assertNull(res[1375].exec("ax{100}bcd", 4148));
+assertNull(res[1375].exec("ax{100000}bcd", 4149));
+assertNull(res[1375].exec("x{100}x{100}x{100}b", 4150));
+assertNull(res[1375].exec("*** Failers ", 4151));
+assertNull(res[1375].exec("x{100}x{100}b", 4152));
+assertNull(res[1375].exec("x{ab} ", 4153));
+assertNull(res[1375].exec("\xc2\xab", 4154));
+assertNull(res[1375].exec("*** Failers ", 4155));
+assertNull(res[1375].exec("\x00{ab}", 4156));
+assertNull(res[1375].exec("WXYZ", 4157));
+assertNull(res[1375].exec("x{256}XYZ ", 4158));
+assertNull(res[1375].exec("*** Failers", 4159));
+assertNull(res[1375].exec("XYZ ", 4160));
+assertToStringEquals("bcd", res[1376].exec("bcd"), 4161);
+assertToStringEquals("00}", res[1376].exec("x{100}aYx{256}Z "), 4162);
+assertToStringEquals("x{", res[1377].exec("x{100}bc"), 4163);
+assertToStringEquals("x{100}bcA", res[1378].exec("x{100}bcAa"), 4164);
+assertToStringEquals("x{", res[1379].exec("x{100}bca"), 4165);
+assertToStringEquals("bcd", res[1380].exec("bcd"), 4166);
+assertToStringEquals("00}", res[1380].exec("x{100}aYx{256}Z "), 4167);
+assertToStringEquals("x{", res[1381].exec("x{100}bc"), 4168);
+assertToStringEquals("x{100}bc", res[1382].exec("x{100}bcAa"), 4169);
+assertToStringEquals("x{", res[1383].exec("x{100}bca"), 4170);
+assertNull(res[1383].exec("abcd", 4171));
+assertNull(res[1383].exec("abcd", 4172));
+assertToStringEquals("x{", res[1383].exec("x{100}x{100} "), 4173);
+assertToStringEquals("x{", res[1383].exec("x{100}x{100} "), 4174);
+assertToStringEquals("x{", res[1383].exec("x{100}x{100}x{100}x{100} "), 4175);
+assertNull(res[1383].exec("abce", 4176));
+assertToStringEquals("x{", res[1383].exec("x{100}x{100}x{100}x{100} "), 4177);
+assertNull(res[1383].exec("abcdx{100}x{100}x{100}x{100} ", 4178));
+assertNull(res[1383].exec("abcdx{100}x{100}x{100}x{100} ", 4179));
+assertNull(res[1383].exec("abcdx{100}x{100}x{100}x{100} ", 4180));
+assertNull(res[1383].exec("abcdx{100}x{100}x{100}XX", 4181));
+assertNull(res[1383].exec("abcdx{100}x{100}x{100}x{100}x{100}x{100}x{100}XX", 4182));
+assertNull(res[1383].exec("abcdx{100}x{100}x{100}x{100}x{100}x{100}x{100}XX", 4183));
+assertToStringEquals("Xy", res[1383].exec("Xyyyax{100}x{100}bXzzz"), 4184);
+assertToStringEquals("X", res[1386].exec("1X2"), 4185);
+assertToStringEquals("x", res[1386].exec("1x{100}2 "), 4186);
+assertToStringEquals(">X", res[1387].exec("> >X Y"), 4187);
+assertToStringEquals(">x", res[1387].exec("> >x{100} Y"), 4188);
+assertToStringEquals("1", res[1388].exec("x{100}3"), 4189);
+assertToStringEquals(" ", res[1389].exec("x{100} X"), 4190);
+assertToStringEquals("abcd", res[1390].exec("12abcd34"), 4191);
+assertToStringEquals("*** Failers", res[1390].exec("*** Failers"), 4192);
+assertToStringEquals(" ", res[1390].exec("1234 "), 4193);
+assertToStringEquals("abc", res[1391].exec("12abcd34"), 4194);
+assertToStringEquals("ab", res[1391].exec("12ab34"), 4195);
+assertToStringEquals("***", res[1391].exec("*** Failers "), 4196);
+assertNull(res[1391].exec("1234", 4197));
+assertToStringEquals(" ", res[1391].exec("12a34 "), 4198);
+assertToStringEquals("ab", res[1392].exec("12abcd34"), 4199);
+assertToStringEquals("ab", res[1392].exec("12ab34"), 4200);
+assertToStringEquals("**", res[1392].exec("*** Failers "), 4201);
+assertNull(res[1392].exec("1234", 4202));
+assertToStringEquals(" ", res[1392].exec("12a34 "), 4203);
+assertToStringEquals("12", res[1393].exec("12abcd34"), 4204);
+assertNull(res[1393].exec("*** Failers", 4205));
+assertToStringEquals("12", res[1394].exec("12abcd34"), 4206);
+assertToStringEquals("123", res[1394].exec("1234abcd"), 4207);
+assertNull(res[1394].exec("*** Failers ", 4208));
+assertNull(res[1394].exec("1.4 ", 4209));
+assertToStringEquals("12", res[1395].exec("12abcd34"), 4210);
+assertToStringEquals("12", res[1395].exec("1234abcd"), 4211);
+assertNull(res[1395].exec("*** Failers ", 4212));
+assertNull(res[1395].exec("1.4 ", 4213));
+assertToStringEquals("12abcd34", res[1396].exec("12abcd34"), 4214);
+assertToStringEquals("***", res[1396].exec("*** Failers"), 4215);
+assertNull(res[1396].exec(" ", 4216));
+assertToStringEquals("12a", res[1397].exec("12abcd34"), 4217);
+assertToStringEquals("123", res[1397].exec("1234abcd"), 4218);
+assertToStringEquals("***", res[1397].exec("*** Failers"), 4219);
+assertNull(res[1397].exec(" ", 4220));
+assertToStringEquals("12", res[1398].exec("12abcd34"), 4221);
+assertToStringEquals("12", res[1398].exec("1234abcd"), 4222);
+assertToStringEquals("**", res[1398].exec("*** Failers"), 4223);
+assertNull(res[1398].exec(" ", 4224));
+assertToStringEquals("> <", res[1399].exec("12> <34"), 4225);
+assertNull(res[1399].exec("*** Failers", 4226));
+assertToStringEquals("> <", res[1400].exec("ab> <cd"), 4227);
+assertToStringEquals("> <", res[1400].exec("ab> <ce"), 4228);
+assertNull(res[1400].exec("*** Failers", 4229));
+assertNull(res[1400].exec("ab> <cd ", 4230));
+assertToStringEquals("> <", res[1401].exec("ab> <cd"), 4231);
+assertToStringEquals("> <", res[1401].exec("ab> <ce"), 4232);
+assertNull(res[1401].exec("*** Failers", 4233));
+assertNull(res[1401].exec("ab> <cd ", 4234));
+assertToStringEquals("12", res[1402].exec("12 34"), 4235);
+assertToStringEquals("Failers", res[1402].exec("*** Failers"), 4236);
+assertNull(res[1402].exec("+++=*! ", 4237));
+assertToStringEquals("ab", res[1403].exec("ab cd"), 4238);
+assertToStringEquals("abc", res[1403].exec("abcd ce"), 4239);
+assertToStringEquals("Fai", res[1403].exec("*** Failers"), 4240);
+assertNull(res[1403].exec("a.b.c", 4241));
+assertToStringEquals("ab", res[1404].exec("ab cd"), 4242);
+assertToStringEquals("ab", res[1404].exec("abcd ce"), 4243);
+assertToStringEquals("Fa", res[1404].exec("*** Failers"), 4244);
+assertNull(res[1404].exec("a.b.c", 4245));
+assertToStringEquals("====", res[1405].exec("12====34"), 4246);
+assertToStringEquals("*** ", res[1405].exec("*** Failers"), 4247);
+assertToStringEquals(" ", res[1405].exec("abcd "), 4248);
+assertToStringEquals("===", res[1406].exec("ab====cd"), 4249);
+assertToStringEquals("==", res[1406].exec("ab==cd"), 4250);
+assertToStringEquals("***", res[1406].exec("*** Failers"), 4251);
+assertNull(res[1406].exec("a.b.c", 4252));
+assertToStringEquals("==", res[1407].exec("ab====cd"), 4253);
+assertToStringEquals("==", res[1407].exec("ab==cd"), 4254);
+assertToStringEquals("**", res[1407].exec("*** Failers"), 4255);
+assertNull(res[1407].exec("a.b.c", 4256));
+assertNull(res[1407].exec("x{100}", 4257));
+assertNull(res[1407].exec("Zx{100}", 4258));
+assertNull(res[1407].exec("x{100}Z", 4259));
+assertToStringEquals("**", res[1407].exec("*** Failers "), 4260);
+assertNull(res[1407].exec("Zx{100}", 4261));
+assertNull(res[1407].exec("x{100}", 4262));
+assertNull(res[1407].exec("x{100}Z", 4263));
+assertToStringEquals("**", res[1407].exec("*** Failers "), 4264);
+assertNull(res[1407].exec("abcx{200}X", 4265));
+assertNull(res[1407].exec("abcx{100}X ", 4266));
+assertToStringEquals("**", res[1407].exec("*** Failers"), 4267);
+assertToStringEquals(" ", res[1407].exec("X "), 4268);
+assertNull(res[1407].exec("abcx{200}X", 4269));
+assertNull(res[1407].exec("abcx{100}X ", 4270));
+assertNull(res[1407].exec("abQX ", 4271));
+assertToStringEquals("**", res[1407].exec("*** Failers"), 4272);
+assertToStringEquals(" ", res[1407].exec("X "), 4273);
+assertNull(res[1407].exec("abcx{100}x{200}x{100}X", 4274));
+assertToStringEquals("**", res[1407].exec("*** Failers"), 4275);
+assertNull(res[1407].exec("abcx{200}X", 4276));
+assertToStringEquals(" ", res[1407].exec("X "), 4277);
+assertNull(res[1407].exec("AX", 4278));
+assertNull(res[1407].exec("x{150}X", 4279));
+assertNull(res[1407].exec("x{500}X ", 4280));
+assertToStringEquals("**", res[1407].exec("*** Failers"), 4281);
+assertNull(res[1407].exec("x{100}X", 4282));
+assertToStringEquals(" ", res[1407].exec("x{200}X "), 4283);
+assertNull(res[1407].exec("AX", 4284));
+assertNull(res[1407].exec("x{150}X", 4285));
+assertNull(res[1407].exec("x{500}X ", 4286));
+assertToStringEquals("**", res[1407].exec("*** Failers"), 4287);
+assertNull(res[1407].exec("x{100}X", 4288));
+assertToStringEquals(" ", res[1407].exec("x{200}X "), 4289);
+assertNull(res[1407].exec("QX ", 4290));
+assertNull(res[1407].exec("AX", 4291));
+assertNull(res[1407].exec("x{500}X ", 4292));
+assertToStringEquals("**", res[1407].exec("*** Failers"), 4293);
+assertNull(res[1407].exec("x{100}X", 4294));
+assertNull(res[1407].exec("x{150}X", 4295));
+assertToStringEquals(" ", res[1407].exec("x{200}X "), 4296);
+assertNull(res[1407].exec("z", 4297));
+assertNull(res[1407].exec("Z ", 4298));
+assertNull(res[1407].exec("x{100}", 4299));
+assertToStringEquals("**", res[1407].exec("*** Failers"), 4300);
+assertNull(res[1407].exec("x{102}", 4301));
+assertToStringEquals(" ", res[1407].exec("y "), 4302);
+assertToStringEquals("\xff", res[1408].exec(">\xff<"), 4303);
+assertNull(res[1409].exec(">x{ff}<", 4304));
+assertToStringEquals("X", res[1410].exec("XYZ"), 4305);
+assertToStringEquals("X", res[1411].exec("XYZ"), 4306);
+assertToStringEquals("x", res[1411].exec("x{123} "), 4307);
+assertToStringEquals(",", res[1416].exec("catac"), 4308);
+assertToStringEquals(",", res[1416].exec("ax{256}a "), 4309);
+assertToStringEquals(",", res[1416].exec("x{85}"), 4310);
+assertToStringEquals("abc1", res[1417].exec("abc1 \nabc2 \x0babc3xx \x0cabc4 \x0dabc5xx \x0d\nabc6 x{0085}abc7 x{2028}abc8 x{2029}abc9 JUNK"), 4311);
+assertToStringEquals("abc1", res[1418].exec("abc1\n abc2\x0b abc3\x0c abc4\x0d abc5\x0d\n abc6x{0085} abc7x{2028} abc8x{2029} abc9"), 4312);
+assertNull(res[1419].exec("a\nb", 4313));
+assertNull(res[1419].exec("a\x0db", 4314));
+assertNull(res[1419].exec("a\x0d\nb", 4315));
+assertNull(res[1419].exec("a\x0bb", 4316));
+assertNull(res[1419].exec("a\x0cb", 4317));
+assertNull(res[1419].exec("ax{85}b ", 4318));
+assertNull(res[1419].exec("ax{2028}b ", 4319));
+assertNull(res[1419].exec("ax{2029}b ", 4320));
+assertNull(res[1419].exec("** Failers", 4321));
+assertNull(res[1419].exec("a\n\x0db ", 4322));
+assertToStringEquals("ab", res[1420].exec("ab"), 4323);
+assertNull(res[1420].exec("a\nb", 4324));
+assertNull(res[1420].exec("a\x0db", 4325));
+assertNull(res[1420].exec("a\x0d\nb", 4326));
+assertNull(res[1420].exec("a\x0bb", 4327));
+assertNull(res[1420].exec("a\x0cx{2028}x{2029}b", 4328));
+assertNull(res[1420].exec("ax{85}b ", 4329));
+assertNull(res[1420].exec("a\n\x0db ", 4330));
+assertNull(res[1420].exec("a\n\x0dx{85}\x0cb ", 4331));
+assertNull(res[1421].exec("a\nb", 4332));
+assertNull(res[1421].exec("a\x0db", 4333));
+assertNull(res[1421].exec("a\x0d\nb", 4334));
+assertNull(res[1421].exec("a\x0bb", 4335));
+assertNull(res[1421].exec("a\x0cx{2028}x{2029}b", 4336));
+assertNull(res[1421].exec("ax{85}b ", 4337));
+assertNull(res[1421].exec("a\n\x0db ", 4338));
+assertNull(res[1421].exec("a\n\x0dx{85}\x0cb ", 4339));
+assertNull(res[1421].exec("** Failers", 4340));
+assertNull(res[1421].exec("ab ", 4341));
+assertNull(res[1422].exec("a\nb", 4342));
+assertNull(res[1422].exec("a\n\x0db", 4343));
+assertNull(res[1422].exec("a\n\x0dx{85}b", 4344));
+assertNull(res[1422].exec("a\x0d\n\x0d\nb ", 4345));
+assertNull(res[1422].exec("a\x0d\n\x0d\n\x0d\nb ", 4346));
+assertNull(res[1422].exec("a\n\x0d\n\x0db", 4347));
+assertNull(res[1422].exec("a\n\n\x0d\nb ", 4348));
+assertNull(res[1422].exec("** Failers", 4349));
+assertNull(res[1422].exec("a\n\n\n\x0db", 4350));
+assertNull(res[1422].exec("a\x0d", 4351));
+assertNull(res[1423].exec("\x09 x{a0}X\n\x0b\x0c\x0d\n", 4352));
+assertNull(res[1424].exec(" x{a0}X\n\x0b\x0c\x0d\n", 4353));
+assertNull(res[1425].exec(">\x09 x{a0}X\n\n\n<", 4354));
+assertNull(res[1426].exec(">\x09 x{a0}X\n\n\n<", 4355));
+assertNull(res[1427].exec("X X\n", 4356));
+assertNull(res[1427].exec("X\x09X\x0b", 4357));
+assertNull(res[1427].exec("** Failers", 4358));
+assertNull(res[1427].exec("x{a0} X\n ", 4359));
+assertNull(res[1428].exec("\x09 x{a0}X\n\x0b\x0c\x0d\n", 4360));
+assertNull(res[1428].exec("\x09 x{a0}\n\x0b\x0c\x0d\n", 4361));
+assertNull(res[1428].exec("\x09 x{a0}\n\x0b\x0c", 4362));
+assertNull(res[1428].exec("** Failers ", 4363));
+assertNull(res[1428].exec("\x09 x{a0}\n\x0b", 4364));
+assertNull(res[1428].exec(" ", 4365));
+assertNull(res[1429].exec("x{3001}x{3000}x{2030}x{2028}", 4366));
+assertNull(res[1429].exec("Xx{180e}Xx{85}", 4367));
+assertNull(res[1429].exec("** Failers", 4368));
+assertNull(res[1429].exec("x{2009} X\n ", 4369));
+assertNull(res[1430].exec("x{1680}x{180e}x{2007}Xx{2028}x{2029}\x0c\x0d\n", 4370));
+assertNull(res[1430].exec("\x09x{205f}x{a0}\nx{2029}\x0cx{2028}\n", 4371));
+assertNull(res[1430].exec("\x09 x{202f}\n\x0b\x0c", 4372));
+assertNull(res[1430].exec("** Failers ", 4373));
+assertNull(res[1430].exec("\x09x{200a}x{a0}x{2028}\x0b", 4374));
+assertNull(res[1430].exec(" ", 4375));
+assertNull(res[1431].exec("a\x0db", 4376));
+assertNull(res[1431].exec("a\nb", 4377));
+assertNull(res[1431].exec("a\x0d\nb", 4378));
+assertNull(res[1431].exec("** Failers", 4379));
+assertNull(res[1431].exec("ax{85}b", 4380));
+assertNull(res[1431].exec("a\x0bb ", 4381));
+assertNull(res[1432].exec("a\x0db", 4382));
+assertNull(res[1432].exec("a\nb", 4383));
+assertNull(res[1432].exec("a\x0d\nb", 4384));
+assertNull(res[1432].exec("ax{85}b", 4385));
+assertNull(res[1432].exec("a\x0bb ", 4386));
+assertNull(res[1432].exec("** Failers ", 4387));
+assertNull(res[1432].exec("ax{85}b<bsr_anycrlf>", 4388));
+assertNull(res[1432].exec("a\x0bb<bsr_anycrlf>", 4389));
+assertNull(res[1433].exec("a\x0db", 4390));
+assertNull(res[1433].exec("a\nb", 4391));
+assertNull(res[1433].exec("a\x0d\nb", 4392));
+assertNull(res[1433].exec("** Failers", 4393));
+assertNull(res[1433].exec("ax{85}b", 4394));
+assertNull(res[1433].exec("a\x0bb ", 4395));
+assertNull(res[1434].exec("a\x0db", 4396));
+assertNull(res[1434].exec("a\nb", 4397));
+assertNull(res[1434].exec("a\x0d\nb", 4398));
+assertNull(res[1434].exec("ax{85}b", 4399));
+assertNull(res[1434].exec("a\x0bb ", 4400));
+assertNull(res[1434].exec("** Failers ", 4401));
+assertNull(res[1434].exec("ax{85}b<bsr_anycrlf>", 4402));
+assertNull(res[1434].exec("a\x0bb<bsr_anycrlf>", 4403));
+assertToStringEquals("X", res[1435].exec("Ax{1ec5}ABCXYZ"), 4404);
+assertNull(res[1437].exec("AB", 4405));
+assertNull(res[1437].exec("*** Failers", 4406));
+assertNull(res[1437].exec("A0", 4407));
+assertNull(res[1437].exec("00 ", 4408));
+assertNull(res[1438].exec("AB", 4409));
+assertNull(res[1438].exec("Ax{300}BC ", 4410));
+assertNull(res[1438].exec("Ax{300}x{301}x{302}BC ", 4411));
+assertNull(res[1438].exec("*** Failers", 4412));
+assertNull(res[1438].exec("x{300} ", 4413));
+assertNull(res[1439].exec("ABC", 4414));
+assertNull(res[1439].exec("Ax{300}Bx{300}x{301}C ", 4415));
+assertNull(res[1439].exec("Ax{300}x{301}x{302}BC ", 4416));
+assertNull(res[1439].exec("*** Failers", 4417));
+assertNull(res[1439].exec("x{300} ", 4418));
+assertNull(res[1440].exec("abcd", 4419));
+assertNull(res[1440].exec("a ", 4420));
+assertNull(res[1440].exec("*** Failers ", 4421));
+assertNull(res[1441].exec("1234", 4422));
+assertNull(res[1441].exec("= ", 4423));
+assertNull(res[1441].exec("*** Failers ", 4424));
+assertNull(res[1441].exec("abcd ", 4425));
+assertNull(res[1442].exec("abcdAx{300}x{301}x{302}", 4426));
+assertNull(res[1442].exec("Ax{300}x{301}x{302}", 4427));
+assertNull(res[1442].exec("Ax{300}x{301}x{302}Ax{300}x{301}x{302}", 4428));
+assertNull(res[1442].exec("a ", 4429));
+assertNull(res[1442].exec("*** Failers ", 4430));
+assertNull(res[1442].exec("x{300}x{301}x{302}", 4431));
+assertToStringEquals("abc", res[1443].exec("abc"), 4432);
+assertToStringEquals("abc", res[1443].exec("Ax{300}abc"), 4433);
+assertToStringEquals("abc", res[1443].exec("Ax{300}x{301}x{302}Ax{300}Ax{300}Ax{300}abcxyz"), 4434);
+assertToStringEquals("abc", res[1443].exec("x{300}abc "), 4435);
+assertNull(res[1443].exec("*** Failers", 4436));
+assertToStringEquals("abc", res[1444].exec("abc"), 4437);
+assertNull(res[1444].exec("Ax{300}abc", 4438));
+assertNull(res[1444].exec("*** Failers", 4439));
+assertNull(res[1444].exec("Ax{300}x{301}x{302}Ax{300}Ax{300}Ax{300}abcxyz", 4440));
+assertNull(res[1444].exec("x{300}abc ", 4441));
+assertToStringEquals("abc", res[1445].exec("abc"), 4442);
+assertToStringEquals("abc", res[1445].exec("Ax{300}abc"), 4443);
+assertToStringEquals("abc", res[1445].exec("Ax{300}x{301}x{302}Ax{300}Ax{300}Ax{300}abcxyz"), 4444);
+assertToStringEquals("abc", res[1445].exec("x{300}abc "), 4445);
+assertNull(res[1445].exec("*** Failers", 4446));
+assertToStringEquals("abc", res[1446].exec("abc"), 4447);
+assertNull(res[1446].exec("Ax{300}abc", 4448));
+assertNull(res[1446].exec("Ax{300}x{301}x{302}Ax{300}Ax{300}Ax{300}abcxyz", 4449));
+assertNull(res[1446].exec("*** Failers", 4450));
+assertNull(res[1446].exec("x{300}abc ", 4451));
+assertNull(res[1447].exec("A=b", 4452));
+assertNull(res[1447].exec("=c ", 4453));
+assertNull(res[1447].exec("*** Failers", 4454));
+assertNull(res[1447].exec("1=2 ", 4455));
+assertNull(res[1447].exec("AAAA=b ", 4456));
+assertNull(res[1448].exec("AAAA=b", 4457));
+assertNull(res[1448].exec("=c ", 4458));
+assertNull(res[1448].exec("*** Failers", 4459));
+assertNull(res[1448].exec("1=2 ", 4460));
+assertNull(res[1449].exec("Ax{300}x{301}x{302}Ax{300}x{301}x{302}X", 4461));
+assertNull(res[1449].exec("Ax{300}x{301}x{302}Ax{300}x{301}x{302}Ax{300}x{301}x{302}X ", 4462));
+assertNull(res[1449].exec("*** Failers", 4463));
+assertNull(res[1449].exec("X", 4464));
+assertNull(res[1449].exec("Ax{300}x{301}x{302}X", 4465));
+assertNull(res[1449].exec("Ax{300}x{301}x{302}Ax{300}x{301}x{302}Ax{300}x{301}x{302}Ax{300}x{301}x{302}X", 4466));
+assertNull(res[1450].exec("x{c0}x{30f}x{660}x{66c}x{f01}x{1680}<", 4467));
+assertNull(res[1450].exec("\npx{300}9!$ < ", 4468));
+assertNull(res[1450].exec("** Failers ", 4469));
+assertNull(res[1450].exec("apx{300}9!$ < ", 4470));
+assertNull(res[1451].exec("X", 4471));
+assertNull(res[1451].exec("** Failers ", 4472));
+assertNull(res[1451].exec("", 4473));
+assertNull(res[1452].exec("9", 4474));
+assertNull(res[1452].exec("** Failers ", 4475));
+assertNull(res[1452].exec("x{c0}", 4476));
+assertNull(res[1453].exec("X", 4477));
+assertNull(res[1453].exec("** Failers ", 4478));
+assertNull(res[1453].exec("x{30f}", 4479));
+assertNull(res[1454].exec("X", 4480));
+assertNull(res[1454].exec("** Failers ", 4481));
+assertNull(res[1454].exec("x{660}", 4482));
+assertNull(res[1455].exec("X", 4483));
+assertNull(res[1455].exec("** Failers ", 4484));
+assertNull(res[1455].exec("x{66c}", 4485));
+assertNull(res[1456].exec("X", 4486));
+assertNull(res[1456].exec("** Failers ", 4487));
+assertNull(res[1456].exec("x{f01}", 4488));
+assertNull(res[1457].exec("X", 4489));
+assertNull(res[1457].exec("** Failers ", 4490));
+assertNull(res[1457].exec("x{1680}", 4491));
+assertNull(res[1458].exec("x{017}", 4492));
+assertNull(res[1458].exec("x{09f} ", 4493));
+assertNull(res[1458].exec("** Failers", 4494));
+assertNull(res[1458].exec("x{0600} ", 4495));
+assertNull(res[1459].exec("x{601}", 4496));
+assertNull(res[1459].exec("** Failers", 4497));
+assertNull(res[1459].exec("x{09f} ", 4498));
+assertNull(res[1460].exec("** Failers", 4499));
+assertNull(res[1460].exec("x{09f} ", 4500));
+assertNull(res[1461].exec("x{f8ff}", 4501));
+assertNull(res[1461].exec("** Failers", 4502));
+assertNull(res[1461].exec("x{09f} ", 4503));
+assertNull(res[1462].exec("?x{dfff}", 4504));
+assertNull(res[1462].exec("** Failers", 4505));
+assertNull(res[1462].exec("x{09f} ", 4506));
+assertNull(res[1463].exec("a", 4507));
+assertNull(res[1463].exec("** Failers ", 4508));
+assertNull(res[1463].exec("Z", 4509));
+assertNull(res[1463].exec("x{e000} ", 4510));
+assertNull(res[1464].exec("x{2b0}", 4511));
+assertNull(res[1464].exec("** Failers", 4512));
+assertNull(res[1464].exec("a ", 4513));
+assertNull(res[1465].exec("x{1bb}", 4514));
+assertNull(res[1465].exec("** Failers", 4515));
+assertNull(res[1465].exec("a ", 4516));
+assertNull(res[1465].exec("x{2b0}", 4517));
+assertNull(res[1466].exec("x{1c5}", 4518));
+assertNull(res[1466].exec("** Failers", 4519));
+assertNull(res[1466].exec("a ", 4520));
+assertNull(res[1466].exec("x{2b0}", 4521));
+assertNull(res[1467].exec("A", 4522));
+assertNull(res[1467].exec("** Failers", 4523));
+assertNull(res[1467].exec("x{2b0}", 4524));
+assertNull(res[1468].exec("x{903}", 4525));
+assertNull(res[1468].exec("** Failers", 4526));
+assertNull(res[1468].exec("X", 4527));
+assertNull(res[1468].exec("x{300}", 4528));
+assertNull(res[1468].exec(" ", 4529));
+assertNull(res[1469].exec("x{488}", 4530));
+assertNull(res[1469].exec("** Failers", 4531));
+assertNull(res[1469].exec("X", 4532));
+assertNull(res[1469].exec("x{903}", 4533));
+assertNull(res[1469].exec("x{300}", 4534));
+assertNull(res[1470].exec("x{300}", 4535));
+assertNull(res[1470].exec("** Failers", 4536));
+assertNull(res[1470].exec("X", 4537));
+assertNull(res[1470].exec("x{903}", 4538));
+assertNull(res[1470].exec("0123456789x{660}x{661}x{662}x{663}x{664}x{665}x{666}x{667}x{668}x{669}x{66a}", 4539));
+assertNull(res[1470].exec("x{6f0}x{6f1}x{6f2}x{6f3}x{6f4}x{6f5}x{6f6}x{6f7}x{6f8}x{6f9}x{6fa}", 4540));
+assertNull(res[1470].exec("x{966}x{967}x{968}x{969}x{96a}x{96b}x{96c}x{96d}x{96e}x{96f}x{970}", 4541));
+assertNull(res[1470].exec("** Failers", 4542));
+assertNull(res[1470].exec("X", 4543));
+assertNull(res[1471].exec("x{16ee}", 4544));
+assertNull(res[1471].exec("** Failers", 4545));
+assertNull(res[1471].exec("X", 4546));
+assertNull(res[1471].exec("x{966}", 4547));
+assertNull(res[1472].exec("x{b2}", 4548));
+assertNull(res[1472].exec("x{b3}", 4549));
+assertNull(res[1472].exec("** Failers", 4550));
+assertNull(res[1472].exec("X", 4551));
+assertNull(res[1472].exec("x{16ee}", 4552));
+assertNull(res[1473].exec("_", 4553));
+assertNull(res[1473].exec("x{203f}", 4554));
+assertNull(res[1473].exec("** Failers", 4555));
+assertNull(res[1473].exec("X", 4556));
+assertNull(res[1473].exec("-", 4557));
+assertNull(res[1473].exec("x{58a}", 4558));
+assertNull(res[1474].exec("-", 4559));
+assertNull(res[1474].exec("x{58a}", 4560));
+assertNull(res[1474].exec("** Failers", 4561));
+assertNull(res[1474].exec("X", 4562));
+assertNull(res[1474].exec("x{203f}", 4563));
+assertNull(res[1475].exec(")", 4564));
+assertNull(res[1475].exec("]", 4565));
+assertNull(res[1475].exec("}", 4566));
+assertNull(res[1475].exec("x{f3b}", 4567));
+assertNull(res[1475].exec("** Failers", 4568));
+assertNull(res[1475].exec("X", 4569));
+assertNull(res[1475].exec("x{203f}", 4570));
+assertNull(res[1475].exec("(", 4571));
+assertNull(res[1475].exec("[", 4572));
+assertNull(res[1475].exec("{", 4573));
+assertNull(res[1475].exec("x{f3c}", 4574));
+assertNull(res[1476].exec("x{bb}", 4575));
+assertNull(res[1476].exec("x{2019}", 4576));
+assertNull(res[1476].exec("** Failers", 4577));
+assertNull(res[1476].exec("X", 4578));
+assertNull(res[1476].exec("x{203f}", 4579));
+assertNull(res[1477].exec("x{ab}", 4580));
+assertNull(res[1477].exec("x{2018}", 4581));
+assertNull(res[1477].exec("** Failers", 4582));
+assertNull(res[1477].exec("X", 4583));
+assertNull(res[1477].exec("x{203f}", 4584));
+assertNull(res[1478].exec("!", 4585));
+assertNull(res[1478].exec("x{37e}", 4586));
+assertNull(res[1478].exec("** Failers", 4587));
+assertNull(res[1478].exec("X", 4588));
+assertNull(res[1478].exec("x{203f}", 4589));
+assertNull(res[1479].exec("(", 4590));
+assertNull(res[1479].exec("[", 4591));
+assertNull(res[1479].exec("{", 4592));
+assertNull(res[1479].exec("x{f3c}", 4593));
+assertNull(res[1479].exec("** Failers", 4594));
+assertNull(res[1479].exec("X", 4595));
+assertNull(res[1479].exec(")", 4596));
+assertNull(res[1479].exec("]", 4597));
+assertNull(res[1479].exec("}", 4598));
+assertNull(res[1479].exec("x{f3b}", 4599));
+assertNull(res[1479].exec("$x{a2}x{a3}x{a4}x{a5}x{a6}", 4600));
+assertNull(res[1479].exec("x{9f2}", 4601));
+assertNull(res[1479].exec("** Failers", 4602));
+assertNull(res[1479].exec("X", 4603));
+assertNull(res[1479].exec("x{2c2}", 4604));
+assertNull(res[1480].exec("x{2c2}", 4605));
+assertNull(res[1480].exec("** Failers", 4606));
+assertNull(res[1480].exec("X", 4607));
+assertNull(res[1480].exec("x{9f2}", 4608));
+assertNull(res[1480].exec("+<|~x{ac}x{2044}", 4609));
+assertNull(res[1480].exec("** Failers", 4610));
+assertNull(res[1480].exec("X", 4611));
+assertNull(res[1480].exec("x{9f2}", 4612));
+assertNull(res[1481].exec("x{a6}", 4613));
+assertNull(res[1481].exec("x{482} ", 4614));
+assertNull(res[1481].exec("** Failers", 4615));
+assertNull(res[1481].exec("X", 4616));
+assertNull(res[1481].exec("x{9f2}", 4617));
+assertNull(res[1482].exec("x{2028}", 4618));
+assertNull(res[1482].exec("** Failers", 4619));
+assertNull(res[1482].exec("X", 4620));
+assertNull(res[1482].exec("x{2029}", 4621));
+assertNull(res[1483].exec("x{2029}", 4622));
+assertNull(res[1483].exec("** Failers", 4623));
+assertNull(res[1483].exec("X", 4624));
+assertNull(res[1483].exec("x{2028}", 4625));
+assertNull(res[1484].exec("\\ \\", 4626));
+assertNull(res[1484].exec("x{a0}", 4627));
+assertNull(res[1484].exec("x{1680}", 4628));
+assertNull(res[1484].exec("x{180e}", 4629));
+assertNull(res[1484].exec("x{2000}", 4630));
+assertNull(res[1484].exec("x{2001} ", 4631));
+assertNull(res[1484].exec("** Failers", 4632));
+assertNull(res[1484].exec("x{2028}", 4633));
+assertNull(res[1484].exec("x{200d} ", 4634));
+assertNull(res[1484].exec(" x{660}x{661}x{662}ABC", 4635));
+assertNull(res[1484].exec(" x{660}x{661}x{662}ABC", 4636));
+assertNull(res[1485].exec(" x{660}x{661}x{662}ABC", 4637));
+assertNull(res[1486].exec(" x{660}x{661}x{662}ABC", 4638));
+assertNull(res[1487].exec(" x{660}x{661}x{662}ABC", 4639));
+assertNull(res[1488].exec(" x{660}x{661}x{662}ABC", 4640));
+assertNull(res[1489].exec(" x{660}x{661}x{662}ABC", 4641));
+assertNull(res[1490].exec(" x{660}x{661}x{662}ABC", 4642));
+assertNull(res[1491].exec(" x{660}x{661}x{662}ABC", 4643));
+assertNull(res[1492].exec(" x{660}x{661}x{662}ABC", 4644));
+assertNull(res[1493].exec(" x{660}x{661}x{662}ABC", 4645));
+assertNull(res[1493].exec(" x{660}x{661}x{662}ABC", 4646));
+assertNull(res[1493].exec(" x{660}x{661}x{662}ABC", 4647));
+assertNull(res[1493].exec(" ** Failers", 4648));
+assertNull(res[1493].exec(" x{660}x{661}x{662}ABC", 4649));
+assertNull(res[1494].exec("A", 4650));
+assertNull(res[1494].exec("ax{10a0}B ", 4651));
+assertNull(res[1494].exec("** Failers ", 4652));
+assertNull(res[1494].exec("a", 4653));
+assertNull(res[1494].exec("x{1d00} ", 4654));
+assertNull(res[1495].exec("1234", 4655));
+assertNull(res[1495].exec("** Failers", 4656));
+assertNull(res[1495].exec("ABC ", 4657));
+assertNull(res[1496].exec("1234", 4658));
+assertNull(res[1496].exec("** Failers", 4659));
+assertNull(res[1496].exec("ABC ", 4660));
+assertNull(res[1496].exec("A2XYZ", 4661));
+assertNull(res[1496].exec("123A5XYZPQR", 4662));
+assertNull(res[1496].exec("ABAx{660}XYZpqr", 4663));
+assertNull(res[1496].exec("** Failers", 4664));
+assertNull(res[1496].exec("AXYZ", 4665));
+assertNull(res[1496].exec("XYZ ", 4666));
+assertNull(res[1496].exec("1XYZ", 4667));
+assertNull(res[1496].exec("AB=XYZ.. ", 4668));
+assertNull(res[1496].exec("XYZ ", 4669));
+assertNull(res[1496].exec("** Failers", 4670));
+assertNull(res[1496].exec("WXYZ ", 4671));
+assertNull(res[1497].exec("1234", 4672));
+assertNull(res[1497].exec("1234", 4673));
+assertNull(res[1497].exec("12-34", 4674));
+assertToStringEquals("{", res[1497].exec("12+x{661}-34 "), 4675);
+assertNull(res[1497].exec("** Failers", 4676));
+assertToStringEquals("d", res[1497].exec("abcd "), 4677);
+assertToStringEquals("d", res[1498].exec("abcd"), 4678);
+assertNull(res[1498].exec("** Failers", 4679));
+assertNull(res[1498].exec("1234", 4680));
+assertNull(res[1499].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 4681));
+assertToStringEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[1499].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 4682);
+assertToStringEquals(" ", res[1499].exec(" "), 4683);
+assertNull(res[1499].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 4684));
+assertToStringEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[1499].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 4685);
+assertNull(res[1500].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 4686));
+assertToStringEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[1500].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 4687);
+assertNull(res[1501].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 4688));
+assertNull(res[1501].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 4689));
+assertNull(res[1502].exec("11111111111111111111111111111111111111111111111111111111111111111111111", 4690));
+assertToStringEquals("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", res[1502].exec("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 4691);
+assertNull(res[1503].exec("a", 4692));
+assertNull(res[1503].exec("A ", 4693));
+assertNull(res[1504].exec("a", 4694));
+assertNull(res[1504].exec("A ", 4695));
+assertNull(res[1505].exec("A", 4696));
+assertNull(res[1505].exec("aZ", 4697));
+assertNull(res[1505].exec("** Failers", 4698));
+assertNull(res[1505].exec("abc ", 4699));
+assertNull(res[1506].exec("A", 4700));
+assertNull(res[1506].exec("aZ", 4701));
+assertNull(res[1506].exec("** Failers", 4702));
+assertNull(res[1506].exec("abc ", 4703));
+assertNull(res[1507].exec("a", 4704));
+assertNull(res[1507].exec("Az", 4705));
+assertNull(res[1507].exec("** Failers", 4706));
+assertNull(res[1507].exec("ABC ", 4707));
+assertNull(res[1508].exec("a", 4708));
+assertNull(res[1508].exec("Az", 4709));
+assertNull(res[1508].exec("** Failers", 4710));
+assertNull(res[1508].exec("ABC ", 4711));
+assertNull(res[1508].exec("x{c0}", 4712));
+assertNull(res[1508].exec("x{e0} ", 4713));
+assertNull(res[1508].exec("x{c0}", 4714));
+assertNull(res[1508].exec("x{e0} ", 4715));
+assertNull(res[1508].exec("Ax{391}x{10427}x{ff3a}x{1fb0}", 4716));
+assertNull(res[1508].exec("** Failers", 4717));
+assertNull(res[1508].exec("ax{391}x{10427}x{ff3a}x{1fb0} ", 4718));
+assertNull(res[1508].exec("Ax{3b1}x{10427}x{ff3a}x{1fb0}", 4719));
+assertNull(res[1508].exec("Ax{391}x{1044F}x{ff3a}x{1fb0}", 4720));
+assertNull(res[1508].exec("Ax{391}x{10427}x{ff5a}x{1fb0}", 4721));
+assertNull(res[1508].exec("Ax{391}x{10427}x{ff3a}x{1fb8}", 4722));
+assertNull(res[1508].exec("Ax{391}x{10427}x{ff3a}x{1fb0}", 4723));
+assertNull(res[1508].exec("ax{391}x{10427}x{ff3a}x{1fb0} ", 4724));
+assertNull(res[1508].exec("Ax{3b1}x{10427}x{ff3a}x{1fb0}", 4725));
+assertNull(res[1508].exec("Ax{391}x{1044F}x{ff3a}x{1fb0}", 4726));
+assertNull(res[1508].exec("Ax{391}x{10427}x{ff5a}x{1fb0}", 4727));
+assertNull(res[1508].exec("Ax{391}x{10427}x{ff3a}x{1fb8}", 4728));
+assertNull(res[1508].exec("x{391}x{3b1}x{3b1}x{3b1}x{391}", 4729));
+assertNull(res[1508].exec("x{391}x{3b1}x{3b1}x{3b1}x{391}X", 4730));
+assertNull(res[1508].exec("x{391}x{3b1}x{3b1}x{3b1}x{391}X", 4731));
+assertNull(res[1508].exec("x{391}", 4732));
+assertNull(res[1508].exec("x{ff3a}", 4733));
+assertNull(res[1508].exec("x{3b1}", 4734));
+assertNull(res[1508].exec("x{ff5a} ", 4735));
+assertNull(res[1508].exec("x{c0}", 4736));
+assertNull(res[1508].exec("x{e0} ", 4737));
+assertNull(res[1508].exec("x{104}", 4738));
+assertNull(res[1508].exec("x{105}", 4739));
+assertNull(res[1508].exec("x{109} ", 4740));
+assertNull(res[1508].exec("** Failers", 4741));
+assertNull(res[1508].exec("x{100}", 4742));
+assertNull(res[1508].exec("x{10a} ", 4743));
+assertNull(res[1508].exec("Z", 4744));
+assertNull(res[1508].exec("z", 4745));
+assertNull(res[1508].exec("x{39c}", 4746));
+assertNull(res[1508].exec("x{178}", 4747));
+assertNull(res[1508].exec("|", 4748));
+assertNull(res[1508].exec("x{80}", 4749));
+assertNull(res[1508].exec("x{ff}", 4750));
+assertNull(res[1508].exec("x{100}", 4751));
+assertNull(res[1508].exec("x{101} ", 4752));
+assertNull(res[1508].exec("** Failers", 4753));
+assertNull(res[1508].exec("x{102}", 4754));
+assertNull(res[1508].exec("Y", 4755));
+assertNull(res[1508].exec("y ", 4756));
+assertNull(res[1509].exec("A", 4757));
+assertNull(res[1509].exec("Ax{300}BC ", 4758));
+assertNull(res[1509].exec("Ax{300}x{301}x{302}BC ", 4759));
+assertNull(res[1509].exec("*** Failers", 4760));
+assertNull(res[1509].exec("x{300} ", 4761));
+assertToStringEquals("X", res[1510].exec("X123"), 4762);
+assertNull(res[1510].exec("*** Failers", 4763));
+assertNull(res[1510].exec("AXYZ", 4764));
+assertNull(res[1511].exec("Ax{300}x{301}x{302}BCAx{300}x{301} ", 4765));
+assertNull(res[1511].exec("Ax{300}x{301}x{302}BCAx{300}x{301}C ", 4766));
+assertNull(res[1512].exec("Ax{300}x{301}x{302}BCAx{300}x{301} ", 4767));
+assertNull(res[1512].exec("Ax{300}x{301}x{302}BCAx{300}x{301}C ", 4768));
+assertToStringEquals("A,,A", res[1513].exec("Ax{300}x{301}x{302}BCAx{300}x{301} "), 4769);
+assertToStringEquals("A,,A", res[1513].exec("Ax{300}x{301}x{302}BCAx{300}x{301}C "), 4770);
+assertToStringEquals("A,,A", res[1514].exec("Ax{300}x{301}x{302}BCAx{300}x{301} "), 4771);
+assertToStringEquals("A,,A", res[1514].exec("Ax{300}x{301}x{302}BCAx{300}x{301}C "), 4772);
+assertNull(res[1515].exec("*** Failers", 4773));
+assertNull(res[1515].exec("Ax{300}x{301}x{302}", 4774));
+assertNull(res[1516].exec("Ax{300}x{301}Bx{300}X", 4775));
+assertNull(res[1516].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}", 4776));
+assertNull(res[1516].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}X", 4777));
+assertNull(res[1516].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}DAx{300}X", 4778));
+assertNull(res[1517].exec("Ax{300}x{301}Bx{300}X", 4779));
+assertNull(res[1517].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}", 4780));
+assertNull(res[1517].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}X", 4781));
+assertNull(res[1517].exec("Ax{300}x{301}Bx{300}Cx{300}x{301}DAx{300}X", 4782));
+assertNull(res[1518].exec("12X", 4783));
+assertNull(res[1518].exec("123X", 4784));
+assertNull(res[1518].exec("*** Failers", 4785));
+assertNull(res[1518].exec("X", 4786));
+assertNull(res[1518].exec("1X", 4787));
+assertNull(res[1518].exec("1234X ", 4788));
+assertNull(res[1518].exec("x{100} ", 4789));
+assertNull(res[1518].exec("x{101} ", 4790));
+assertNull(res[1518].exec("x{2e81}x{3007}x{2f804}x{31a0}", 4791));
+assertNull(res[1518].exec("** Failers", 4792));
+assertNull(res[1518].exec("x{2e7f} ", 4793));
+assertNull(res[1518].exec("x{3105}", 4794));
+assertNull(res[1518].exec("** Failers", 4795));
+assertNull(res[1518].exec("x{30ff} ", 4796));
+assertNull(res[1519].exec("x{06e9}", 4797));
+assertNull(res[1519].exec("x{060b}", 4798));
+assertNull(res[1519].exec("** Failers", 4799));
+assertNull(res[1519].exec("Xx{06e9} ", 4800));
+assertNull(res[1520].exec("x{2f800}", 4801));
+assertNull(res[1520].exec("** Failers", 4802));
+assertNull(res[1520].exec("x{a014}", 4803));
+assertNull(res[1520].exec("x{a4c6} ", 4804));
+assertNull(res[1521].exec("AXYZ", 4805));
+assertNull(res[1521].exec("x{1234}XYZ ", 4806));
+assertNull(res[1521].exec("** Failers", 4807));
+assertNull(res[1521].exec("X ", 4808));
+assertNull(res[1522].exec("** Failers", 4809));
+assertNull(res[1522].exec("AX", 4810));
+assertNull(res[1523].exec("XYZ", 4811));
+assertNull(res[1523].exec("AXYZ", 4812));
+assertNull(res[1523].exec("x{1234}XYZ ", 4813));
+assertNull(res[1523].exec("** Failers", 4814));
+assertNull(res[1523].exec("ABXYZ ", 4815));
+assertNull(res[1524].exec("XYZ", 4816));
+assertNull(res[1524].exec("** Failers", 4817));
+assertNull(res[1524].exec("AXYZ", 4818));
+assertNull(res[1524].exec("x{1234}XYZ ", 4819));
+assertNull(res[1524].exec("ABXYZ ", 4820));
+assertNull(res[1524].exec("AXYZ", 4821));
+assertNull(res[1524].exec("x{1234}XYZ", 4822));
+assertNull(res[1524].exec("Ax{1234}XYZ", 4823));
+assertNull(res[1524].exec("** Failers", 4824));
+assertNull(res[1524].exec("XYZ", 4825));
+assertNull(res[1524].exec("** Failers", 4826));
+assertNull(res[1524].exec("AXYZ", 4827));
+assertNull(res[1524].exec("x{1234}XYZ", 4828));
+assertNull(res[1524].exec("Ax{1234}XYZ", 4829));
+assertNull(res[1524].exec("XYZ", 4830));
+assertNull(res[1525].exec("XYZ", 4831));
+assertNull(res[1525].exec("AXYZ", 4832));
+assertNull(res[1525].exec("x{1234}XYZ", 4833));
+assertNull(res[1525].exec("Ax{1234}XYZ", 4834));
+assertNull(res[1525].exec("** Failers", 4835));
+assertNull(res[1526].exec("XYZ", 4836));
+assertNull(res[1526].exec("** Failers", 4837));
+assertNull(res[1526].exec("AXYZ", 4838));
+assertNull(res[1526].exec("x{1234}XYZ", 4839));
+assertNull(res[1526].exec("Ax{1234}XYZ", 4840));
+assertToStringEquals("AX", res[1527].exec("AXYZ"), 4841);
+assertNull(res[1527].exec("x{1234}XYZ ", 4842));
+assertNull(res[1527].exec("** Failers", 4843));
+assertNull(res[1527].exec("X ", 4844));
+assertNull(res[1528].exec("** Failers", 4845));
+assertToStringEquals("AX", res[1528].exec("AX"), 4846);
+assertToStringEquals("X", res[1529].exec("XYZ"), 4847);
+assertToStringEquals("AX", res[1529].exec("AXYZ"), 4848);
+assertNull(res[1529].exec("x{1234}XYZ ", 4849));
+assertNull(res[1529].exec("** Failers", 4850));
+assertNull(res[1529].exec("ABXYZ ", 4851));
+assertToStringEquals("X", res[1530].exec("XYZ"), 4852);
+assertNull(res[1530].exec("** Failers", 4853));
+assertToStringEquals("AX", res[1530].exec("AXYZ"), 4854);
+assertNull(res[1530].exec("x{1234}XYZ ", 4855));
+assertNull(res[1530].exec("ABXYZ ", 4856));
+assertToStringEquals("AX", res[1531].exec("AXYZ"), 4857);
+assertNull(res[1531].exec("x{1234}XYZ", 4858));
+assertNull(res[1531].exec("Ax{1234}XYZ", 4859));
+assertNull(res[1531].exec("** Failers", 4860));
+assertNull(res[1531].exec("XYZ", 4861));
+assertNull(res[1532].exec("** Failers", 4862));
+assertToStringEquals("AX", res[1532].exec("AXYZ"), 4863);
+assertNull(res[1532].exec("x{1234}XYZ", 4864));
+assertNull(res[1532].exec("Ax{1234}XYZ", 4865));
+assertNull(res[1532].exec("XYZ", 4866));
+assertToStringEquals("X", res[1533].exec("XYZ"), 4867);
+assertToStringEquals("AX", res[1533].exec("AXYZ"), 4868);
+assertNull(res[1533].exec("x{1234}XYZ", 4869));
+assertNull(res[1533].exec("Ax{1234}XYZ", 4870));
+assertNull(res[1533].exec("** Failers", 4871));
+assertToStringEquals("X", res[1534].exec("XYZ"), 4872);
+assertNull(res[1534].exec("** Failers", 4873));
+assertToStringEquals("AX", res[1534].exec("AXYZ"), 4874);
+assertNull(res[1534].exec("x{1234}XYZ", 4875));
+assertNull(res[1534].exec("Ax{1234}XYZ", 4876));
+assertNull(res[1535].exec("abcdefgh", 4877));
+assertNull(res[1535].exec("x{1234}\n\x0dx{3456}xyz ", 4878));
+assertNull(res[1536].exec("abcdefgh", 4879));
+assertNull(res[1536].exec("x{1234}\n\x0dx{3456}xyz ", 4880));
+assertNull(res[1537].exec("** Failers", 4881));
+assertNull(res[1537].exec("abcdefgh", 4882));
+assertNull(res[1537].exec("x{1234}\n\x0dx{3456}xyz ", 4883));
+assertNull(res[1538].exec(" AXY", 4884));
+assertNull(res[1538].exec(" aXY", 4885));
+assertNull(res[1538].exec(" x{1c5}XY", 4886));
+assertNull(res[1538].exec(" ** Failers", 4887));
+assertNull(res[1538].exec(" x{1bb}XY", 4888));
+assertNull(res[1538].exec(" x{2b0}XY", 4889));
+assertNull(res[1538].exec(" !XY ", 4890));
+assertNull(res[1539].exec(" AXY", 4891));
+assertNull(res[1539].exec(" aXY", 4892));
+assertNull(res[1539].exec(" x{1c5}XY", 4893));
+assertNull(res[1539].exec(" ** Failers", 4894));
+assertNull(res[1539].exec(" x{1bb}XY", 4895));
+assertNull(res[1539].exec(" x{2b0}XY", 4896));
+assertNull(res[1539].exec(" !XY ", 4897));
+assertNull(res[1539].exec(" AXY", 4898));
+assertNull(res[1539].exec(" aXY", 4899));
+assertNull(res[1539].exec(" AbcdeXyz ", 4900));
+assertNull(res[1539].exec(" x{1c5}AbXY", 4901));
+assertNull(res[1539].exec(" abcDEXypqreXlmn ", 4902));
+assertNull(res[1539].exec(" ** Failers", 4903));
+assertNull(res[1539].exec(" x{1bb}XY", 4904));
+assertNull(res[1539].exec(" x{2b0}XY", 4905));
+assertNull(res[1539].exec(" !XY ", 4906));
+assertNull(res[1540].exec(" AXY", 4907));
+assertNull(res[1540].exec(" aXY", 4908));
+assertNull(res[1540].exec(" AbcdeXyz ", 4909));
+assertNull(res[1540].exec(" x{1c5}AbXY", 4910));
+assertNull(res[1540].exec(" abcDEXypqreXlmn ", 4911));
+assertNull(res[1540].exec(" ** Failers", 4912));
+assertNull(res[1540].exec(" x{1bb}XY", 4913));
+assertNull(res[1540].exec(" x{2b0}XY", 4914));
+assertNull(res[1540].exec(" !XY ", 4915));
+assertNull(res[1540].exec(" AXY", 4916));
+assertNull(res[1540].exec(" aXY", 4917));
+assertNull(res[1540].exec(" AbcdeXyz ", 4918));
+assertNull(res[1540].exec(" x{1c5}AbXY", 4919));
+assertNull(res[1540].exec(" abcDEXypqreXlmn ", 4920));
+assertNull(res[1540].exec(" ** Failers", 4921));
+assertNull(res[1540].exec(" x{1bb}XY", 4922));
+assertNull(res[1540].exec(" x{2b0}XY", 4923));
+assertNull(res[1540].exec(" !XY ", 4924));
+assertNull(res[1541].exec(" AXY", 4925));
+assertNull(res[1541].exec(" aXY", 4926));
+assertNull(res[1541].exec(" AbcdeXyz ", 4927));
+assertNull(res[1541].exec(" x{1c5}AbXY", 4928));
+assertNull(res[1541].exec(" abcDEXypqreXlmn ", 4929));
+assertNull(res[1541].exec(" ** Failers", 4930));
+assertNull(res[1541].exec(" x{1bb}XY", 4931));
+assertNull(res[1541].exec(" x{2b0}XY", 4932));
+assertNull(res[1541].exec(" !XY ", 4933));
+assertNull(res[1542].exec(" !XY", 4934));
+assertNull(res[1542].exec(" x{1bb}XY", 4935));
+assertNull(res[1542].exec(" x{2b0}XY", 4936));
+assertNull(res[1542].exec(" ** Failers", 4937));
+assertNull(res[1542].exec(" x{1c5}XY", 4938));
+assertNull(res[1542].exec(" AXY ", 4939));
+assertNull(res[1543].exec(" !XY", 4940));
+assertNull(res[1543].exec(" x{1bb}XY", 4941));
+assertNull(res[1543].exec(" x{2b0}XY", 4942));
+assertNull(res[1543].exec(" ** Failers", 4943));
+assertNull(res[1543].exec(" x{1c5}XY", 4944));
+assertNull(res[1543].exec(" AXY ", 4945));
+assertNull(res[1543].exec("x{c0}x{e0}x{116}x{117}", 4946));
+assertNull(res[1543].exec("x{c0}x{e0}x{116}x{117}", 4947));
+assertNull(res[1545].exec("123abcdefg", 4948));
+assertNull(res[1545].exec("123abc\xc4\xc5zz", 4949));
+assertNull(res[1546].exec("x{102A4}x{AA52}x{A91D}x{1C46}x{10283}x{1092E}x{1C6B}x{A93B}x{A8BF}x{1BA0}x{A50A}====", 4950));
+assertNull(res[1546].exec("x{a77d}x{1d79}", 4951));
+assertNull(res[1546].exec("x{1d79}x{a77d} ", 4952));
+assertNull(res[1546].exec("x{a77d}x{1d79}", 4953));
+assertNull(res[1546].exec("** Failers ", 4954));
+assertNull(res[1546].exec("x{1d79}x{a77d} ", 4955));
assertThrows("var re = //;", 4956);
diff --git a/test/mjsunit/tools/codemap.js b/test/mjsunit/tools/codemap.js
index 81fb810..33d7e4e 100644
--- a/test/mjsunit/tools/codemap.js
+++ b/test/mjsunit/tools/codemap.js
@@ -157,6 +157,7 @@
codeMap.addStaticCode(0x15500, newCodeEntry(0x5000, 'lib2'));
codeMap.addStaticCode(0x155500, newCodeEntry(0x10000, 'lib3'));
var allStatics = codeMap.getAllStaticEntries();
+ allStatics = allStatics.map(String);
allStatics.sort();
assertEquals(['lib1: 3000', 'lib2: 5000', 'lib3: 10000'], allStatics);
})();
@@ -168,13 +169,15 @@
codeMap.addCode(0x1700, newCodeEntry(0x100, 'code2'));
codeMap.addCode(0x1900, newCodeEntry(0x50, 'code3'));
var allDynamics = codeMap.getAllDynamicEntries();
+ allDynamics = allDynamics.map(String);
allDynamics.sort();
assertEquals(['code1: 200', 'code2: 100', 'code3: 50'], allDynamics);
codeMap.deleteCode(0x1700);
var allDynamics2 = codeMap.getAllDynamicEntries();
+ allDynamics2 = allDynamics2.map(String);
allDynamics2.sort();
assertEquals(['code1: 200', 'code3: 50'], allDynamics2);
codeMap.deleteCode(0x1500);
var allDynamics3 = codeMap.getAllDynamicEntries();
- assertEquals(['code3: 50'], allDynamics3);
+ assertEquals(['code3: 50'], allDynamics3.map(String));
})();
diff --git a/test/mjsunit/tools/splaytree.js b/test/mjsunit/tools/splaytree.js
index 5e18796..d582dc9 100644
--- a/test/mjsunit/tools/splaytree.js
+++ b/test/mjsunit/tools/splaytree.js
@@ -81,13 +81,13 @@
(function testSplay() {
var tree = new SplayTree();
tree.root_ = createSampleTree();
- assertArrayEquals(['50', '30', '60', '10', '40', '90', '20', '70', '100', '15', '80'],
+ assertArrayEquals([50, 30, 60, 10, 40, 90, 20, 70, 100, 15, 80],
tree.exportValues());
tree.splay_(50);
- assertArrayEquals(['50', '30', '60', '10', '40', '90', '20', '70', '100', '15', '80'],
+ assertArrayEquals([50, 30, 60, 10, 40, 90, 20, 70, 100, 15, 80],
tree.exportValues());
tree.splay_(80);
- assertArrayEquals(['80', '60', '90', '50', '70', '100', '30', '10', '40', '20', '15'],
+ assertArrayEquals([80, 60, 90, 50, 70, 100, 30, 10, 40, 20, 15],
tree.exportValues());
})();
diff --git a/src/virtual-frame-inl.h b/test/preparser/empty.js
similarity index 81%
copy from src/virtual-frame-inl.h
copy to test/preparser/empty.js
index c9f4aac..70b88e2 100644
--- a/src/virtual-frame-inl.h
+++ b/test/preparser/empty.js
@@ -1,4 +1,4 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -25,15 +25,4 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#ifndef V8_VIRTUAL_FRAME_INL_H_
-#define V8_VIRTUAL_FRAME_INL_H_
-
-#include "virtual-frame.h"
-
-#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64
-#include "virtual-frame-heavy-inl.h"
-#else
-#include "virtual-frame-light-inl.h"
-#endif
-
-#endif // V8_VIRTUAL_FRAME_INL_H_
+// This file contains no JavaScript code.
diff --git a/src/virtual-frame-inl.h b/test/preparser/functions-only.js
similarity index 81%
copy from src/virtual-frame-inl.h
copy to test/preparser/functions-only.js
index c9f4aac..4dcde57 100644
--- a/src/virtual-frame-inl.h
+++ b/test/preparser/functions-only.js
@@ -1,4 +1,4 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -25,15 +25,14 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#ifndef V8_VIRTUAL_FRAME_INL_H_
-#define V8_VIRTUAL_FRAME_INL_H_
+// This file contains no identifiers or string literals, but does contain
+// symbols.
-#include "virtual-frame.h"
-
-#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64
-#include "virtual-frame-heavy-inl.h"
-#else
-#include "virtual-frame-light-inl.h"
-#endif
-
-#endif // V8_VIRTUAL_FRAME_INL_H_
+(function () {
+ if (this != null) {
+ return this;
+ }
+ while (true) {
+ if ([][2]) return false;
+ }
+})({}, function() { return [true]; } );
diff --git a/test/preparser/non-alphanum.js b/test/preparser/non-alphanum.js
new file mode 100644
index 0000000..83bd1f8
--- /dev/null
+++ b/test/preparser/non-alphanum.js
@@ -0,0 +1,34 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file contains no symbols or function declarations, and only
+// non-alphanumeric characters, but does contain valid code.
+
+// Created using http://discogscounter.getfreehosting.co.uk/js-noalnum_com.php
+// Probably only works in Firefox, but should parse fine.
+
+([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()[(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]])([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()[([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(![]+[])[+!+[]]]((![]+[])[+!+[]])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+[]]+(+[![]]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+!+[]]]+[][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()[(![]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()+[])[!+[]+!+[]]]((![]+[])[+!+[]]+(+[![]]+[])[+[]])[+[]]+(![]+[])[+!+[]]+(+[]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+!+[]+[+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(+[![]]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+!+[]]]+([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(+[![]]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+!+[]]]+([]+([]+[])[([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]])[+!+[]+[!+[]+!+[]+!+[]+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()[(![]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()+[])[!+[]+!+[]]]((![]+[])[+!+[]]+[+[]])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[!+[]+!+[]]+[][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()[(![]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()+[])[!+[]+!+[]]]((+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]]+[+[]])+[])[+[]]+(![]+[])[+[]])[+[]])
diff --git a/src/virtual-frame-inl.h b/test/preparser/symbols-only.js
similarity index 80%
copy from src/virtual-frame-inl.h
copy to test/preparser/symbols-only.js
index c9f4aac..b652063 100644
--- a/src/virtual-frame-inl.h
+++ b/test/preparser/symbols-only.js
@@ -1,4 +1,4 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -25,15 +25,25 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#ifndef V8_VIRTUAL_FRAME_INL_H_
-#define V8_VIRTUAL_FRAME_INL_H_
+// This file contains no function declarations.
-#include "virtual-frame.h"
-
-#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64
-#include "virtual-frame-heavy-inl.h"
-#else
-#include "virtual-frame-light-inl.h"
-#endif
-
-#endif // V8_VIRTUAL_FRAME_INL_H_
+var x = 42;
+var y = "hello world";
+if (x == y) {
+ with ({ x: 10, y: "20", z: 42 }) {
+ print(z);
+ }
+}
+try {
+ x = 2;
+ throw y;
+ y = 4;
+} catch (e) {
+ y = e;
+} finally {
+ x = y;
+}
+for (var i = 0; i < 10; i++) {
+ x += x;
+}
+print(y);
diff --git a/test/preparser/testcfg.py b/test/preparser/testcfg.py
new file mode 100644
index 0000000..c78d03b
--- /dev/null
+++ b/test/preparser/testcfg.py
@@ -0,0 +1,90 @@
+# Copyright 2011 the V8 project authors. All rights reserved.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import test
+import os
+from os.path import join, dirname, exists
+import platform
+import utils
+
+
+class PreparserTestCase(test.TestCase):
+
+ def __init__(self, root, path, executable, mode, context):
+ super(PreparserTestCase, self).__init__(context, path, mode)
+ self.executable = executable
+ self.root = root
+
+ def GetLabel(self):
+ return "%s %s %s" % (self.mode, self.path[-2], self.path[-1])
+
+ def GetName(self):
+ return self.path[-1]
+
+ def BuildCommand(self, path):
+ testfile = join(self.root, self.GetName()) + ".js"
+ result = [self.executable, testfile]
+ return result
+
+ def GetCommand(self):
+ return self.BuildCommand(self.path)
+
+ def Run(self):
+ return test.TestCase.Run(self)
+
+
+class PreparserTestConfiguration(test.TestConfiguration):
+
+ def __init__(self, context, root):
+ super(PreparserTestConfiguration, self).__init__(context, root)
+
+ def GetBuildRequirements(self):
+ return ['preparser']
+
+ def ListTests(self, current_path, path, mode, variant_flags):
+ executable = join('obj', 'preparser', mode, 'preparser')
+ if utils.IsWindows():
+ executable += '.exe'
+ executable = join(self.context.buildspace, executable)
+ # Find all .js files in tests/preparser directory.
+ filenames = [f[:-3] for f in os.listdir(self.root) if f.endswith(".js")]
+ filenames.sort()
+ result = []
+ for file in filenames:
+ result.append(PreparserTestCase(self.root,
+ current_path + [file], executable,
+ mode, self.context))
+ return result
+
+ def GetTestStatus(self, sections, defs):
+ status_file = join(self.root, 'preparser.status')
+ if exists(status_file):
+ test.ReadConfigurationInto(status_file, sections, defs)
+
+
+def GetConfiguration(context, root):
+ return PreparserTestConfiguration(context, root)
diff --git a/test/sputnik/testcfg.py b/test/sputnik/testcfg.py
index c1e3c1b..c9eb4f2 100644
--- a/test/sputnik/testcfg.py
+++ b/test/sputnik/testcfg.py
@@ -57,7 +57,7 @@
def AfterRun(self, result):
# Dispose the temporary file if everything looks okay.
- if not result.HasPreciousOutput(): self.tmpfile.Dispose()
+ if result is None or not result.HasPreciousOutput(): self.tmpfile.Dispose()
self.tmpfile = None
def GetCommand(self):
diff --git a/test/test262/harness-adapt.js b/test/test262/harness-adapt.js
index b52afdb..bc10a9d 100644
--- a/test/test262/harness-adapt.js
+++ b/test/test262/harness-adapt.js
@@ -43,7 +43,7 @@
}
Test262Error.prototype.toString = function() {
- return this.result;
+ return this.result + " " + error;
}
function registerTest(test) {
@@ -52,22 +52,24 @@
try {
var res = test.test.call($this);
} catch(e) {
- print(e);
- res = 'fail'; error = e;
+ res = 'fail';
+ error = e;
}
var retVal = /^s/i.test(test.id)
? (res === true || typeof res == 'undefined' ? 'pass' : 'fail')
: (res === true ? 'pass' : 'fail');
if (retVal != 'pass') {
+ var precondition = (test.precondition !== undefined)
+ ? test.precondition.toString()
+ : '';
+
throw new Test262Error(
test.id,
test.path,
test.description,
test.test.toString(),
- (test.precondition !== undefined)
- ? test.precondition.toString()
- : '',
+ precondition,
retVal,
error);
}
diff --git a/test/test262/test262.status b/test/test262/test262.status
index bdc8b9c..754984d 100644
--- a/test/test262/test262.status
+++ b/test/test262/test262.status
@@ -32,6 +32,20 @@
prefix ietestcenter
+
+#
+# Deliberate differences for compatibility with other browsers
+#
+# 15.9.5.43-0-9 and 15.9.5.43-0-10. V8 doesn't throw RangeError
+# from Date.prototype.toISOString when string is not a finite number.
+# This is compatible with Firefox and Safari.
+15.9.5.43-0-9: PASS || FAIL
+15.9.5.43-0-10: PASS || FAIL
+
+#
+# Unanalyzed failures which may be bugs or deliberate differences
+#
+
# BUG: 7.6 - SyntaxError expected: reserved words used as Identifier
# Names in UTF8: class (class)
7.6-30: FAIL
@@ -913,66 +927,6 @@
# of 'O', and 'desc' is accessor descriptor, test updating multiple
# attribute values of 'P' (10.6 [[DefineOwnProperty]] step 3)
15.2.3.7-6-a-292: FAIL
-# Bug? Object.preventExtensions - indexed properties cannot be added into the
-# returned object
-15.2.3.10-3-2: FAIL
-# Bug? Object.preventExtensions - indexed properties cannot be added into a
-# Function object
-15.2.3.10-3-3: FAIL
-# Bug? Object.preventExtensions - indexed properties cannot be added into an
-# Array object
-15.2.3.10-3-4: FAIL
-# Bug? Object.preventExtensions - indexed properties cannot be added into a
-# String object
-15.2.3.10-3-5-1: FAIL
-# Bug? Object.preventExtensions - indexed properties cannot be added into a
-# Boolean object
-15.2.3.10-3-6: FAIL
-# Bug? Object.preventExtensions - indexed properties cannot be added into a
-# Number object
-15.2.3.10-3-7: FAIL
-# Bug? Object.preventExtensions - indexed properties cannot be added into a Date
-# object
-15.2.3.10-3-8: FAIL
-# Bug? Object.preventExtensions - indexed properties cannot be added into a
-# RegExp object
-15.2.3.10-3-9: FAIL
-# Bug? Object.preventExtensions - indexed properties cannot be added into an
-# Error object
-15.2.3.10-3-10: FAIL
-# Bug? Object.preventExtensions - indexed properties cannot be added into an
-# Arguments object
-15.2.3.10-3-11: FAIL
-# Bug? Object.preventExtensions - named properties cannot be added into the
-# returned object
-15.2.3.10-3-12: FAIL
-# Bug? Object.preventExtensions - named properties cannot be added into a
-# Function object
-15.2.3.10-3-13: FAIL
-# Bug? Object.preventExtensions - named properties cannot be added into an Array
-# object
-15.2.3.10-3-14: FAIL
-# Bug? Object.preventExtensions - named properties cannot be added into a String
-# object
-15.2.3.10-3-15: FAIL
-# Bug? Object.preventExtensions - named properties cannot be added into a
-# Boolean object
-15.2.3.10-3-16: FAIL
-# Bug? Object.preventExtensions - named properties cannot be added into a Number
-# object
-15.2.3.10-3-17: FAIL
-# Bug? Object.preventExtensions - named properties cannot be added into a Date
-# object
-15.2.3.10-3-18: FAIL
-# Bug? Object.preventExtensions - named properties cannot be added into a RegExp
-# object
-15.2.3.10-3-19: FAIL
-# Bug? Object.preventExtensions - named properties cannot be added into an Error
-# object
-15.2.3.10-3-20: FAIL
-# Bug? Object.preventExtensions - named properties cannot be added into an
-# Arguments object
-15.2.3.10-3-21: FAIL
# Bug? Object.prototype.toString - '[object Undefined]' will be returned when
# 'this' value is undefined
15.2.4.2-1-1: FAIL
diff --git a/tools/gcmole/Makefile b/tools/gcmole/Makefile
new file mode 100644
index 0000000..23c029c
--- /dev/null
+++ b/tools/gcmole/Makefile
@@ -0,0 +1,43 @@
+# Copyright 2011 the V8 project authors. All rights reserved.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# This is Makefile for clang plugin part of gcmole tool. See README.
+
+LLVM_INCLUDE:=$(LLVM_SRC_ROOT)/include
+CLANG_INCLUDE:=$(LLVM_SRC_ROOT)/tools/clang/include
+
+libgcmole.so: gcmole.cc
+ g++ -I$(LLVM_INCLUDE) -I$(CLANG_INCLUDE) -I. -D_DEBUG -D_GNU_SOURCE \
+ -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -O3 \
+ -fomit-frame-pointer -fno-exceptions -fno-rtti -fPIC \
+ -Woverloaded-virtual -Wcast-qual -fno-strict-aliasing \
+ -pedantic -Wno-long-long -Wall \
+ -W -Wno-unused-parameter -Wwrite-strings \
+ -shared -o libgcmole.so gcmole.cc
+
+clean:
+ rm libgcmole.so
diff --git a/tools/gcmole/README b/tools/gcmole/README
new file mode 100644
index 0000000..02cf88c
--- /dev/null
+++ b/tools/gcmole/README
@@ -0,0 +1,62 @@
+DESCRIPTION -------------------------------------------------------------------
+
+gcmole is a simple static analysis tool used to find possible evaluation order
+dependent GC-unsafe places in the V8 codebase.
+
+For example the following code is GC-unsafe:
+
+Handle<Object> Foo(); // Assume Foo can trigger a GC.
+void Bar(Object*, Object*);
+
+Handle<Object> baz;
+baz->Qux(*Foo()); // (a)
+Bar(*Foo(), *baz); // (b)
+
+Both in cases (a) and (b) compiler is free to evaluate call arguments (that
+includes receiver) in any order. That means it can dereference baz before
+calling to Foo and save a raw pointer to a heap object in the register or
+on the stack.
+
+PREREQUISITES -----------------------------------------------------------------
+
+1) Install Lua 5.1
+
+2) Get LLVM and Clang sources and build them.
+
+Follow the instructions on http://clang.llvm.org/get_started.html.
+
+Make sure to pass --enable-optimized to configure to get Release build
+instead of a Debug one.
+
+3) Build gcmole Clang plugin (libgcmole.so)
+
+In the tools/gcmole execute the following command:
+
+LLVM_SRC_ROOT=<path-to-llvm-source-root> make
+
+USING GCMOLE ------------------------------------------------------------------
+
+gcmole consists of driver script written in Lua and Clang plugin that does
+C++ AST processing. Plugin (libgcmole.so) is expected to be in the same
+folder as driver (gcmole.lua).
+
+To start analysis cd into the root of v8 checkout and execute the following
+command:
+
+CLANG_BIN=<path-to-clang-bin-folder> lua tools/gcmole/gcmole.lua [<arch>]
+
+where arch should be one of architectures supported by V8 (arm, ia32, x64).
+
+Analysis will be performed in 2 stages:
+
+- on the first stage driver will parse all files and build a global callgraph
+approximation to find all functions that might potentially cause GC, list
+of this functions will be written into gcsuspects file.
+
+- on the second stage driver will parse all files again and will locate all
+callsites that might be GC-unsafe based on the list of functions causing GC.
+Such places are marked with a "Possible problem with evaluation order."
+warning. Messages "Failed to resolve v8::internal::Object" are benign and
+can be ignored.
+
+If any errors were found driver exits with non-zero status.
diff --git a/tools/gcmole/gcmole.cc b/tools/gcmole/gcmole.cc
new file mode 100644
index 0000000..ad64c1d
--- /dev/null
+++ b/tools/gcmole/gcmole.cc
@@ -0,0 +1,495 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This is clang plugin used by gcmole tool. See README for more details.
+
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/Mangle.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Frontend/FrontendPluginRegistry.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <bitset>
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <set>
+#include <stack>
+
+namespace {
+
+typedef std::string MangledName;
+typedef std::set<MangledName> CalleesSet;
+
+static bool GetMangledName(clang::MangleContext* ctx,
+ const clang::NamedDecl* decl,
+ MangledName* result) {
+ if (!isa<clang::CXXConstructorDecl>(decl) &&
+ !isa<clang::CXXDestructorDecl>(decl)) {
+ llvm::SmallVector<char, 512> output;
+ llvm::raw_svector_ostream out(output);
+ ctx->mangleName(decl, out);
+ *result = out.str().str();
+ return true;
+ }
+
+ return false;
+}
+
+
+static bool InV8Namespace(const clang::NamedDecl* decl) {
+ return decl->getQualifiedNameAsString().compare(0, 4, "v8::") == 0;
+}
+
+
+class CalleesPrinter : public clang::RecursiveASTVisitor<CalleesPrinter> {
+ public:
+ explicit CalleesPrinter(clang::MangleContext* ctx) : ctx_(ctx) {
+ }
+
+ virtual bool VisitCallExpr(clang::CallExpr* expr) {
+ const clang::FunctionDecl* callee = expr->getDirectCallee();
+ if (callee != NULL) AnalyzeFunction(callee);
+ return true;
+ }
+
+ void AnalyzeFunction(const clang::FunctionDecl* f) {
+ MangledName name;
+ if (InV8Namespace(f) && GetMangledName(ctx_, f, &name)) {
+ AddCallee(name);
+
+ const clang::FunctionDecl* body = NULL;
+ if (f->hasBody(body) && !Analyzed(name)) {
+ EnterScope(name);
+ TraverseStmt(body->getBody());
+ LeaveScope();
+ }
+ }
+ }
+
+ typedef std::map<MangledName, CalleesSet* > Callgraph;
+
+ bool Analyzed(const MangledName& name) {
+ return callgraph_[name] != NULL;
+ }
+
+ void EnterScope(const MangledName& name) {
+ CalleesSet* callees = callgraph_[name];
+
+ if (callees == NULL) {
+ callgraph_[name] = callees = new CalleesSet();
+ }
+
+ scopes_.push(callees);
+ }
+
+ void LeaveScope() {
+ scopes_.pop();
+ }
+
+ void AddCallee(const MangledName& name) {
+ if (!scopes_.empty()) scopes_.top()->insert(name);
+ }
+
+ void PrintCallGraph() {
+ for (Callgraph::const_iterator i = callgraph_.begin(), e = callgraph_.end();
+ i != e;
+ ++i) {
+ std::cout << i->first << "\n";
+
+ CalleesSet* callees = i->second;
+ for (CalleesSet::const_iterator j = callees->begin(), e = callees->end();
+ j != e;
+ ++j) {
+ std::cout << "\t" << *j << "\n";
+ }
+ }
+ }
+
+ private:
+ clang::MangleContext* ctx_;
+
+ std::stack<CalleesSet* > scopes_;
+ Callgraph callgraph_;
+};
+
+class FunctionDeclarationFinder
+ : public clang::ASTConsumer,
+ public clang::RecursiveASTVisitor<FunctionDeclarationFinder> {
+ public:
+ explicit FunctionDeclarationFinder(clang::Diagnostic& d,
+ clang::SourceManager& sm)
+ : d_(d), sm_(sm) { }
+
+ virtual void HandleTranslationUnit(clang::ASTContext &ctx) {
+ mangle_context_ = clang::createItaniumMangleContext(ctx, d_);
+ callees_printer_ = new CalleesPrinter(mangle_context_);
+
+ TraverseDecl(ctx.getTranslationUnitDecl());
+
+ callees_printer_->PrintCallGraph();
+ }
+
+ virtual bool VisitFunctionDecl(clang::FunctionDecl* decl) {
+ callees_printer_->AnalyzeFunction(decl);
+ return true;
+ }
+
+ private:
+ clang::Diagnostic& d_;
+ clang::SourceManager& sm_;
+ clang::MangleContext* mangle_context_;
+
+ CalleesPrinter* callees_printer_;
+};
+
+
+static bool loaded = false;
+static CalleesSet gc_suspects;
+
+
+static void LoadGCSuspects() {
+ if (loaded) return;
+
+ std::ifstream fin("gcsuspects");
+ std::string s;
+
+ while (fin >> s) gc_suspects.insert(s);
+
+ loaded = true;
+}
+
+
+static bool KnownToCauseGC(clang::MangleContext* ctx,
+ const clang::FunctionDecl* decl) {
+ LoadGCSuspects();
+
+ if (!InV8Namespace(decl)) return false;
+
+ MangledName name;
+ if (GetMangledName(ctx, decl, &name)) {
+ return gc_suspects.find(name) != gc_suspects.end();
+ }
+
+ return false;
+}
+
+
+static bool IsHandleType(const clang::DeclarationName& handleDeclName,
+ const clang::QualType& qtype) {
+ const clang::Type* canonical_type =
+ qtype.getTypePtr()->getCanonicalTypeUnqualified().getTypePtr();
+
+ if (const clang::TemplateSpecializationType* type =
+ canonical_type->getAs<clang::TemplateSpecializationType>()) {
+ if (clang::TemplateDecl* decl =
+ type->getTemplateName().getAsTemplateDecl()) {
+ if (decl->getTemplatedDecl()->getDeclName() == handleDeclName) {
+ return true;
+ }
+ }
+ } else if (const clang::RecordType* type =
+ canonical_type->getAs<clang::RecordType>()) {
+ if (const clang::ClassTemplateSpecializationDecl* t =
+ dyn_cast<clang::ClassTemplateSpecializationDecl>(type->getDecl())) {
+ if (t->getSpecializedTemplate()->getDeclName() == handleDeclName) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+class ExpressionClassifier :
+ public clang::RecursiveASTVisitor<ExpressionClassifier> {
+ public:
+ ExpressionClassifier(clang::DeclarationName handleDeclName,
+ clang::MangleContext* ctx,
+ clang::CXXRecordDecl* objectDecl)
+ : handleDeclName_(handleDeclName),
+ ctx_(ctx),
+ objectDecl_(objectDecl) {
+ }
+
+ bool IsBadExpression(clang::Expr* expr) {
+ has_derefs_ = has_gc_ = false;
+ TraverseStmt(expr);
+ return has_derefs_ && has_gc_;
+ }
+
+ bool IsBadCallSite(clang::Expr* expr) {
+ if (isa<clang::CallExpr>(expr)) {
+ clang::CallExpr* call = cast<clang::CallExpr>(expr);
+
+ MarkGCSuspectAsArgument(call);
+ MarkHandleDereferenceAsArgument(call);
+
+ return derefs_.any() &&
+ ((gc_.count() > 1) || (gc_.any() && (gc_ ^ derefs_).any()));
+ }
+ return false;
+ }
+
+ virtual bool VisitExpr(clang::Expr* expr) {
+ has_derefs_ = has_derefs_ || IsRawPointerType(expr);
+ return !has_gc_ || !has_derefs_;
+ }
+
+ virtual bool VisitCallExpr(clang::CallExpr* expr) {
+ has_gc_ = has_gc_ || CanCauseGC(expr);
+ return !has_gc_ || !has_derefs_;
+ }
+ private:
+ void MarkHandleDereferenceAsArgument(clang::CallExpr* call) {
+ derefs_.reset();
+
+ if (clang::CXXMemberCallExpr* memcall =
+ dyn_cast<clang::CXXMemberCallExpr>(call)) {
+ if (ManipulatesRawPointers(memcall->getImplicitObjectArgument())) {
+ derefs_.set(0);
+ }
+ }
+
+ for (unsigned arg = 0; arg < call->getNumArgs(); arg++) {
+ if (ManipulatesRawPointers(call->getArg(arg))) derefs_.set(arg + 1);
+ }
+ }
+
+ void MarkGCSuspectAsArgument(clang::CallExpr* call) {
+ gc_.reset();
+
+ clang::CXXMemberCallExpr* memcall =
+ dyn_cast_or_null<clang::CXXMemberCallExpr>(call);
+ if (memcall != NULL && CanCauseGC(memcall->getImplicitObjectArgument())) {
+ gc_.set(0);
+ }
+
+ for (unsigned arg = 0; arg < call->getNumArgs(); arg++) {
+ if (CanCauseGC(call->getArg(arg))) gc_.set(arg + 1);
+ }
+ }
+
+ const clang::TagType* ToTagType(const clang::Type* t) {
+ if (t == NULL) {
+ return NULL;
+ } else if (isa<clang::TagType>(t)) {
+ return cast<clang::TagType>(t);
+ } else if (isa<clang::SubstTemplateTypeParmType>(t)) {
+ return ToTagType(cast<clang::SubstTemplateTypeParmType>(t)->
+ getReplacementType().getTypePtr());
+ } else {
+ return NULL;
+ }
+ }
+
+ bool IsRawPointerType(clang::Expr* expr) {
+ clang::QualType result = expr->getType();
+
+ const clang::PointerType* type =
+ dyn_cast_or_null<clang::PointerType>(expr->getType().getTypePtr());
+ if (type == NULL) return false;
+
+ const clang::TagType* pointee =
+ ToTagType(type->getPointeeType().getTypePtr());
+ if (pointee == NULL) return false;
+
+ clang::CXXRecordDecl* record =
+ dyn_cast_or_null<clang::CXXRecordDecl>(pointee->getDecl());
+ if (record == NULL) return false;
+
+ return InV8Namespace(record) &&
+ record->hasDefinition() &&
+ ((record == objectDecl_) || record->isDerivedFrom(objectDecl_));
+ }
+
+ bool IsHandleDereference(clang::Expr* expr) {
+ if (expr == NULL) {
+ return false;
+ } else if (isa<clang::UnaryOperator>(expr)) {
+ clang::UnaryOperator* unop = cast<clang::UnaryOperator>(expr);
+ return unop->getOpcode() == clang::UO_Deref &&
+ IsHandleType(handleDeclName_, unop->getSubExpr()->getType());
+ } else if (isa<clang::CXXOperatorCallExpr>(expr)) {
+ clang::CXXOperatorCallExpr* op = cast<clang::CXXOperatorCallExpr>(expr);
+ return (op->getOperator() == clang::OO_Star ||
+ op->getOperator() == clang::OO_Arrow) &&
+ IsHandleType(handleDeclName_, op->getArg(0)->getType());
+ } else {
+ return false;
+ }
+ }
+
+ bool CanCauseGC(clang::Expr* expr) {
+ if (expr == NULL) return false;
+
+ has_gc_ = false;
+ has_derefs_ = true;
+ TraverseStmt(expr);
+ return has_gc_;
+ }
+
+ bool ManipulatesRawPointers(clang::Expr* expr) {
+ if (expr == NULL) return false;
+
+ has_gc_ = true;
+ has_derefs_ = false;
+ TraverseStmt(expr);
+ return has_derefs_;
+ }
+
+ bool CanCauseGC(const clang::CallExpr* call) {
+ const clang::FunctionDecl* fn = call->getDirectCallee();
+ return (fn != NULL) && KnownToCauseGC(ctx_, fn);
+ }
+
+ // For generic expression classification.
+ bool has_derefs_;
+ bool has_gc_;
+
+ // For callsite classification.
+ static const int kMaxNumberOfArguments = 64;
+ std::bitset<kMaxNumberOfArguments> derefs_;
+ std::bitset<kMaxNumberOfArguments> gc_;
+
+ clang::DeclarationName handleDeclName_;
+ clang::MangleContext* ctx_;
+ clang::CXXRecordDecl* objectDecl_;
+};
+
+const std::string BAD_EXPRESSION_MSG("Possible problem with evaluation order.");
+
+class ExpressionsFinder : public clang::ASTConsumer,
+ public clang::RecursiveASTVisitor<ExpressionsFinder> {
+ public:
+ explicit ExpressionsFinder(clang::Diagnostic& d, clang::SourceManager& sm)
+ : d_(d), sm_(sm) { }
+
+ struct Resolver {
+ explicit Resolver(clang::ASTContext& ctx)
+ : ctx_(ctx), decl_ctx_(ctx.getTranslationUnitDecl()) {
+ }
+
+ Resolver(clang::ASTContext& ctx, clang::DeclContext* decl_ctx)
+ : ctx_(ctx), decl_ctx_(decl_ctx) {
+ }
+
+ clang::DeclarationName ResolveName(const char* n) {
+ clang::IdentifierInfo* ident = &ctx_.Idents.get(n);
+ return ctx_.DeclarationNames.getIdentifier(ident);
+ }
+
+ Resolver ResolveNamespace(const char* n) {
+ return Resolver(ctx_, Resolve<clang::NamespaceDecl>(n));
+ }
+
+ template<typename T>
+ T* Resolve(const char* n) {
+ if (decl_ctx_ == NULL) return NULL;
+
+ clang::DeclContext::lookup_result result =
+ decl_ctx_->lookup(ResolveName(n));
+
+ for (clang::DeclContext::lookup_iterator i = result.first,
+ e = result.second;
+ i != e;
+ i++) {
+ if (isa<T>(*i)) return cast<T>(*i);
+ }
+
+ return NULL;
+ }
+
+ private:
+ clang::ASTContext& ctx_;
+ clang::DeclContext* decl_ctx_;
+ };
+
+ virtual void HandleTranslationUnit(clang::ASTContext &ctx) {
+ Resolver r(ctx);
+
+ clang::CXXRecordDecl* objectDecl =
+ r.ResolveNamespace("v8").ResolveNamespace("internal").
+ Resolve<clang::CXXRecordDecl>("Object");
+
+ if (objectDecl != NULL) {
+ expression_classifier_ =
+ new ExpressionClassifier(r.ResolveName("Handle"),
+ clang::createItaniumMangleContext(ctx, d_),
+ objectDecl);
+ TraverseDecl(ctx.getTranslationUnitDecl());
+ } else {
+ std::cerr << "Failed to resolve v8::internal::Object" << std::endl;
+ }
+ }
+
+ virtual bool VisitExpr(clang::Expr* expr) {
+ if ( expression_classifier_->IsBadCallSite(expr) ) {
+ d_.Report(clang::FullSourceLoc(expr->getExprLoc(), sm_),
+ d_.getCustomDiagID(clang::Diagnostic::Warning,
+ BAD_EXPRESSION_MSG));
+ }
+
+ return true;
+ }
+
+ private:
+ clang::Diagnostic& d_;
+ clang::SourceManager& sm_;
+
+ ExpressionClassifier* expression_classifier_;
+};
+
+
+template<typename ConsumerType>
+class Action : public clang::PluginASTAction {
+ protected:
+ clang::ASTConsumer *CreateASTConsumer(clang::CompilerInstance &CI,
+ llvm::StringRef InFile) {
+ return new ConsumerType(CI.getDiagnostics(), CI.getSourceManager());
+ }
+
+ bool ParseArgs(const clang::CompilerInstance &CI,
+ const std::vector<std::string>& args) {
+ return true;
+ }
+
+ void PrintHelp(llvm::raw_ostream& ros) { }
+};
+
+
+}
+
+static clang::FrontendPluginRegistry::Add<Action<ExpressionsFinder> >
+FindProblems("find-problems", "Find possible problems with evaluations order.");
+
+static clang::FrontendPluginRegistry::Add<Action<FunctionDeclarationFinder> >
+DumpCallees("dump-callees", "Dump callees for each function.");
diff --git a/tools/gcmole/gcmole.lua b/tools/gcmole/gcmole.lua
new file mode 100644
index 0000000..7fb8de0
--- /dev/null
+++ b/tools/gcmole/gcmole.lua
@@ -0,0 +1,275 @@
+-- Copyright 2011 the V8 project authors. All rights reserved.
+-- Redistribution and use in source and binary forms, with or without
+-- modification, are permitted provided that the following conditions are
+-- met:
+--
+-- * Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- * Redistributions in binary form must reproduce the above
+-- copyright notice, this list of conditions and the following
+-- disclaimer in the documentation and/or other materials provided
+-- with the distribution.
+-- * Neither the name of Google Inc. nor the names of its
+-- contributors may be used to endorse or promote products derived
+-- from this software without specific prior written permission.
+--
+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+-- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-- This is main driver for gcmole tool. See README for more details.
+-- Usage: CLANG_BIN=clang-bin-dir lua tools/gcmole/gcmole.lua [arm|ia32|x64]
+
+local DIR = arg[0]:match("^(.+)/[^/]+$")
+
+local ARCHS = arg[1] and { arg[1] } or { 'ia32', 'arm', 'x64' }
+
+local io = require "io"
+local os = require "os"
+
+function log(...)
+ io.stderr:write(string.format(...))
+ io.stderr:write "\n"
+end
+
+-------------------------------------------------------------------------------
+-- Clang invocation
+
+local CLANG_BIN = os.getenv "CLANG_BIN"
+
+if not CLANG_BIN or CLANG_BIN == "" then
+ error "CLANG_BIN not set"
+end
+
+local function MakeClangCommandLine(plugin, triple, arch_define)
+ return CLANG_BIN .. "/clang -cc1 -load " .. DIR .. "/libgcmole.so"
+ .. " -plugin " .. plugin
+ .. " -triple " .. triple
+ .. " -D" .. arch_define
+ .. " -DENABLE_VMSTATE_TRACKING"
+ .. " -DENABLE_LOGGING_AND_PROFILING"
+ .. " -DENABLE_DEBUGGER_SUPPORT"
+ .. " -Isrc"
+end
+
+function InvokeClangPluginForEachFile(filenames, cfg, func)
+ local cmd_line = MakeClangCommandLine(cfg.plugin,
+ cfg.triple,
+ cfg.arch_define)
+
+ for _, filename in ipairs(filenames) do
+ log("-- %s", filename)
+
+ local action = cmd_line .. " src/" .. filename .. " 2>&1"
+
+ local pipe = io.popen(action)
+ func(filename, pipe:lines())
+ pipe:close()
+ end
+end
+
+-------------------------------------------------------------------------------
+-- SConscript parsing
+
+local function ParseSConscript()
+ local f = assert(io.open("src/SConscript"), "failed to open SConscript")
+ local sconscript = f:read('*a')
+ f:close()
+
+ local SOURCES = sconscript:match "SOURCES = {(.-)}";
+
+ local sources = {}
+
+ for condition, list in
+ SOURCES:gmatch "'([^']-)': Split%(\"\"\"(.-)\"\"\"%)" do
+ local files = {}
+ for file in list:gmatch "[^%s]+" do table.insert(files, file) end
+ sources[condition] = files
+ end
+
+ for condition, list in SOURCES:gmatch "'([^']-)': %[(.-)%]" do
+ local files = {}
+ for file in list:gmatch "'([^']-)'" do table.insert(files, file) end
+ sources[condition] = files
+ end
+
+ return sources
+end
+
+local function EvaluateCondition(cond, props)
+ if cond == 'all' then return true end
+
+ local p, v = cond:match "(%w+):(%w+)"
+
+ assert(p and v, "failed to parse condition: " .. cond)
+ assert(props[p] ~= nil, "undefined configuration property: " .. p)
+
+ return props[p] == v
+end
+
+local function BuildFileList(sources, props)
+ local list = {}
+ for condition, files in pairs(sources) do
+ if EvaluateCondition(condition, props) then
+ for i = 1, #files do table.insert(list, files[i]) end
+ end
+ end
+ return list
+end
+
+local sources = ParseSConscript()
+
+local function FilesForArch(arch)
+ return BuildFileList(sources, { os = 'linux',
+ arch = arch,
+ mode = 'debug',
+ simulator = ''})
+end
+
+local mtConfig = {}
+
+mtConfig.__index = mtConfig
+
+local function config (t) return setmetatable(t, mtConfig) end
+
+function mtConfig:extend(t)
+ local e = {}
+ for k, v in pairs(self) do e[k] = v end
+ for k, v in pairs(t) do e[k] = v end
+ return config(e)
+end
+
+local ARCHITECTURES = {
+ ia32 = config { triple = "i586-unknown-linux",
+ arch_define = "V8_TARGET_ARCH_IA32" },
+ arm = config { triple = "i586-unknown-linux",
+ arch_define = "V8_TARGET_ARCH_ARM" },
+ x64 = config { triple = "x86_64-unknown-linux",
+ arch_define = "V8_TARGET_ARCH_X64" }
+}
+
+-------------------------------------------------------------------------------
+-- GCSuspects Generation
+
+local gc = {}
+local funcs = {}
+
+local function resolve(name)
+ local f = funcs[name]
+
+ if not f then
+ f = {}
+ funcs[name] = f
+
+ if name:match "Collect.*Garbage" then gc[name] = true end
+ end
+
+ return f
+end
+
+local function parse (filename, lines)
+ local scope
+
+ for funcname in lines do
+ if funcname:sub(1, 1) ~= '\t' then
+ resolve(funcname)
+ scope = funcname
+ else
+ local name = funcname:sub(2)
+ resolve(name)[scope] = true
+ end
+ end
+end
+
+local function propagate ()
+ log "** Propagating GC information"
+
+ local function mark(callers)
+ for caller, _ in pairs(callers) do
+ if not gc[caller] then
+ gc[caller] = true
+ mark(funcs[caller])
+ end
+ end
+ end
+
+ for funcname, callers in pairs(funcs) do
+ if gc[funcname] then mark(callers) end
+ end
+end
+
+local function GenerateGCSuspects(arch, files, cfg)
+ log ("** Building GC Suspects for %s", arch)
+ InvokeClangPluginForEachFile (files,
+ cfg:extend { plugin = "dump-callees" },
+ parse)
+
+ propagate()
+
+ local out = assert(io.open("gcsuspects", "w"))
+ for name, _ in pairs(gc) do out:write (name, '\n') end
+ out:close()
+ log ("** GCSuspects generated for %s", arch)
+end
+
+-------------------------------------------------------------------------------
+-- Analysis
+
+local function CheckCorrectnessForArch(arch)
+ local files = FilesForArch(arch)
+ local cfg = ARCHITECTURES[arch]
+
+ GenerateGCSuspects(arch, files, cfg)
+
+ local processed_files = 0
+ local errors_found = false
+ local function SearchForErrors(filename, lines)
+ processed_files = processed_files + 1
+ for l in lines do
+ errors_found = errors_found or
+ l:match "^[^:]+:%d+:%d+:" or
+ l:match "error" or
+ l:match "warning"
+ print(l)
+ end
+ end
+
+ log("** Searching for evaluation order problems for %s", arch)
+ InvokeClangPluginForEachFile(files,
+ cfg:extend { plugin = "find-problems" },
+ SearchForErrors)
+ log("** Done processing %d files. %s",
+ processed_files,
+ errors_found and "Errors found" or "No errors found")
+
+ return errors_found
+end
+
+local function SafeCheckCorrectnessForArch(arch)
+ local status, errors = pcall(CheckCorrectnessForArch, arch)
+ if not status then
+ print(string.format("There was an error: %s", errors))
+ errors = true
+ end
+ return errors
+end
+
+local errors = false
+
+for _, arch in ipairs(ARCHS) do
+ if not ARCHITECTURES[arch] then
+ error ("Unknown arch: " .. arch)
+ end
+
+ errors = SafeCheckCorrectnessForArch(arch, report) or errors
+end
+
+os.exit(errors and 1 or 0)
diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp
index 8804454..a11d19a 100644
--- a/tools/gyp/v8.gyp
+++ b/tools/gyp/v8.gyp
@@ -230,7 +230,8 @@
'../../src',
],
'sources': [
- '<(SHARED_INTERMEDIATE_DIR)/libraries-empty.cc',
+ '<(SHARED_INTERMEDIATE_DIR)/libraries.cc',
+ '<(SHARED_INTERMEDIATE_DIR)/experimental-libraries.cc',
'<(INTERMEDIATE_DIR)/snapshot.cc',
],
'actions': [
@@ -259,6 +260,7 @@
],
'sources': [
'<(SHARED_INTERMEDIATE_DIR)/libraries.cc',
+ '<(SHARED_INTERMEDIATE_DIR)/experimental-libraries.cc',
'../../src/snapshot-empty.cc',
],
'conditions': [
@@ -399,9 +401,6 @@
'../../src/inspector.h',
'../../src/interpreter-irregexp.cc',
'../../src/interpreter-irregexp.h',
- '../../src/jump-target-inl.h',
- '../../src/jump-target.cc',
- '../../src/jump-target.h',
'../../src/jsregexp.cc',
'../../src/jsregexp.h',
'../../src/isolate.cc',
@@ -462,9 +461,6 @@
'../../src/regexp-macro-assembler.h',
'../../src/regexp-stack.cc',
'../../src/regexp-stack.h',
- '../../src/register-allocator.h',
- '../../src/register-allocator-inl.h',
- '../../src/register-allocator.cc',
'../../src/rewriter.cc',
'../../src/rewriter.h',
'../../src/runtime.cc',
@@ -526,9 +522,6 @@
'../../src/variables.h',
'../../src/version.cc',
'../../src/version.h',
- '../../src/virtual-frame-inl.h',
- '../../src/virtual-frame.cc',
- '../../src/virtual-frame.h',
'../../src/vm-state-inl.h',
'../../src/vm-state.h',
'../../src/zone-inl.h',
@@ -545,11 +538,6 @@
'../../src/arm',
],
'sources': [
- '../../src/jump-target-light.h',
- '../../src/jump-target-light-inl.h',
- '../../src/jump-target-light.cc',
- '../../src/virtual-frame-light-inl.h',
- '../../src/virtual-frame-light.cc',
'../../src/arm/assembler-arm-inl.h',
'../../src/arm/assembler-arm.cc',
'../../src/arm/assembler-arm.h',
@@ -568,7 +556,6 @@
'../../src/arm/frames-arm.h',
'../../src/arm/full-codegen-arm.cc',
'../../src/arm/ic-arm.cc',
- '../../src/arm/jump-target-arm.cc',
'../../src/arm/lithium-arm.cc',
'../../src/arm/lithium-arm.h',
'../../src/arm/lithium-codegen-arm.cc',
@@ -579,12 +566,8 @@
'../../src/arm/macro-assembler-arm.h',
'../../src/arm/regexp-macro-assembler-arm.cc',
'../../src/arm/regexp-macro-assembler-arm.h',
- '../../src/arm/register-allocator-arm.cc',
'../../src/arm/simulator-arm.cc',
'../../src/arm/stub-cache-arm.cc',
- '../../src/arm/virtual-frame-arm-inl.h',
- '../../src/arm/virtual-frame-arm.cc',
- '../../src/arm/virtual-frame-arm.h',
],
'conditions': [
# The ARM assembler assumes the host is 32 bits,
@@ -600,11 +583,6 @@
'../../src/ia32',
],
'sources': [
- '../../src/jump-target-heavy.h',
- '../../src/jump-target-heavy-inl.h',
- '../../src/jump-target-heavy.cc',
- '../../src/virtual-frame-heavy-inl.h',
- '../../src/virtual-frame-heavy.cc',
'../../src/ia32/assembler-ia32-inl.h',
'../../src/ia32/assembler-ia32.cc',
'../../src/ia32/assembler-ia32.h',
@@ -621,7 +599,6 @@
'../../src/ia32/frames-ia32.h',
'../../src/ia32/full-codegen-ia32.cc',
'../../src/ia32/ic-ia32.cc',
- '../../src/ia32/jump-target-ia32.cc',
'../../src/ia32/lithium-codegen-ia32.cc',
'../../src/ia32/lithium-codegen-ia32.h',
'../../src/ia32/lithium-gap-resolver-ia32.cc',
@@ -632,10 +609,7 @@
'../../src/ia32/macro-assembler-ia32.h',
'../../src/ia32/regexp-macro-assembler-ia32.cc',
'../../src/ia32/regexp-macro-assembler-ia32.h',
- '../../src/ia32/register-allocator-ia32.cc',
'../../src/ia32/stub-cache-ia32.cc',
- '../../src/ia32/virtual-frame-ia32.cc',
- '../../src/ia32/virtual-frame-ia32.h',
],
}],
['v8_target_arch=="x64" or v8_target_arch=="mac" or OS=="mac"', {
@@ -643,11 +617,6 @@
'../../src/x64',
],
'sources': [
- '../../src/jump-target-heavy.h',
- '../../src/jump-target-heavy-inl.h',
- '../../src/jump-target-heavy.cc',
- '../../src/virtual-frame-heavy-inl.h',
- '../../src/virtual-frame-heavy.cc',
'../../src/x64/assembler-x64-inl.h',
'../../src/x64/assembler-x64.cc',
'../../src/x64/assembler-x64.h',
@@ -664,7 +633,6 @@
'../../src/x64/frames-x64.h',
'../../src/x64/full-codegen-x64.cc',
'../../src/x64/ic-x64.cc',
- '../../src/x64/jump-target-x64.cc',
'../../src/x64/lithium-codegen-x64.cc',
'../../src/x64/lithium-codegen-x64.h',
'../../src/x64/lithium-gap-resolver-x64.cc',
@@ -675,10 +643,7 @@
'../../src/x64/macro-assembler-x64.h',
'../../src/x64/regexp-macro-assembler-x64.cc',
'../../src/x64/regexp-macro-assembler-x64.h',
- '../../src/x64/register-allocator-x64.cc',
'../../src/x64/stub-cache-x64.cc',
- '../../src/x64/virtual-frame-x64.cc',
- '../../src/x64/virtual-frame-x64.h',
],
}],
['OS=="linux"', {
@@ -759,6 +724,10 @@
'../../src/regexp.js',
'../../src/macros.py',
],
+ 'experimental_library_files': [
+ '../../src/proxy.js',
+ '../../src/macros.py',
+ ],
},
'actions': [
{
@@ -769,7 +738,6 @@
],
'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/libraries.cc',
- '<(SHARED_INTERMEDIATE_DIR)/libraries-empty.cc',
],
'action': [
'python',
@@ -779,6 +747,23 @@
'<@(library_files)'
],
},
+ {
+ 'action_name': 'js2c_experimental',
+ 'inputs': [
+ '../../tools/js2c.py',
+ '<@(experimental_library_files)',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/experimental-libraries.cc',
+ ],
+ 'action': [
+ 'python',
+ '../../tools/js2c.py',
+ '<@(_outputs)',
+ 'EXPERIMENTAL',
+ '<@(experimental_library_files)'
+ ],
+ },
],
},
{
diff --git a/tools/js2c.py b/tools/js2c.py
old mode 100755
new mode 100644
index 2da132f..8211ec5
--- a/tools/js2c.py
+++ b/tools/js2c.py
@@ -204,7 +204,7 @@
HEADER_TEMPLATE = """\
-// Copyright 2008 Google Inc. All Rights Reserved.
+// Copyright 2011 Google Inc. All Rights Reserved.
// This file was generated from .js source files by SCons. If you
// want to make changes to this file you should either change the
@@ -288,7 +288,6 @@
minifier = jsmin.JavaScriptMinifier()
- source_lines_empty = []
for module in modules:
filename = str(module)
debugger = filename.endswith('-debugger.js')
@@ -305,7 +304,6 @@
else:
ids.append((id, len(lines)))
source_lines.append(SOURCE_DECLARATION % { 'id': id, 'data': data })
- source_lines_empty.append(SOURCE_DECLARATION % { 'id': id, 'data': data })
# Build debugger support functions
get_index_cases = [ ]
@@ -356,25 +354,11 @@
})
output.close()
- if len(target) > 1:
- output = open(str(target[1]), "w")
- output.write(HEADER_TEMPLATE % {
- 'builtin_count': len(ids) + len(debugger_ids),
- 'debugger_count': len(debugger_ids),
- 'source_lines': "\n".join(source_lines_empty),
- 'get_index_cases': "".join(get_index_cases),
- 'get_script_source_cases': "".join(get_script_source_cases),
- 'get_script_name_cases': "".join(get_script_name_cases),
- 'type': env['TYPE']
- })
- output.close()
-
def main():
natives = sys.argv[1]
- natives_empty = sys.argv[2]
- type = sys.argv[3]
- source_files = sys.argv[4:]
- JS2C(source_files, [natives, natives_empty], { 'TYPE': type })
+ type = sys.argv[2]
+ source_files = sys.argv[3:]
+ JS2C(source_files, [natives], { 'TYPE': type })
if __name__ == "__main__":
main()
diff --git a/tools/test.py b/tools/test.py
index 066a559..c1840bb 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -117,6 +117,8 @@
start = time.time()
output = case.Run()
case.duration = (time.time() - start)
+ except BreakNowException:
+ self.terminate = True
except IOError, e:
assert self.terminate
return
@@ -318,6 +320,12 @@
# --- F r a m e w o r k ---
# -------------------------
+class BreakNowException(Exception):
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return repr(self.value)
+
class CommandOutput(object):
@@ -379,8 +387,12 @@
def Run(self):
self.BeforeRun()
+ result = None
try:
result = self.RunCommand(self.GetCommand())
+ except:
+ self.terminate = True
+ raise BreakNowException("Used pressed CTRL+C or IO went wrong")
finally:
self.AfterRun(result)
return result
@@ -422,7 +434,7 @@
self.output.exit_code != -signal.SIGABRT
def HasTimedOut(self):
- return self.output.timed_out;
+ return self.output.timed_out
def HasFailed(self):
execution_failed = self.test.DidFail(self.output)
@@ -450,7 +462,7 @@
prev_error_mode = SEM_INVALID_VALUE
try:
import ctypes
- prev_error_mode = ctypes.windll.kernel32.SetErrorMode(mode);
+ prev_error_mode = ctypes.windll.kernel32.SetErrorMode(mode)
except ImportError:
pass
return prev_error_mode
@@ -458,16 +470,16 @@
def RunProcess(context, timeout, args, **rest):
if context.verbose: print "#", " ".join(args)
popen_args = args
- prev_error_mode = SEM_INVALID_VALUE;
+ prev_error_mode = SEM_INVALID_VALUE
if utils.IsWindows():
popen_args = '"' + subprocess.list2cmdline(args) + '"'
if context.suppress_dialogs:
# Try to change the error mode to avoid dialogs on fatal errors. Don't
# touch any existing error mode flags by merging the existing error mode.
# See http://blogs.msdn.com/oldnewthing/archive/2004/07/27/198410.aspx.
- error_mode = SEM_NOGPFAULTERRORBOX;
- prev_error_mode = Win32SetErrorMode(error_mode);
- Win32SetErrorMode(error_mode | prev_error_mode);
+ error_mode = SEM_NOGPFAULTERRORBOX
+ prev_error_mode = Win32SetErrorMode(error_mode)
+ Win32SetErrorMode(error_mode | prev_error_mode)
process = subprocess.Popen(
shell = utils.IsWindows(),
args = popen_args,
@@ -515,7 +527,7 @@
os.unlink(name)
return
except OSError, e:
- retry_count += 1;
+ retry_count += 1
time.sleep(retry_count * 0.1)
PrintError("os.unlink() " + str(e))
@@ -583,7 +595,9 @@
# Use this to run several variants of the tests, e.g.:
# VARIANT_FLAGS = [[], ['--always_compact', '--noflush_code']]
-VARIANT_FLAGS = [[], ['--stress-opt', '--always-opt'], ['--nocrankshaft']]
+VARIANT_FLAGS = [[],
+ ['--stress-opt', '--always-opt'],
+ ['--nocrankshaft']]
class TestRepository(TestSuite):
@@ -699,7 +713,12 @@
def RunTestCases(cases_to_run, progress, tasks):
progress = PROGRESS_INDICATORS[progress](cases_to_run)
- return progress.Run(tasks)
+ result = 0
+ try:
+ result = progress.Run(tasks)
+ except Exception, e:
+ print "\n", e
+ return result
def BuildRequirements(context, requirements, mode, scons_flags):
@@ -1316,7 +1335,7 @@
return ExpandCommand
-BUILT_IN_TESTS = ['mjsunit', 'cctest', 'message']
+BUILT_IN_TESTS = ['mjsunit', 'cctest', 'message', 'preparser']
def GetSuites(test_root):
@@ -1336,11 +1355,11 @@
print "shard-run not a valid number, should be in [1:shard-count]"
print "defaulting back to running all tests"
return tests
- count = 0;
+ count = 0
shard = []
for test in tests:
if count % options.shard_count == options.shard_run - 1:
- shard.append(test);
+ shard.append(test)
count += 1
return shard
@@ -1409,9 +1428,6 @@
globally_unused_rules = None
for path in paths:
for mode in options.mode:
- if not exists(context.GetVm(mode)):
- print "Can't find shell executable: '%s'" % context.GetVm(mode)
- continue
env = {
'mode': mode,
'system': utils.GuessOS(),
diff --git a/tools/tickprocessor.js b/tools/tickprocessor.js
index 7a05ef1..9d6bfb6 100644
--- a/tools/tickprocessor.js
+++ b/tools/tickprocessor.js
@@ -345,7 +345,6 @@
return this.stateFilter_ == null || this.stateFilter_ == vmState;
};
-
TickProcessor.prototype.processTick = function(pc,
sp,
is_external_callback,
@@ -361,8 +360,10 @@
if (is_external_callback) {
// Don't use PC when in external callback code, as it can point
// inside callback's code, and we will erroneously report
- // that a callback calls itself.
- pc = 0;
+ // that a callback calls itself. Instead we use tos_or_external_callback,
+ // as simply resetting PC will produce unaccounted ticks.
+ pc = tos_or_external_callback;
+ tos_or_external_callback = 0;
} else if (tos_or_external_callback) {
// Find out, if top of stack was pointing inside a JS function
// meaning that we have encountered a frameless invocation.
diff --git a/tools/v8.xcodeproj/project.pbxproj b/tools/v8.xcodeproj/project.pbxproj
index 386e7f0..77ac2e1 100644
--- a/tools/v8.xcodeproj/project.pbxproj
+++ b/tools/v8.xcodeproj/project.pbxproj
@@ -29,18 +29,6 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
- 58950D5E0F55519800F3E8BA /* jump-target.cc in Sources */ = {isa = PBXBuildFile; fileRef = 58950D500F55514900F3E8BA /* jump-target.cc */; };
- 58950D5F0F55519D00F3E8BA /* jump-target-heavy.cc in Sources */ = {isa = PBXBuildFile; fileRef = 58950D4F0F55514900F3E8BA /* jump-target-heavy.cc */; };
- 58950D600F5551A300F3E8BA /* jump-target.cc in Sources */ = {isa = PBXBuildFile; fileRef = 58950D500F55514900F3E8BA /* jump-target.cc */; };
- 58950D610F5551A400F3E8BA /* jump-target-light.cc in Sources */ = {isa = PBXBuildFile; fileRef = 58950D4E0F55514900F3E8BA /* jump-target-light.cc */; };
- 58950D620F5551AF00F3E8BA /* register-allocator-ia32.cc in Sources */ = {isa = PBXBuildFile; fileRef = 58950D530F55514900F3E8BA /* register-allocator-ia32.cc */; };
- 58950D630F5551AF00F3E8BA /* register-allocator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 58950D540F55514900F3E8BA /* register-allocator.cc */; };
- 58950D640F5551B500F3E8BA /* register-allocator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 58950D540F55514900F3E8BA /* register-allocator.cc */; };
- 58950D650F5551B600F3E8BA /* register-allocator-arm.cc in Sources */ = {isa = PBXBuildFile; fileRef = 58950D520F55514900F3E8BA /* register-allocator-arm.cc */; };
- 58950D660F5551C200F3E8BA /* virtual-frame.cc in Sources */ = {isa = PBXBuildFile; fileRef = 58950D5A0F55514900F3E8BA /* virtual-frame.cc */; };
- 58950D670F5551C400F3E8BA /* virtual-frame-heavy.cc in Sources */ = {isa = PBXBuildFile; fileRef = 58950D580F55514900F3E8BA /* virtual-frame-heavy.cc */; };
- 58950D680F5551CB00F3E8BA /* virtual-frame.cc in Sources */ = {isa = PBXBuildFile; fileRef = 58950D5A0F55514900F3E8BA /* virtual-frame.cc */; };
- 58950D690F5551CE00F3E8BA /* virtual-frame-light.cc in Sources */ = {isa = PBXBuildFile; fileRef = 58950D560F55514900F3E8BA /* virtual-frame-light.cc */; };
8900116C0E71CA2300F91F35 /* libraries.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8900116B0E71CA2300F91F35 /* libraries.cc */; };
890A13FE0EE9C47F00E49346 /* interpreter-irregexp.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89A15C660EE4665300B48DEB /* interpreter-irregexp.cc */; };
890A14010EE9C4B000E49346 /* regexp-macro-assembler-arm.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89A15C700EE466D000B48DEB /* regexp-macro-assembler-arm.cc */; };
@@ -147,8 +135,6 @@
8956925A12D4ED240072C313 /* ic.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF14C0E719B8F00D62E90 /* ic.cc */; };
8956925B12D4ED240072C313 /* interpreter-irregexp.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89A15C660EE4665300B48DEB /* interpreter-irregexp.cc */; };
8956925C12D4ED240072C313 /* jsregexp.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF14E0E719B8F00D62E90 /* jsregexp.cc */; };
- 8956925D12D4ED240072C313 /* jump-target-heavy.cc in Sources */ = {isa = PBXBuildFile; fileRef = 58950D4F0F55514900F3E8BA /* jump-target-heavy.cc */; };
- 8956925F12D4ED240072C313 /* jump-target.cc in Sources */ = {isa = PBXBuildFile; fileRef = 58950D500F55514900F3E8BA /* jump-target.cc */; };
8956926012D4ED240072C313 /* libraries.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8900116B0E71CA2300F91F35 /* libraries.cc */; };
8956926112D4ED240072C313 /* liveedit.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FA38BA91175B2D200C4CD55 /* liveedit.cc */; };
8956926212D4ED240072C313 /* log-utils.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F4B7B870FCC877A00DC4117 /* log-utils.cc */; };
@@ -167,7 +153,6 @@
8956927212D4ED240072C313 /* regexp-macro-assembler-tracer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89A15C770EE466D000B48DEB /* regexp-macro-assembler-tracer.cc */; };
8956927312D4ED240072C313 /* regexp-macro-assembler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89A15C790EE466D000B48DEB /* regexp-macro-assembler.cc */; };
8956927412D4ED240072C313 /* regexp-stack.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8944AD0E0F1D4D3A0028D560 /* regexp-stack.cc */; };
- 8956927612D4ED240072C313 /* register-allocator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 58950D540F55514900F3E8BA /* register-allocator.cc */; };
8956927712D4ED240072C313 /* rewriter.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF16F0E719B8F00D62E90 /* rewriter.cc */; };
8956927812D4ED240072C313 /* runtime.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1710E719B8F00D62E90 /* runtime.cc */; };
8956927912D4ED240072C313 /* scanner.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1730E719B8F00D62E90 /* scanner.cc */; };
@@ -189,8 +174,6 @@
8956928A12D4ED240072C313 /* v8threads.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF19D0E719B8F00D62E90 /* v8threads.cc */; };
8956928B12D4ED240072C313 /* variables.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF19F0E719B8F00D62E90 /* variables.cc */; };
8956928C12D4ED240072C313 /* version.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF32F0FAA0ED200136CF6 /* version.cc */; };
- 8956928D12D4ED240072C313 /* virtual-frame-heavy.cc in Sources */ = {isa = PBXBuildFile; fileRef = 58950D580F55514900F3E8BA /* virtual-frame-heavy.cc */; };
- 8956928F12D4ED240072C313 /* virtual-frame.cc in Sources */ = {isa = PBXBuildFile; fileRef = 58950D5A0F55514900F3E8BA /* virtual-frame.cc */; };
8956929012D4ED240072C313 /* zone.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1A20E719B8F00D62E90 /* zone.cc */; };
8956929212D4ED240072C313 /* bignum-dtoa.cc in Sources */ = {isa = PBXBuildFile; fileRef = 893E248612B14B3D0083370F /* bignum-dtoa.cc */; };
8956929312D4ED240072C313 /* bignum.cc in Sources */ = {isa = PBXBuildFile; fileRef = 893E248812B14B3D0083370F /* bignum.cc */; };
@@ -307,13 +290,10 @@
89B91BA312D4EF95002FF4BC /* frames-x64.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89B91B8812D4EF95002FF4BC /* frames-x64.cc */; };
89B91BA412D4EF95002FF4BC /* full-codegen-x64.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89B91B8A12D4EF95002FF4BC /* full-codegen-x64.cc */; };
89B91BA512D4EF95002FF4BC /* ic-x64.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89B91B8B12D4EF95002FF4BC /* ic-x64.cc */; };
- 89B91BA612D4EF95002FF4BC /* jump-target-x64.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89B91B8C12D4EF95002FF4BC /* jump-target-x64.cc */; };
89B91BA712D4EF95002FF4BC /* macro-assembler-x64.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89B91B8F12D4EF95002FF4BC /* macro-assembler-x64.cc */; };
89B91BA812D4EF95002FF4BC /* regexp-macro-assembler-x64.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89B91B9112D4EF95002FF4BC /* regexp-macro-assembler-x64.cc */; };
- 89B91BA912D4EF95002FF4BC /* register-allocator-x64.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89B91B9412D4EF95002FF4BC /* register-allocator-x64.cc */; };
89B91BAA12D4EF95002FF4BC /* simulator-x64.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89B91B9612D4EF95002FF4BC /* simulator-x64.cc */; };
89B91BAB12D4EF95002FF4BC /* stub-cache-x64.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89B91B9812D4EF95002FF4BC /* stub-cache-x64.cc */; };
- 89B91BAC12D4EF95002FF4BC /* virtual-frame-x64.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89B91B9912D4EF95002FF4BC /* virtual-frame-x64.cc */; };
89B91BB812D4F02A002FF4BC /* shell.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1B50E719C0900D62E90 /* shell.cc */; settings = {COMPILER_FLAGS = "-I../include"; }; };
89B91BC512D4F02A002FF4BC /* d8-debug.cc in Sources */ = {isa = PBXBuildFile; fileRef = 893988150F2A3686007D5254 /* d8-debug.cc */; };
89B91BC612D4F02A002FF4BC /* d8-js.cc in Sources */ = {isa = PBXBuildFile; fileRef = 893988320F2A3B8B007D5254 /* d8-js.cc */; };
@@ -425,11 +405,7 @@
9FA38BBF1175B2D200C4CD55 /* liveedit.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FA38BA91175B2D200C4CD55 /* liveedit.cc */; };
9FA38BC01175B2D200C4CD55 /* type-info.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FA38BAE1175B2D200C4CD55 /* type-info.cc */; };
9FA38BC51175B2E500C4CD55 /* full-codegen-ia32.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FA38BC21175B2E500C4CD55 /* full-codegen-ia32.cc */; };
- 9FA38BC61175B2E500C4CD55 /* jump-target-ia32.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FA38BC31175B2E500C4CD55 /* jump-target-ia32.cc */; };
- 9FA38BC71175B2E500C4CD55 /* virtual-frame-ia32.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FA38BC41175B2E500C4CD55 /* virtual-frame-ia32.cc */; };
9FA38BCF1175B30400C4CD55 /* full-codegen-arm.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FA38BCB1175B30400C4CD55 /* full-codegen-arm.cc */; };
- 9FA38BD01175B30400C4CD55 /* jump-target-arm.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FA38BCC1175B30400C4CD55 /* jump-target-arm.cc */; };
- 9FA38BD11175B30400C4CD55 /* virtual-frame-arm.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FA38BCD1175B30400C4CD55 /* virtual-frame-arm.cc */; };
C2BD4BD7120165460046BF9F /* dtoa.cc in Sources */ = {isa = PBXBuildFile; fileRef = C2BD4BD5120165460046BF9F /* dtoa.cc */; };
C2BD4BDB120165A70046BF9F /* fixed-dtoa.cc in Sources */ = {isa = PBXBuildFile; fileRef = C2BD4BD9120165A70046BF9F /* fixed-dtoa.cc */; };
C2BD4BE4120166180046BF9F /* fixed-dtoa.cc in Sources */ = {isa = PBXBuildFile; fileRef = C2BD4BD9120165A70046BF9F /* fixed-dtoa.cc */; };
@@ -550,20 +526,6 @@
/* Begin PBXFileReference section */
22A76C900FF259E600FDC694 /* log-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "log-inl.h"; sourceTree = "<group>"; };
- 58950D4E0F55514900F3E8BA /* jump-target-light.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "jump-target-light.cc"; sourceTree = "<group>"; };
- 58950D4F0F55514900F3E8BA /* jump-target-heavy.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "jump-target-heavy.cc"; sourceTree = "<group>"; };
- 58950D500F55514900F3E8BA /* jump-target.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "jump-target.cc"; sourceTree = "<group>"; };
- 58950D510F55514900F3E8BA /* jump-target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "jump-target.h"; sourceTree = "<group>"; };
- 58950D520F55514900F3E8BA /* register-allocator-arm.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "register-allocator-arm.cc"; path = "arm/register-allocator-arm.cc"; sourceTree = "<group>"; };
- 58950D530F55514900F3E8BA /* register-allocator-ia32.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "register-allocator-ia32.cc"; path = "ia32/register-allocator-ia32.cc"; sourceTree = "<group>"; };
- 58950D540F55514900F3E8BA /* register-allocator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "register-allocator.cc"; sourceTree = "<group>"; };
- 58950D550F55514900F3E8BA /* register-allocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "register-allocator.h"; sourceTree = "<group>"; };
- 58950D560F55514900F3E8BA /* virtual-frame-light.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "virtual-frame-light.cc"; sourceTree = "<group>"; };
- 58950D570F55514900F3E8BA /* virtual-frame-arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "virtual-frame-arm.h"; path = "arm/virtual-frame-arm.h"; sourceTree = "<group>"; };
- 58950D580F55514900F3E8BA /* virtual-frame-heavy.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "virtual-frame-heavy.cc"; sourceTree = "<group>"; };
- 58950D590F55514900F3E8BA /* virtual-frame-ia32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "virtual-frame-ia32.h"; path = "ia32/virtual-frame-ia32.h"; sourceTree = "<group>"; };
- 58950D5A0F55514900F3E8BA /* virtual-frame.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "virtual-frame.cc"; sourceTree = "<group>"; };
- 58950D5B0F55514900F3E8BA /* virtual-frame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "virtual-frame.h"; sourceTree = "<group>"; };
8900116B0E71CA2300F91F35 /* libraries.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = libraries.cc; sourceTree = "<group>"; };
891C92FD1334226000FF4757 /* lithium-allocator-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "lithium-allocator-inl.h"; sourceTree = "<group>"; };
891C92FE133422EB00FF4757 /* isolate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = isolate.h; sourceTree = "<group>"; };
@@ -576,7 +538,6 @@
893988320F2A3B8B007D5254 /* d8-js.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "d8-js.cc"; sourceTree = "<group>"; };
893A72230F7B0FF200303DD2 /* platform-posix.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "platform-posix.cc"; sourceTree = "<group>"; };
893A722A0F7B4A3200303DD2 /* dateparser-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "dateparser-inl.h"; sourceTree = "<group>"; };
- 893A722D0F7B4A7100303DD2 /* register-allocator-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "register-allocator-inl.h"; sourceTree = "<group>"; };
893A72320F7B4AD700303DD2 /* d8-debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "d8-debug.h"; path = "../src/d8-debug.h"; sourceTree = "<group>"; };
893E248112B14AD40083370F /* v8-preparser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "v8-preparser.h"; sourceTree = "<group>"; };
893E248212B14AD40083370F /* v8-testing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "v8-testing.h"; sourceTree = "<group>"; };
@@ -594,8 +555,6 @@
893E248E12B14B3D0083370F /* hydrogen-instructions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "hydrogen-instructions.h"; sourceTree = "<group>"; };
893E248F12B14B3D0083370F /* hydrogen.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hydrogen.cc; sourceTree = "<group>"; };
893E249012B14B3D0083370F /* hydrogen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hydrogen.h; sourceTree = "<group>"; };
- 893E249112B14B3D0083370F /* jump-target-heavy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "jump-target-heavy.h"; sourceTree = "<group>"; };
- 893E249212B14B3D0083370F /* jump-target-light.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "jump-target-light.h"; sourceTree = "<group>"; };
893E249312B14B3D0083370F /* lithium-allocator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "lithium-allocator.cc"; sourceTree = "<group>"; };
893E249412B14B3D0083370F /* lithium-allocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "lithium-allocator.h"; sourceTree = "<group>"; };
893E249512B14B3D0083370F /* preparse-data.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "preparse-data.cc"; sourceTree = "<group>"; };
@@ -621,7 +580,6 @@
893E24C812B14B510083370F /* lithium-arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "lithium-arm.h"; path = "arm/lithium-arm.h"; sourceTree = "<group>"; };
893E24C912B14B520083370F /* lithium-codegen-arm.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "lithium-codegen-arm.cc"; path = "arm/lithium-codegen-arm.cc"; sourceTree = "<group>"; };
893E24CA12B14B520083370F /* lithium-codegen-arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "lithium-codegen-arm.h"; path = "arm/lithium-codegen-arm.h"; sourceTree = "<group>"; };
- 893E24CB12B14B520083370F /* virtual-frame-arm-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "virtual-frame-arm-inl.h"; path = "arm/virtual-frame-arm-inl.h"; sourceTree = "<group>"; };
893E24CF12B14B780083370F /* atomicops_internals_x86_macosx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = atomicops_internals_x86_macosx.h; sourceTree = "<group>"; };
893E24D012B14B8A0083370F /* deoptimizer-ia32.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "deoptimizer-ia32.cc"; path = "ia32/deoptimizer-ia32.cc"; sourceTree = "<group>"; };
893E24D112B14B8A0083370F /* lithium-codegen-ia32.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "lithium-codegen-ia32.cc"; path = "ia32/lithium-codegen-ia32.cc"; sourceTree = "<group>"; };
@@ -645,14 +603,7 @@
8956B6CE0F5D86570033B5A2 /* debug-agent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "debug-agent.h"; sourceTree = "<group>"; };
895D5B4C1334210400254083 /* allocation-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "allocation-inl.h"; sourceTree = "<group>"; };
895D5B521334212D00254083 /* isolate.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = isolate.cc; sourceTree = "<group>"; };
- 895FA720107FFB15006F39D4 /* jump-target-heavy-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "jump-target-heavy-inl.h"; sourceTree = "<group>"; };
- 895FA725107FFB57006F39D4 /* codegen-ia32-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "codegen-ia32-inl.h"; path = "ia32/codegen-ia32-inl.h"; sourceTree = "<group>"; };
- 895FA72A107FFB85006F39D4 /* register-allocator-ia32-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "register-allocator-ia32-inl.h"; path = "ia32/register-allocator-ia32-inl.h"; sourceTree = "<group>"; };
- 895FA72B107FFB85006F39D4 /* register-allocator-ia32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "register-allocator-ia32.h"; path = "ia32/register-allocator-ia32.h"; sourceTree = "<group>"; };
895FA748107FFE73006F39D4 /* constants-arm.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "constants-arm.cc"; path = "arm/constants-arm.cc"; sourceTree = "<group>"; };
- 895FA74B107FFE82006F39D4 /* codegen-arm-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "codegen-arm-inl.h"; path = "arm/codegen-arm-inl.h"; sourceTree = "<group>"; };
- 895FA750107FFEAE006F39D4 /* register-allocator-arm-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "register-allocator-arm-inl.h"; path = "arm/register-allocator-arm-inl.h"; sourceTree = "<group>"; };
- 895FA751107FFEAE006F39D4 /* register-allocator-arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "register-allocator-arm.h"; path = "arm/register-allocator-arm.h"; sourceTree = "<group>"; };
8964482B0E9C00F700E7C516 /* codegen-ia32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "codegen-ia32.h"; path = "ia32/codegen-ia32.h"; sourceTree = "<group>"; };
896448BC0E9D530500E7C516 /* codegen-arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "codegen-arm.h"; path = "arm/codegen-arm.h"; sourceTree = "<group>"; };
896FA1E3130F93D300042054 /* lithium-gap-resolver-arm.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "lithium-gap-resolver-arm.cc"; path = "arm/lithium-gap-resolver-arm.cc"; sourceTree = "<group>"; };
@@ -694,7 +645,6 @@
897FF1130E719B8F00D62E90 /* code.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = code.h; sourceTree = "<group>"; };
897FF1140E719B8F00D62E90 /* codegen-arm.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "codegen-arm.cc"; path = "arm/codegen-arm.cc"; sourceTree = "<group>"; };
897FF1150E719B8F00D62E90 /* codegen-ia32.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "codegen-ia32.cc"; path = "ia32/codegen-ia32.cc"; sourceTree = "<group>"; };
- 897FF1160E719B8F00D62E90 /* codegen-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "codegen-inl.h"; sourceTree = "<group>"; };
897FF1170E719B8F00D62E90 /* codegen.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = codegen.cc; sourceTree = "<group>"; };
897FF1180E719B8F00D62E90 /* codegen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = codegen.h; sourceTree = "<group>"; };
897FF1190E719B8F00D62E90 /* compiler.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = compiler.cc; sourceTree = "<group>"; };
@@ -815,7 +765,6 @@
897FF18E0E719B8F00D62E90 /* token.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = token.cc; sourceTree = "<group>"; };
897FF18F0E719B8F00D62E90 /* token.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = token.h; sourceTree = "<group>"; };
897FF1900E719B8F00D62E90 /* top.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = top.cc; sourceTree = "<group>"; };
- 897FF1910E719B8F00D62E90 /* top.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = top.h; sourceTree = "<group>"; };
897FF1920E719B8F00D62E90 /* unicode-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "unicode-inl.h"; sourceTree = "<group>"; };
897FF1930E719B8F00D62E90 /* unicode.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = unicode.cc; sourceTree = "<group>"; };
897FF1940E719B8F00D62E90 /* unicode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = unicode.h; sourceTree = "<group>"; };
@@ -875,7 +824,6 @@
89B91B7E12D4EF95002FF4BC /* builtins-x64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "builtins-x64.cc"; path = "x64/builtins-x64.cc"; sourceTree = "<group>"; };
89B91B7F12D4EF95002FF4BC /* code-stubs-x64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "code-stubs-x64.cc"; path = "x64/code-stubs-x64.cc"; sourceTree = "<group>"; };
89B91B8012D4EF95002FF4BC /* code-stubs-x64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "code-stubs-x64.h"; path = "x64/code-stubs-x64.h"; sourceTree = "<group>"; };
- 89B91B8112D4EF95002FF4BC /* codegen-x64-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "codegen-x64-inl.h"; path = "x64/codegen-x64-inl.h"; sourceTree = "<group>"; };
89B91B8212D4EF95002FF4BC /* codegen-x64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "codegen-x64.cc"; path = "x64/codegen-x64.cc"; sourceTree = "<group>"; };
89B91B8312D4EF95002FF4BC /* codegen-x64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "codegen-x64.h"; path = "x64/codegen-x64.h"; sourceTree = "<group>"; };
89B91B8412D4EF95002FF4BC /* cpu-x64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "cpu-x64.cc"; path = "x64/cpu-x64.cc"; sourceTree = "<group>"; };
@@ -886,21 +834,15 @@
89B91B8912D4EF95002FF4BC /* frames-x64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "frames-x64.h"; path = "x64/frames-x64.h"; sourceTree = "<group>"; };
89B91B8A12D4EF95002FF4BC /* full-codegen-x64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "full-codegen-x64.cc"; path = "x64/full-codegen-x64.cc"; sourceTree = "<group>"; };
89B91B8B12D4EF95002FF4BC /* ic-x64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "ic-x64.cc"; path = "x64/ic-x64.cc"; sourceTree = "<group>"; };
- 89B91B8C12D4EF95002FF4BC /* jump-target-x64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "jump-target-x64.cc"; path = "x64/jump-target-x64.cc"; sourceTree = "<group>"; };
89B91B8D12D4EF95002FF4BC /* lithium-codegen-x64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "lithium-codegen-x64.h"; path = "x64/lithium-codegen-x64.h"; sourceTree = "<group>"; };
89B91B8E12D4EF95002FF4BC /* lithium-x64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "lithium-x64.h"; path = "x64/lithium-x64.h"; sourceTree = "<group>"; };
89B91B8F12D4EF95002FF4BC /* macro-assembler-x64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "macro-assembler-x64.cc"; path = "x64/macro-assembler-x64.cc"; sourceTree = "<group>"; };
89B91B9012D4EF95002FF4BC /* macro-assembler-x64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "macro-assembler-x64.h"; path = "x64/macro-assembler-x64.h"; sourceTree = "<group>"; };
89B91B9112D4EF95002FF4BC /* regexp-macro-assembler-x64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "regexp-macro-assembler-x64.cc"; path = "x64/regexp-macro-assembler-x64.cc"; sourceTree = "<group>"; };
89B91B9212D4EF95002FF4BC /* regexp-macro-assembler-x64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "regexp-macro-assembler-x64.h"; path = "x64/regexp-macro-assembler-x64.h"; sourceTree = "<group>"; };
- 89B91B9312D4EF95002FF4BC /* register-allocator-x64-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "register-allocator-x64-inl.h"; path = "x64/register-allocator-x64-inl.h"; sourceTree = "<group>"; };
- 89B91B9412D4EF95002FF4BC /* register-allocator-x64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "register-allocator-x64.cc"; path = "x64/register-allocator-x64.cc"; sourceTree = "<group>"; };
- 89B91B9512D4EF95002FF4BC /* register-allocator-x64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "register-allocator-x64.h"; path = "x64/register-allocator-x64.h"; sourceTree = "<group>"; };
89B91B9612D4EF95002FF4BC /* simulator-x64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "simulator-x64.cc"; path = "x64/simulator-x64.cc"; sourceTree = "<group>"; };
89B91B9712D4EF95002FF4BC /* simulator-x64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "simulator-x64.h"; path = "x64/simulator-x64.h"; sourceTree = "<group>"; };
89B91B9812D4EF95002FF4BC /* stub-cache-x64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "stub-cache-x64.cc"; path = "x64/stub-cache-x64.cc"; sourceTree = "<group>"; };
- 89B91B9912D4EF95002FF4BC /* virtual-frame-x64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "virtual-frame-x64.cc"; path = "x64/virtual-frame-x64.cc"; sourceTree = "<group>"; };
- 89B91B9A12D4EF95002FF4BC /* virtual-frame-x64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "virtual-frame-x64.h"; path = "x64/virtual-frame-x64.h"; sourceTree = "<group>"; };
89B91BBE12D4F02A002FF4BC /* v8_shell-x64 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "v8_shell-x64"; sourceTree = BUILT_PRODUCTS_DIR; };
89B91BCE12D4F02A002FF4BC /* d8-x64 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "d8-x64"; sourceTree = BUILT_PRODUCTS_DIR; };
89D7DDD312E8DDCF001E2B82 /* lithium-gap-resolver-ia32.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "lithium-gap-resolver-ia32.cc"; path = "ia32/lithium-gap-resolver-ia32.cc"; sourceTree = "<group>"; };
@@ -914,6 +856,11 @@
89F3605A12DCDF6400ACF8A6 /* lithium-codegen-x64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "lithium-codegen-x64.cc"; path = "x64/lithium-codegen-x64.cc"; sourceTree = "<group>"; };
89FB0E360F8E531900B04B3C /* d8-posix.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "d8-posix.cc"; path = "../src/d8-posix.cc"; sourceTree = "<group>"; };
89FB0E370F8E531900B04B3C /* d8-windows.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "d8-windows.cc"; path = "../src/d8-windows.cc"; sourceTree = "<group>"; };
+ 89FE7C0513532165008662BD /* date.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = date.js; sourceTree = "<group>"; };
+ 89FE7C0613532165008662BD /* debug-debugger.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "debug-debugger.js"; sourceTree = "<group>"; };
+ 89FE7C0713532165008662BD /* liveedit-debugger.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "liveedit-debugger.js"; sourceTree = "<group>"; };
+ 89FE7C0813532165008662BD /* mirror-debugger.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "mirror-debugger.js"; sourceTree = "<group>"; };
+ 89FE7C0913532165008662BD /* regexp.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = regexp.js; sourceTree = "<group>"; };
9C1F8E1D133906180068B362 /* small-pointer-list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "small-pointer-list.h"; sourceTree = "<group>"; };
9C76176D133FB7740057370B /* platform-tls-mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "platform-tls-mac.h"; sourceTree = "<group>"; };
9C8E8061133CF772004058A5 /* platform-tls.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "platform-tls.h"; sourceTree = "<group>"; };
@@ -945,23 +892,14 @@
9FA38BA21175B2D200C4CD55 /* fast-dtoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "fast-dtoa.h"; sourceTree = "<group>"; };
9FA38BA51175B2D200C4CD55 /* full-codegen.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "full-codegen.cc"; sourceTree = "<group>"; };
9FA38BA61175B2D200C4CD55 /* full-codegen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "full-codegen.h"; sourceTree = "<group>"; };
- 9FA38BA71175B2D200C4CD55 /* jump-target-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "jump-target-inl.h"; sourceTree = "<group>"; };
- 9FA38BA81175B2D200C4CD55 /* jump-target-light-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "jump-target-light-inl.h"; sourceTree = "<group>"; };
9FA38BA91175B2D200C4CD55 /* liveedit.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = liveedit.cc; sourceTree = "<group>"; };
9FA38BAA1175B2D200C4CD55 /* liveedit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = liveedit.h; sourceTree = "<group>"; };
9FA38BAC1175B2D200C4CD55 /* splay-tree-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "splay-tree-inl.h"; sourceTree = "<group>"; };
9FA38BAD1175B2D200C4CD55 /* splay-tree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "splay-tree.h"; sourceTree = "<group>"; };
9FA38BAE1175B2D200C4CD55 /* type-info.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "type-info.cc"; sourceTree = "<group>"; };
9FA38BAF1175B2D200C4CD55 /* type-info.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "type-info.h"; sourceTree = "<group>"; };
- 9FA38BB01175B2D200C4CD55 /* virtual-frame-heavy-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "virtual-frame-heavy-inl.h"; sourceTree = "<group>"; };
- 9FA38BB11175B2D200C4CD55 /* virtual-frame-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "virtual-frame-inl.h"; sourceTree = "<group>"; };
- 9FA38BB21175B2D200C4CD55 /* virtual-frame-light-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "virtual-frame-light-inl.h"; sourceTree = "<group>"; };
9FA38BC21175B2E500C4CD55 /* full-codegen-ia32.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "full-codegen-ia32.cc"; path = "ia32/full-codegen-ia32.cc"; sourceTree = "<group>"; };
- 9FA38BC31175B2E500C4CD55 /* jump-target-ia32.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "jump-target-ia32.cc"; path = "ia32/jump-target-ia32.cc"; sourceTree = "<group>"; };
- 9FA38BC41175B2E500C4CD55 /* virtual-frame-ia32.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "virtual-frame-ia32.cc"; path = "ia32/virtual-frame-ia32.cc"; sourceTree = "<group>"; };
9FA38BCB1175B30400C4CD55 /* full-codegen-arm.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "full-codegen-arm.cc"; path = "arm/full-codegen-arm.cc"; sourceTree = "<group>"; };
- 9FA38BCC1175B30400C4CD55 /* jump-target-arm.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "jump-target-arm.cc"; path = "arm/jump-target-arm.cc"; sourceTree = "<group>"; };
- 9FA38BCD1175B30400C4CD55 /* virtual-frame-arm.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "virtual-frame-arm.cc"; path = "arm/virtual-frame-arm.cc"; sourceTree = "<group>"; };
9FF7A28211A642EA0051B8F2 /* unbound-queue-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "unbound-queue-inl.h"; sourceTree = "<group>"; };
9FF7A28311A642EA0051B8F2 /* unbound-queue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "unbound-queue.h"; sourceTree = "<group>"; };
C2BD4BD5120165460046BF9F /* dtoa.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dtoa.cc; sourceTree = "<group>"; };
@@ -1100,7 +1038,6 @@
897FF1110E719B8F00D62E90 /* code-stubs.cc */,
897FF1120E719B8F00D62E90 /* code-stubs.h */,
897FF1130E719B8F00D62E90 /* code.h */,
- 897FF1160E719B8F00D62E90 /* codegen-inl.h */,
897FF1170E719B8F00D62E90 /* codegen.cc */,
897FF1180E719B8F00D62E90 /* codegen.h */,
89495E460E79FC23001F68C3 /* compilation-cache.cc */,
@@ -1195,15 +1132,6 @@
891C92FE133422EB00FF4757 /* isolate.h */,
897FF14E0E719B8F00D62E90 /* jsregexp.cc */,
897FF14F0E719B8F00D62E90 /* jsregexp.h */,
- 895FA720107FFB15006F39D4 /* jump-target-heavy-inl.h */,
- 58950D4F0F55514900F3E8BA /* jump-target-heavy.cc */,
- 893E249112B14B3D0083370F /* jump-target-heavy.h */,
- 9FA38BA71175B2D200C4CD55 /* jump-target-inl.h */,
- 9FA38BA81175B2D200C4CD55 /* jump-target-light-inl.h */,
- 58950D4E0F55514900F3E8BA /* jump-target-light.cc */,
- 893E249212B14B3D0083370F /* jump-target-light.h */,
- 58950D500F55514900F3E8BA /* jump-target.cc */,
- 58950D510F55514900F3E8BA /* jump-target.h */,
897FF1500E719B8F00D62E90 /* list-inl.h */,
897FF1510E719B8F00D62E90 /* list.h */,
891C92FD1334226000FF4757 /* lithium-allocator-inl.h */,
@@ -1264,9 +1192,6 @@
89A15C7A0EE466D000B48DEB /* regexp-macro-assembler.h */,
8944AD0E0F1D4D3A0028D560 /* regexp-stack.cc */,
8944AD0F0F1D4D3A0028D560 /* regexp-stack.h */,
- 893A722D0F7B4A7100303DD2 /* register-allocator-inl.h */,
- 58950D540F55514900F3E8BA /* register-allocator.cc */,
- 58950D550F55514900F3E8BA /* register-allocator.h */,
897FF16F0E719B8F00D62E90 /* rewriter.cc */,
897FF1700E719B8F00D62E90 /* rewriter.h */,
893E249A12B14B3D0083370F /* runtime-profiler.cc */,
@@ -1309,7 +1234,6 @@
897FF18E0E719B8F00D62E90 /* token.cc */,
897FF18F0E719B8F00D62E90 /* token.h */,
897FF1900E719B8F00D62E90 /* top.cc */,
- 897FF1910E719B8F00D62E90 /* top.h */,
9FA38BAE1175B2D200C4CD55 /* type-info.cc */,
9FA38BAF1175B2D200C4CD55 /* type-info.h */,
9FF7A28211A642EA0051B8F2 /* unbound-queue-inl.h */,
@@ -1332,13 +1256,6 @@
897FF1A00E719B8F00D62E90 /* variables.h */,
897FF32F0FAA0ED200136CF6 /* version.cc */,
897FF3300FAA0ED200136CF6 /* version.h */,
- 9FA38BB01175B2D200C4CD55 /* virtual-frame-heavy-inl.h */,
- 58950D580F55514900F3E8BA /* virtual-frame-heavy.cc */,
- 9FA38BB11175B2D200C4CD55 /* virtual-frame-inl.h */,
- 9FA38BB21175B2D200C4CD55 /* virtual-frame-light-inl.h */,
- 58950D560F55514900F3E8BA /* virtual-frame-light.cc */,
- 58950D5A0F55514900F3E8BA /* virtual-frame.cc */,
- 58950D5B0F55514900F3E8BA /* virtual-frame.h */,
9FA37332116DD9F000C4CD55 /* vm-state-inl.h */,
9FA37334116DD9F000C4CD55 /* vm-state.h */,
897FF1A10E719B8F00D62E90 /* zone-inl.h */,
@@ -1386,6 +1303,11 @@
897FF0D80E719ABA00D62E90 /* js */ = {
isa = PBXGroup;
children = (
+ 89FE7C0513532165008662BD /* date.js */,
+ 89FE7C0613532165008662BD /* debug-debugger.js */,
+ 89FE7C0713532165008662BD /* liveedit-debugger.js */,
+ 89FE7C0813532165008662BD /* mirror-debugger.js */,
+ 89FE7C0913532165008662BD /* regexp.js */,
897FF1A60E719BC100D62E90 /* apinatives.js */,
897FF1A70E719BC100D62E90 /* array.js */,
897FF1AA0E719BC100D62E90 /* math.js */,
@@ -1458,7 +1380,6 @@
89B91B7E12D4EF95002FF4BC /* builtins-x64.cc */,
89B91B7F12D4EF95002FF4BC /* code-stubs-x64.cc */,
89B91B8012D4EF95002FF4BC /* code-stubs-x64.h */,
- 89B91B8112D4EF95002FF4BC /* codegen-x64-inl.h */,
89B91B8212D4EF95002FF4BC /* codegen-x64.cc */,
89B91B8312D4EF95002FF4BC /* codegen-x64.h */,
89B91B8412D4EF95002FF4BC /* cpu-x64.cc */,
@@ -1469,7 +1390,6 @@
89B91B8912D4EF95002FF4BC /* frames-x64.h */,
89B91B8A12D4EF95002FF4BC /* full-codegen-x64.cc */,
89B91B8B12D4EF95002FF4BC /* ic-x64.cc */,
- 89B91B8C12D4EF95002FF4BC /* jump-target-x64.cc */,
89F3605A12DCDF6400ACF8A6 /* lithium-codegen-x64.cc */,
89B91B8D12D4EF95002FF4BC /* lithium-codegen-x64.h */,
8924315A12F8539900906AB2 /* lithium-gap-resolver-x64.cc */,
@@ -1480,14 +1400,9 @@
89B91B9012D4EF95002FF4BC /* macro-assembler-x64.h */,
89B91B9112D4EF95002FF4BC /* regexp-macro-assembler-x64.cc */,
89B91B9212D4EF95002FF4BC /* regexp-macro-assembler-x64.h */,
- 89B91B9312D4EF95002FF4BC /* register-allocator-x64-inl.h */,
- 89B91B9412D4EF95002FF4BC /* register-allocator-x64.cc */,
- 89B91B9512D4EF95002FF4BC /* register-allocator-x64.h */,
89B91B9612D4EF95002FF4BC /* simulator-x64.cc */,
89B91B9712D4EF95002FF4BC /* simulator-x64.h */,
89B91B9812D4EF95002FF4BC /* stub-cache-x64.cc */,
- 89B91B9912D4EF95002FF4BC /* virtual-frame-x64.cc */,
- 89B91B9A12D4EF95002FF4BC /* virtual-frame-x64.h */,
);
name = x64;
sourceTree = "<group>";
@@ -1503,7 +1418,6 @@
897FF10A0E719B8F00D62E90 /* builtins-ia32.cc */,
C68081B012251239001EAFE4 /* code-stubs-ia32.cc */,
C68081B412251257001EAFE4 /* code-stubs-ia32.h */,
- 895FA725107FFB57006F39D4 /* codegen-ia32-inl.h */,
897FF1150E719B8F00D62E90 /* codegen-ia32.cc */,
8964482B0E9C00F700E7C516 /* codegen-ia32.h */,
897FF1240E719B8F00D62E90 /* cpu-ia32.cc */,
@@ -1514,7 +1428,6 @@
897FF13A0E719B8F00D62E90 /* frames-ia32.h */,
9FA38BC21175B2E500C4CD55 /* full-codegen-ia32.cc */,
897FF14A0E719B8F00D62E90 /* ic-ia32.cc */,
- 9FA38BC31175B2E500C4CD55 /* jump-target-ia32.cc */,
893E24D112B14B8A0083370F /* lithium-codegen-ia32.cc */,
893E24D212B14B8A0083370F /* lithium-codegen-ia32.h */,
893E24D312B14B8A0083370F /* lithium-ia32.cc */,
@@ -1523,14 +1436,9 @@
897FF1570E719B8F00D62E90 /* macro-assembler-ia32.h */,
89A15C720EE466D000B48DEB /* regexp-macro-assembler-ia32.cc */,
89A15C730EE466D000B48DEB /* regexp-macro-assembler-ia32.h */,
- 895FA72A107FFB85006F39D4 /* register-allocator-ia32-inl.h */,
- 58950D530F55514900F3E8BA /* register-allocator-ia32.cc */,
- 895FA72B107FFB85006F39D4 /* register-allocator-ia32.h */,
897FF17F0E719B8F00D62E90 /* simulator-ia32.cc */,
897FF1800E719B8F00D62E90 /* simulator-ia32.h */,
897FF18B0E719B8F00D62E90 /* stub-cache-ia32.cc */,
- 9FA38BC41175B2E500C4CD55 /* virtual-frame-ia32.cc */,
- 58950D590F55514900F3E8BA /* virtual-frame-ia32.h */,
);
name = ia32;
sourceTree = "<group>";
@@ -1544,7 +1452,6 @@
897FF1090E719B8F00D62E90 /* builtins-arm.cc */,
C68081AB1225120B001EAFE4 /* code-stubs-arm.cc */,
C68081AC1225120B001EAFE4 /* code-stubs-arm.h */,
- 895FA74B107FFE82006F39D4 /* codegen-arm-inl.h */,
897FF1140E719B8F00D62E90 /* codegen-arm.cc */,
896448BC0E9D530500E7C516 /* codegen-arm.h */,
895FA748107FFE73006F39D4 /* constants-arm.cc */,
@@ -1553,7 +1460,6 @@
893E24C612B14B510083370F /* deoptimizer-arm.cc */,
9FA38BCB1175B30400C4CD55 /* full-codegen-arm.cc */,
897FF1490E719B8F00D62E90 /* ic-arm.cc */,
- 9FA38BCC1175B30400C4CD55 /* jump-target-arm.cc */,
893E24C712B14B510083370F /* lithium-arm.cc */,
893E24C812B14B510083370F /* lithium-arm.h */,
893E24C912B14B520083370F /* lithium-codegen-arm.cc */,
@@ -1564,15 +1470,9 @@
897FF1550E719B8F00D62E90 /* macro-assembler-arm.h */,
89A15C700EE466D000B48DEB /* regexp-macro-assembler-arm.cc */,
89A15C710EE466D000B48DEB /* regexp-macro-assembler-arm.h */,
- 895FA750107FFEAE006F39D4 /* register-allocator-arm-inl.h */,
- 58950D520F55514900F3E8BA /* register-allocator-arm.cc */,
- 895FA751107FFEAE006F39D4 /* register-allocator-arm.h */,
897FF17D0E719B8F00D62E90 /* simulator-arm.cc */,
897FF17E0E719B8F00D62E90 /* simulator-arm.h */,
897FF18A0E719B8F00D62E90 /* stub-cache-arm.cc */,
- 893E24CB12B14B520083370F /* virtual-frame-arm-inl.h */,
- 9FA38BCD1175B30400C4CD55 /* virtual-frame-arm.cc */,
- 58950D570F55514900F3E8BA /* virtual-frame-arm.h */,
);
name = arm;
sourceTree = "<group>";
@@ -1910,9 +1810,6 @@
8956925A12D4ED240072C313 /* ic.cc in Sources */,
8956925B12D4ED240072C313 /* interpreter-irregexp.cc in Sources */,
8956925C12D4ED240072C313 /* jsregexp.cc in Sources */,
- 8956925D12D4ED240072C313 /* jump-target-heavy.cc in Sources */,
- 8956925D12D4ED240072C313 /* jump-target-heavy.cc in Sources */,
- 8956925F12D4ED240072C313 /* jump-target.cc in Sources */,
8956926012D4ED240072C313 /* libraries.cc in Sources */,
8956926112D4ED240072C313 /* liveedit.cc in Sources */,
8956926212D4ED240072C313 /* log-utils.cc in Sources */,
@@ -1931,7 +1828,6 @@
8956927212D4ED240072C313 /* regexp-macro-assembler-tracer.cc in Sources */,
8956927312D4ED240072C313 /* regexp-macro-assembler.cc in Sources */,
8956927412D4ED240072C313 /* regexp-stack.cc in Sources */,
- 8956927612D4ED240072C313 /* register-allocator.cc in Sources */,
8956927712D4ED240072C313 /* rewriter.cc in Sources */,
8956927812D4ED240072C313 /* runtime.cc in Sources */,
8956927912D4ED240072C313 /* scanner.cc in Sources */,
@@ -1954,9 +1850,6 @@
8956928A12D4ED240072C313 /* v8threads.cc in Sources */,
8956928B12D4ED240072C313 /* variables.cc in Sources */,
8956928C12D4ED240072C313 /* version.cc in Sources */,
- 8956928D12D4ED240072C313 /* virtual-frame-heavy.cc in Sources */,
- 8956928F12D4ED240072C313 /* virtual-frame.cc in Sources */,
- 8956928F12D4ED240072C313 /* virtual-frame.cc in Sources */,
8956929012D4ED240072C313 /* zone.cc in Sources */,
8956929212D4ED240072C313 /* bignum-dtoa.cc in Sources */,
8956929312D4ED240072C313 /* bignum.cc in Sources */,
@@ -1986,13 +1879,10 @@
89B91BA312D4EF95002FF4BC /* frames-x64.cc in Sources */,
89B91BA412D4EF95002FF4BC /* full-codegen-x64.cc in Sources */,
89B91BA512D4EF95002FF4BC /* ic-x64.cc in Sources */,
- 89B91BA612D4EF95002FF4BC /* jump-target-x64.cc in Sources */,
89B91BA712D4EF95002FF4BC /* macro-assembler-x64.cc in Sources */,
89B91BA812D4EF95002FF4BC /* regexp-macro-assembler-x64.cc in Sources */,
- 89B91BA912D4EF95002FF4BC /* register-allocator-x64.cc in Sources */,
89B91BAA12D4EF95002FF4BC /* simulator-x64.cc in Sources */,
89B91BAB12D4EF95002FF4BC /* stub-cache-x64.cc in Sources */,
- 89B91BAC12D4EF95002FF4BC /* virtual-frame-x64.cc in Sources */,
8938A2A312D63B630080CDDE /* lithium-x64.cc in Sources */,
894A59E912D777E80000766D /* lithium.cc in Sources */,
89F3605B12DCDF6400ACF8A6 /* lithium-codegen-x64.cc in Sources */,
@@ -2058,10 +1948,6 @@
89A88E0D0E71A66E0043BA31 /* ic.cc in Sources */,
89A15C850EE4678B00B48DEB /* interpreter-irregexp.cc in Sources */,
89A88E0E0E71A66F0043BA31 /* jsregexp.cc in Sources */,
- 58950D5F0F55519D00F3E8BA /* jump-target-heavy.cc in Sources */,
- 58950D5F0F55519D00F3E8BA /* jump-target-heavy.cc in Sources */,
- 9FA38BC61175B2E500C4CD55 /* jump-target-ia32.cc in Sources */,
- 58950D5E0F55519800F3E8BA /* jump-target.cc in Sources */,
8900116C0E71CA2300F91F35 /* libraries.cc in Sources */,
9FA38BBF1175B2D200C4CD55 /* liveedit.cc in Sources */,
9F4B7B890FCC877A00DC4117 /* log-utils.cc in Sources */,
@@ -2082,8 +1968,6 @@
89A15C8A0EE467D100B48DEB /* regexp-macro-assembler-tracer.cc in Sources */,
89A15C810EE4674900B48DEB /* regexp-macro-assembler.cc in Sources */,
8944AD100F1D4D500028D560 /* regexp-stack.cc in Sources */,
- 58950D620F5551AF00F3E8BA /* register-allocator-ia32.cc in Sources */,
- 58950D630F5551AF00F3E8BA /* register-allocator.cc in Sources */,
89A88E190E71A6970043BA31 /* rewriter.cc in Sources */,
89A88E1A0E71A69B0043BA31 /* runtime.cc in Sources */,
89A88E1B0E71A69D0043BA31 /* scanner.cc in Sources */,
@@ -2107,10 +1991,6 @@
89A88E2C0E71A6D20043BA31 /* v8threads.cc in Sources */,
89A88E2D0E71A6D50043BA31 /* variables.cc in Sources */,
89B933AF0FAA0F9600201304 /* version.cc in Sources */,
- 58950D670F5551C400F3E8BA /* virtual-frame-heavy.cc in Sources */,
- 9FA38BC71175B2E500C4CD55 /* virtual-frame-ia32.cc in Sources */,
- 58950D660F5551C200F3E8BA /* virtual-frame.cc in Sources */,
- 58950D660F5551C200F3E8BA /* virtual-frame.cc in Sources */,
89A88E2E0E71A6D60043BA31 /* zone.cc in Sources */,
C68081B112251239001EAFE4 /* code-stubs-ia32.cc in Sources */,
893E24B712B14B3D0083370F /* bignum-dtoa.cc in Sources */,
@@ -2235,10 +2115,6 @@
89F23C600E78D5B2006B2466 /* ic.cc in Sources */,
890A13FE0EE9C47F00E49346 /* interpreter-irregexp.cc in Sources */,
89F23C610E78D5B2006B2466 /* jsregexp.cc in Sources */,
- 9FA38BD01175B30400C4CD55 /* jump-target-arm.cc in Sources */,
- 58950D610F5551A400F3E8BA /* jump-target-light.cc in Sources */,
- 58950D610F5551A400F3E8BA /* jump-target-light.cc in Sources */,
- 58950D600F5551A300F3E8BA /* jump-target.cc in Sources */,
89F23C620E78D5B2006B2466 /* libraries.cc in Sources */,
9FA38BB81175B2D200C4CD55 /* liveedit.cc in Sources */,
9F4B7B8A0FCC877A00DC4117 /* log-utils.cc in Sources */,
@@ -2259,8 +2135,6 @@
890A14030EE9C4B500E49346 /* regexp-macro-assembler-tracer.cc in Sources */,
890A14040EE9C4B700E49346 /* regexp-macro-assembler.cc in Sources */,
8944AD110F1D4D570028D560 /* regexp-stack.cc in Sources */,
- 58950D650F5551B600F3E8BA /* register-allocator-arm.cc in Sources */,
- 58950D640F5551B500F3E8BA /* register-allocator.cc in Sources */,
89F23C6D0E78D5B2006B2466 /* rewriter.cc in Sources */,
89F23C6E0E78D5B2006B2466 /* runtime.cc in Sources */,
89F23C6F0E78D5B2006B2466 /* scanner.cc in Sources */,
@@ -2285,10 +2159,6 @@
89F23C800E78D5B2006B2466 /* v8threads.cc in Sources */,
89F23C810E78D5B2006B2466 /* variables.cc in Sources */,
89B933B00FAA0F9D00201304 /* version.cc in Sources */,
- 9FA38BD11175B30400C4CD55 /* virtual-frame-arm.cc in Sources */,
- 58950D690F5551CE00F3E8BA /* virtual-frame-light.cc in Sources */,
- 58950D680F5551CB00F3E8BA /* virtual-frame.cc in Sources */,
- 58950D680F5551CB00F3E8BA /* virtual-frame.cc in Sources */,
89F23C820E78D5B2006B2466 /* zone.cc in Sources */,
C68081AD1225120B001EAFE4 /* code-stubs-arm.cc in Sources */,
893E24A812B14B3D0083370F /* bignum-dtoa.cc in Sources */,