summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--deps/v8/.gitignore4
-rw-r--r--deps/v8/ChangeLog21
-rwxr-xr-x[-rw-r--r--]deps/v8/SConstruct5
-rw-r--r--deps/v8/include/v8.h115
-rwxr-xr-xdeps/v8/src/SConscript168
-rw-r--r--deps/v8/src/api.cc60
-rw-r--r--deps/v8/src/arm/assembler-arm-inl.h8
-rw-r--r--deps/v8/src/arm/codegen-arm.cc319
-rw-r--r--deps/v8/src/arm/codegen-arm.h19
-rw-r--r--deps/v8/src/arm/debug-arm.cc2
-rw-r--r--deps/v8/src/arm/fast-codegen-arm.cc176
-rw-r--r--deps/v8/src/arm/macro-assembler-arm.h1
-rw-r--r--deps/v8/src/array.js14
-rw-r--r--deps/v8/src/assembler.cc3
-rw-r--r--deps/v8/src/assembler.h8
-rw-r--r--deps/v8/src/ast.cc21
-rw-r--r--deps/v8/src/ast.h97
-rw-r--r--deps/v8/src/checks.h21
-rw-r--r--deps/v8/src/code-stubs.cc2
-rw-r--r--deps/v8/src/code-stubs.h1
-rw-r--r--deps/v8/src/codegen.cc126
-rw-r--r--deps/v8/src/codegen.h4
-rw-r--r--deps/v8/src/compilation-cache.cc10
-rw-r--r--deps/v8/src/compiler.cc303
-rw-r--r--deps/v8/src/d8-posix.cc2
-rw-r--r--deps/v8/src/d8.js2
-rw-r--r--deps/v8/src/dateparser-inl.h2
-rw-r--r--deps/v8/src/debug-delay.js31
-rw-r--r--deps/v8/src/debug.cc39
-rw-r--r--deps/v8/src/execution.cc10
-rw-r--r--deps/v8/src/execution.h1
-rw-r--r--deps/v8/src/factory.cc3
-rw-r--r--deps/v8/src/factory.h1
-rw-r--r--deps/v8/src/fast-codegen.cc269
-rw-r--r--deps/v8/src/fast-codegen.h71
-rw-r--r--deps/v8/src/flag-definitions.h22
-rw-r--r--deps/v8/src/global-handles.cc10
-rw-r--r--deps/v8/src/global-handles.h6
-rw-r--r--deps/v8/src/heap-profiler.cc56
-rw-r--r--deps/v8/src/heap-profiler.h8
-rw-r--r--deps/v8/src/heap.cc9
-rw-r--r--deps/v8/src/ia32/assembler-ia32-inl.h14
-rw-r--r--deps/v8/src/ia32/assembler-ia32.cc13
-rw-r--r--deps/v8/src/ia32/assembler-ia32.h5
-rw-r--r--deps/v8/src/ia32/codegen-ia32.cc800
-rw-r--r--deps/v8/src/ia32/codegen-ia32.h81
-rw-r--r--deps/v8/src/ia32/debug-ia32.cc2
-rw-r--r--deps/v8/src/ia32/disasm-ia32.cc34
-rw-r--r--deps/v8/src/ia32/fast-codegen-ia32.cc163
-rw-r--r--deps/v8/src/ia32/ic-ia32.cc11
-rw-r--r--deps/v8/src/ia32/virtual-frame-ia32.cc35
-rw-r--r--deps/v8/src/jsregexp.cc3
-rw-r--r--deps/v8/src/jsregexp.h2
-rw-r--r--deps/v8/src/jump-target.h2
-rw-r--r--deps/v8/src/location.h56
-rw-r--r--deps/v8/src/log.cc15
-rw-r--r--deps/v8/src/log.h2
-rw-r--r--deps/v8/src/macros.py4
-rw-r--r--deps/v8/src/mark-compact.cc5
-rw-r--r--deps/v8/src/mirror-delay.js2
-rw-r--r--deps/v8/src/objects-debug.cc1
-rw-r--r--deps/v8/src/objects-inl.h51
-rw-r--r--deps/v8/src/objects.cc17
-rw-r--r--deps/v8/src/objects.h51
-rw-r--r--deps/v8/src/parser.cc55
-rw-r--r--deps/v8/src/parser.h10
-rw-r--r--deps/v8/src/platform-win32.cc1
-rw-r--r--deps/v8/src/prettyprinter.cc506
-rw-r--r--deps/v8/src/prettyprinter.h118
-rw-r--r--deps/v8/src/rewriter.cc56
-rw-r--r--deps/v8/src/runtime.cc49
-rw-r--r--deps/v8/src/runtime.h1
-rw-r--r--deps/v8/src/serialize.cc20
-rw-r--r--deps/v8/src/string.js15
-rw-r--r--deps/v8/src/top.cc11
-rw-r--r--deps/v8/src/usage-analyzer.cc29
-rw-r--r--deps/v8/src/utils.h3
-rw-r--r--deps/v8/src/v8-counters.h8
-rw-r--r--deps/v8/src/v8.cc7
-rw-r--r--deps/v8/src/version.cc2
-rw-r--r--deps/v8/src/x64/assembler-x64-inl.h23
-rw-r--r--deps/v8/src/x64/assembler-x64.cc92
-rw-r--r--deps/v8/src/x64/assembler-x64.h46
-rw-r--r--deps/v8/src/x64/builtins-x64.cc46
-rw-r--r--deps/v8/src/x64/codegen-x64.cc779
-rw-r--r--deps/v8/src/x64/codegen-x64.h19
-rw-r--r--deps/v8/src/x64/debug-x64.cc5
-rw-r--r--deps/v8/src/x64/fast-codegen-x64.cc181
-rw-r--r--deps/v8/src/x64/frames-x64.h3
-rw-r--r--deps/v8/src/x64/ic-x64.cc51
-rw-r--r--deps/v8/src/x64/macro-assembler-x64.cc1060
-rw-r--r--deps/v8/src/x64/macro-assembler-x64.h87
-rw-r--r--deps/v8/src/x64/stub-cache-x64.cc13
-rw-r--r--deps/v8/src/x64/virtual-frame-x64.cc47
-rw-r--r--deps/v8/src/x64/virtual-frame-x64.h1
-rw-r--r--deps/v8/test/cctest/SConscript4
-rw-r--r--deps/v8/test/cctest/test-api.cc162
-rw-r--r--deps/v8/test/cctest/test-assembler-x64.cc54
-rw-r--r--deps/v8/test/cctest/test-debug.cc138
-rw-r--r--deps/v8/test/cctest/test-disasm-ia32.cc24
-rw-r--r--deps/v8/test/cctest/test-heap.cc13
-rwxr-xr-xdeps/v8/test/cctest/test-macro-assembler-x64.cc2096
-rw-r--r--deps/v8/test/mjsunit/compiler/literals-assignment.js71
-rw-r--r--deps/v8/test/mjsunit/compiler/literals.js35
-rw-r--r--deps/v8/test/mjsunit/debug-backtrace.js22
-rw-r--r--deps/v8/test/mjsunit/debug-changebreakpoint.js2
-rw-r--r--deps/v8/test/mjsunit/debug-clearbreakpoint.js2
-rw-r--r--deps/v8/test/mjsunit/debug-clearbreakpointgroup.js2
-rw-r--r--deps/v8/test/mjsunit/debug-continue.js31
-rw-r--r--deps/v8/test/mjsunit/debug-evaluate-bool-constructor.js80
-rw-r--r--deps/v8/test/mjsunit/debug-evaluate-recursive.js28
-rw-r--r--deps/v8/test/mjsunit/debug-evaluate.js7
-rw-r--r--deps/v8/test/mjsunit/debug-handle.js49
-rw-r--r--deps/v8/test/mjsunit/debug-mirror-cache.js10
-rw-r--r--deps/v8/test/mjsunit/debug-references.js4
-rw-r--r--deps/v8/test/mjsunit/debug-scopes.js4
-rw-r--r--deps/v8/test/mjsunit/debug-scripts-request.js2
-rw-r--r--deps/v8/test/mjsunit/debug-setbreakpoint.js2
-rw-r--r--deps/v8/test/mjsunit/debug-suspend.js96
-rw-r--r--deps/v8/test/mjsunit/for-in.js44
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1081309.js4
-rw-r--r--deps/v8/test/mjsunit/regress/regress-1199401.js78
-rw-r--r--deps/v8/test/mjsunit/smi-negative-zero.js84
-rw-r--r--deps/v8/test/mjsunit/testcfg.py3
-rw-r--r--deps/v8/test/mjsunit/third_party/array-isarray.js48
-rw-r--r--deps/v8/test/mjsunit/third_party/string-trim.js107
-rw-r--r--deps/v8/tools/gyp/v8.gyp14
-rw-r--r--deps/v8/tools/tickprocessor.js49
-rw-r--r--deps/v8/tools/v8.xcodeproj/project.pbxproj37
-rw-r--r--deps/v8/tools/visual_studio/v8_base.vcproj18
-rw-r--r--deps/v8/tools/visual_studio/v8_base_arm.vcproj18
-rw-r--r--deps/v8/tools/visual_studio/v8_base_x64.vcproj2
132 files changed, 7981 insertions, 2226 deletions
diff --git a/deps/v8/.gitignore b/deps/v8/.gitignore
index 685e9a2f9..e5687e730 100644
--- a/deps/v8/.gitignore
+++ b/deps/v8/.gitignore
@@ -10,6 +10,10 @@
*.suo
*.user
*.xcodeproj
+*.idb
+*.pdb
+#*#
+*~
d8
d8_g
shell
diff --git a/deps/v8/ChangeLog b/deps/v8/ChangeLog
index d07c4586d..d13d74f54 100644
--- a/deps/v8/ChangeLog
+++ b/deps/v8/ChangeLog
@@ -1,3 +1,24 @@
+2009-10-16: Version 1.3.16
+
+ X64: Convert smis to holding 32 bits of payload.
+
+ Introduce v8::Integer::NewFromUnsigned method.
+
+ Add missing null check in Context::GetCurrent.
+
+ Add trim, trimLeft and trimRight methods to String
+ Patch by Jan de Mooij <jandemooij@gmail.com>
+
+ Implement ES5 Array.isArray
+ Patch by Jan de Mooij <jandemooij@gmail.com>
+
+ Skip access checks for hidden properties.
+
+ Add String::Concat(Handle<String> left, Handle<String> right) to the V8 API.
+
+ Fix GYP-based builds of V8.
+
+
2009-10-07: Version 1.3.15
Expand the maximum size of the code space to 512MB for 64-bit mode.
diff --git a/deps/v8/SConstruct b/deps/v8/SConstruct
index e1a37f34f..af8119bfd 100644..100755
--- a/deps/v8/SConstruct
+++ b/deps/v8/SConstruct
@@ -372,7 +372,8 @@ CCTEST_EXTRA_FLAGS = {
'CPPDEFINES': ['V8_TARGET_ARCH_IA32']
},
'arch:x64': {
- 'CPPDEFINES': ['V8_TARGET_ARCH_X64']
+ 'CPPDEFINES': ['V8_TARGET_ARCH_X64'],
+ 'LINKFLAGS': ['/STACK:2091752']
},
}
}
@@ -473,7 +474,7 @@ SAMPLE_FLAGS = {
},
'arch:x64': {
'CPPDEFINES': ['V8_TARGET_ARCH_X64'],
- 'LINKFLAGS': ['/MACHINE:X64']
+ 'LINKFLAGS': ['/MACHINE:X64', '/STACK:2091752']
},
'mode:debug': {
'CCFLAGS': ['/Od'],
diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h
index adb9f4317..d923f9785 100644
--- a/deps/v8/include/v8.h
+++ b/deps/v8/include/v8.h
@@ -756,7 +756,7 @@ class V8EXPORT Value : public Data {
/** JS == */
bool Equals(Handle<Value> that) const;
bool StrictEquals(Handle<Value> that) const;
-
+
private:
inline bool QuickIsString() const;
bool FullIsString() const;
@@ -919,6 +919,12 @@ class V8EXPORT String : public Primitive {
static Local<String> NewSymbol(const char* data, int length = -1);
/**
+ * Creates a new string by concatenating the left and the right strings
+ * passed in as parameters.
+ */
+ static Local<String> Concat(Handle<String> left, Handle<String>right);
+
+ /**
* Creates a new external string using the data defined in the given
* resource. The resource is deleted when the external string is no
* longer live on V8's heap. The caller of this function should not
@@ -1036,7 +1042,7 @@ class V8EXPORT String : public Primitive {
Value(const Value&);
void operator=(const Value&);
};
-
+
private:
void VerifyExternalStringResource(ExternalStringResource* val) const;
static void CheckCast(v8::Value* obj);
@@ -1063,6 +1069,7 @@ class V8EXPORT Number : public Primitive {
class V8EXPORT Integer : public Number {
public:
static Local<Integer> New(int32_t value);
+ static inline Local<Integer> NewFromUnsigned(uint32_t value);
int64_t Value() const;
static inline Integer* Cast(v8::Value* obj);
private:
@@ -1193,7 +1200,7 @@ class V8EXPORT Object : public Value {
/** Gets a native pointer from an internal field. */
inline void* GetPointerFromInternalField(int index);
-
+
/** Sets a native pointer in an internal field. */
void SetPointerInInternalField(int index, void* value);
@@ -1246,7 +1253,7 @@ class V8EXPORT Object : public Value {
bool SetHiddenValue(Handle<String> key, Handle<Value> value);
Local<Value> GetHiddenValue(Handle<String> key);
bool DeleteHiddenValue(Handle<String> key);
-
+
/**
* Returns true if this is an instance of an api function (one
* created from a function created from a function template) and has
@@ -1277,10 +1284,11 @@ class V8EXPORT Object : public Value {
Object();
static void CheckCast(Value* obj);
Local<Value> CheckedGetInternalField(int index);
+ void* SlowGetPointerFromInternalField(int index);
/**
* If quick access to the internal field is possible this method
- * returns the value. Otherwise an empty handle is returned.
+ * returns the value. Otherwise an empty handle is returned.
*/
inline Local<Value> UncheckedGetInternalField(int index);
};
@@ -2719,12 +2727,37 @@ const int kHeapObjectTag = 1;
const int kHeapObjectTagSize = 2;
const intptr_t kHeapObjectTagMask = (1 << kHeapObjectTagSize) - 1;
-
// Tag information for Smi.
const int kSmiTag = 0;
const int kSmiTagSize = 1;
const intptr_t kSmiTagMask = (1 << kSmiTagSize) - 1;
+template <size_t ptr_size> struct SmiConstants;
+
+// Smi constants for 32-bit systems.
+template <> struct SmiConstants<4> {
+ static const int kSmiShiftSize = 0;
+ static const int kSmiValueSize = 31;
+ static inline int SmiToInt(internal::Object* value) {
+ int shift_bits = kSmiTagSize + kSmiShiftSize;
+ // Throw away top 32 bits and shift down (requires >> to be sign extending).
+ return static_cast<int>(reinterpret_cast<intptr_t>(value)) >> shift_bits;
+ }
+};
+
+// Smi constants for 64-bit systems.
+template <> struct SmiConstants<8> {
+ static const int kSmiShiftSize = 31;
+ static const int kSmiValueSize = 32;
+ static inline int SmiToInt(internal::Object* value) {
+ int shift_bits = kSmiTagSize + kSmiShiftSize;
+ // Shift down and throw away top 32 bits.
+ return static_cast<int>(reinterpret_cast<intptr_t>(value) >> shift_bits);
+ }
+};
+
+const int kSmiShiftSize = SmiConstants<sizeof(void*)>::kSmiShiftSize;
+const int kSmiValueSize = SmiConstants<sizeof(void*)>::kSmiValueSize;
/**
* This class exports constants and functionality from within v8 that
@@ -2743,7 +2776,6 @@ class Internals {
static const int kJSObjectHeaderSize = 3 * sizeof(void*);
static const int kFullStringRepresentationMask = 0x07;
static const int kExternalTwoByteRepresentationTag = 0x03;
- static const int kAlignedPointerShift = 2;
// These constants are compiler dependent so their values must be
// defined within the implementation.
@@ -2761,7 +2793,23 @@ class Internals {
}
static inline int SmiValue(internal::Object* value) {
- return static_cast<int>(reinterpret_cast<intptr_t>(value)) >> kSmiTagSize;
+ return SmiConstants<sizeof(void*)>::SmiToInt(value);
+ }
+
+ static inline int GetInstanceType(internal::Object* obj) {
+ typedef internal::Object O;
+ O* map = ReadField<O*>(obj, kHeapObjectMapOffset);
+ return ReadField<uint8_t>(map, kMapInstanceTypeOffset);
+ }
+
+ static inline void* GetExternalPointer(internal::Object* obj) {
+ if (HasSmiTag(obj)) {
+ return obj;
+ } else if (GetInstanceType(obj) == kProxyType) {
+ return ReadField<void*>(obj, kProxyProxyOffset);
+ } else {
+ return NULL;
+ }
}
static inline bool IsExternalTwoByteString(int instance_type) {
@@ -2921,9 +2969,7 @@ Local<Value> Object::UncheckedGetInternalField(int index) {
typedef internal::Object O;
typedef internal::Internals I;
O* obj = *reinterpret_cast<O**>(this);
- O* map = I::ReadField<O*>(obj, I::kHeapObjectMapOffset);
- int instance_type = I::ReadField<uint8_t>(map, I::kMapInstanceTypeOffset);
- if (instance_type == I::kJSObjectType) {
+ if (I::GetInstanceType(obj) == I::kJSObjectType) {
// If the object is a plain JSObject, which is the common case,
// we know where to find the internal fields and can return the
// value directly.
@@ -2948,25 +2994,27 @@ void* External::Unwrap(Handle<v8::Value> obj) {
void* External::QuickUnwrap(Handle<v8::Value> wrapper) {
typedef internal::Object O;
- typedef internal::Internals I;
O* obj = *reinterpret_cast<O**>(const_cast<v8::Value*>(*wrapper));
- if (I::HasSmiTag(obj)) {
- int value = I::SmiValue(obj) << I::kAlignedPointerShift;
- return reinterpret_cast<void*>(value);
- } else {
- O* map = I::ReadField<O*>(obj, I::kHeapObjectMapOffset);
- int instance_type = I::ReadField<uint8_t>(map, I::kMapInstanceTypeOffset);
- if (instance_type == I::kProxyType) {
- return I::ReadField<void*>(obj, I::kProxyProxyOffset);
- } else {
- return NULL;
- }
- }
+ return internal::Internals::GetExternalPointer(obj);
}
void* Object::GetPointerFromInternalField(int index) {
- return External::Unwrap(GetInternalField(index));
+ typedef internal::Object O;
+ typedef internal::Internals I;
+
+ O* obj = *reinterpret_cast<O**>(this);
+
+ if (I::GetInstanceType(obj) == I::kJSObjectType) {
+ // If the object is a plain JSObject, which is the common case,
+ // we know where to find the internal fields and can return the
+ // value directly.
+ int offset = I::kJSObjectHeaderSize + (sizeof(void*) * index);
+ O* value = I::ReadField<O*>(obj, offset);
+ return I::GetExternalPointer(value);
+ }
+
+ return SlowGetPointerFromInternalField(index);
}
@@ -2982,10 +3030,8 @@ String::ExternalStringResource* String::GetExternalStringResource() const {
typedef internal::Object O;
typedef internal::Internals I;
O* obj = *reinterpret_cast<O**>(const_cast<String*>(this));
- O* map = I::ReadField<O*>(obj, I::kHeapObjectMapOffset);
- int instance_type = I::ReadField<uint8_t>(map, I::kMapInstanceTypeOffset);
String::ExternalStringResource* result;
- if (I::IsExternalTwoByteString(instance_type)) {
+ if (I::IsExternalTwoByteString(I::GetInstanceType(obj))) {
void* value = I::ReadField<void*>(obj, I::kStringResourceOffset);
result = reinterpret_cast<String::ExternalStringResource*>(value);
} else {
@@ -3011,9 +3057,7 @@ bool Value::QuickIsString() const {
typedef internal::Internals I;
O* obj = *reinterpret_cast<O**>(const_cast<Value*>(this));
if (!I::HasHeapObjectTag(obj)) return false;
- O* map = I::ReadField<O*>(obj, I::kHeapObjectMapOffset);
- int instance_type = I::ReadField<uint8_t>(map, I::kMapInstanceTypeOffset);
- return (instance_type < I::kFirstNonstringType);
+ return (I::GetInstanceType(obj) < I::kFirstNonstringType);
}
@@ -3025,6 +3069,15 @@ Number* Number::Cast(v8::Value* value) {
}
+Local<Integer> Integer::NewFromUnsigned(uint32_t value) {
+ bool fits_into_int32_t = (value & (1 << 31)) == 0;
+ if (fits_into_int32_t) {
+ return Integer::New(static_cast<int32_t>(value));
+ }
+ return Local<Integer>::Cast(Number::New(value));
+}
+
+
Integer* Integer::Cast(v8::Value* value) {
#ifdef V8_ENABLE_CHECKS
CheckCast(value);
diff --git a/deps/v8/src/SConscript b/deps/v8/src/SConscript
index b6c2b4d26..85fd72496 100755
--- a/deps/v8/src/SConscript
+++ b/deps/v8/src/SConscript
@@ -34,51 +34,129 @@ Import('context')
SOURCES = {
- 'all': [
- 'accessors.cc', 'allocation.cc', 'api.cc', 'assembler.cc', 'ast.cc',
- 'bootstrapper.cc', 'builtins.cc', 'checks.cc', 'code-stubs.cc',
- 'codegen.cc', 'compilation-cache.cc', 'compiler.cc', 'contexts.cc',
- 'conversions.cc', 'counters.cc', 'dateparser.cc', 'debug.cc',
- 'debug-agent.cc', 'disassembler.cc', 'execution.cc', 'factory.cc',
- 'flags.cc', 'frame-element.cc', 'frames.cc', 'func-name-inferrer.cc',
- 'global-handles.cc', 'handles.cc', 'hashmap.cc', 'heap.cc',
- 'heap-profiler.cc', 'ic.cc', 'interpreter-irregexp.cc', 'jsregexp.cc',
- 'jump-target.cc', 'log.cc', 'log-utils.cc', 'mark-compact.cc',
- 'messages.cc', 'objects.cc', 'oprofile-agent.cc', 'parser.cc',
- 'property.cc', 'regexp-macro-assembler.cc',
- 'regexp-macro-assembler-irregexp.cc', 'regexp-stack.cc',
- 'register-allocator.cc', 'rewriter.cc', 'runtime.cc', 'scanner.cc',
- 'scopeinfo.cc', 'scopes.cc', 'serialize.cc', 'snapshot-common.cc',
- 'spaces.cc', 'string-stream.cc', 'stub-cache.cc', 'token.cc', 'top.cc',
- 'unicode.cc', 'usage-analyzer.cc', 'utils.cc', 'v8-counters.cc',
- 'v8.cc', 'v8threads.cc', 'variables.cc', 'version.cc',
- 'virtual-frame.cc', 'zone.cc'
- ],
- 'arch:arm': [
- 'arm/assembler-arm.cc', 'arm/builtins-arm.cc', 'arm/codegen-arm.cc',
- 'arm/constants-arm.cc', 'arm/cpu-arm.cc', 'arm/disasm-arm.cc',
- 'arm/debug-arm.cc', 'arm/frames-arm.cc', 'arm/ic-arm.cc',
- 'arm/jump-target-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'
- ],
- 'arch:ia32': [
- 'ia32/assembler-ia32.cc', 'ia32/builtins-ia32.cc',
- 'ia32/codegen-ia32.cc', 'ia32/cpu-ia32.cc', 'ia32/disasm-ia32.cc',
- 'ia32/debug-ia32.cc', 'ia32/frames-ia32.cc', 'ia32/ic-ia32.cc',
- 'ia32/jump-target-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': [
- 'x64/assembler-x64.cc', 'x64/builtins-x64.cc', 'x64/codegen-x64.cc',
- 'x64/cpu-x64.cc', 'x64/disasm-x64.cc', 'x64/debug-x64.cc',
- 'x64/frames-x64.cc', 'x64/ic-x64.cc', 'x64/jump-target-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'
- ],
+ 'all': Split("""
+ accessors.cc
+ allocation.cc
+ api.cc
+ assembler.cc
+ ast.cc
+ bootstrapper.cc
+ builtins.cc
+ checks.cc
+ code-stubs.cc
+ codegen.cc
+ compilation-cache.cc
+ compiler.cc
+ contexts.cc
+ conversions.cc
+ counters.cc
+ dateparser.cc
+ debug-agent.cc
+ debug.cc
+ disassembler.cc
+ execution.cc
+ factory.cc
+ fast-codegen.cc
+ flags.cc
+ frame-element.cc
+ frames.cc
+ func-name-inferrer.cc
+ global-handles.cc
+ handles.cc
+ hashmap.cc
+ heap-profiler.cc
+ heap.cc
+ ic.cc
+ interpreter-irregexp.cc
+ jsregexp.cc
+ jump-target.cc
+ log-utils.cc
+ log.cc
+ mark-compact.cc
+ messages.cc
+ objects.cc
+ oprofile-agent.cc
+ parser.cc
+ property.cc
+ regexp-macro-assembler-irregexp.cc
+ regexp-macro-assembler.cc
+ regexp-stack.cc
+ register-allocator.cc
+ rewriter.cc
+ runtime.cc
+ scanner.cc
+ scopeinfo.cc
+ scopes.cc
+ serialize.cc
+ snapshot-common.cc
+ spaces.cc
+ string-stream.cc
+ stub-cache.cc
+ token.cc
+ top.cc
+ unicode.cc
+ usage-analyzer.cc
+ utils.cc
+ v8-counters.cc
+ v8.cc
+ v8threads.cc
+ variables.cc
+ version.cc
+ virtual-frame.cc
+ zone.cc
+ """),
+ 'arch:arm': Split("""
+ arm/assembler-arm.cc
+ arm/builtins-arm.cc
+ arm/codegen-arm.cc
+ arm/constants-arm.cc
+ arm/cpu-arm.cc
+ arm/debug-arm.cc
+ arm/disasm-arm.cc
+ arm/fast-codegen-arm.cc
+ arm/frames-arm.cc
+ arm/ic-arm.cc
+ arm/jump-target-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
+ """),
+ 'arch:ia32': Split("""
+ ia32/assembler-ia32.cc
+ ia32/builtins-ia32.cc
+ ia32/codegen-ia32.cc
+ ia32/cpu-ia32.cc
+ ia32/debug-ia32.cc
+ ia32/disasm-ia32.cc
+ ia32/fast-codegen-ia32.cc
+ ia32/frames-ia32.cc
+ ia32/ic-ia32.cc
+ ia32/jump-target-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("""
+ x64/assembler-x64.cc
+ x64/builtins-x64.cc
+ x64/codegen-x64.cc
+ x64/cpu-x64.cc
+ x64/debug-x64.cc
+ x64/disasm-x64.cc
+ x64/fast-codegen-x64.cc
+ x64/frames-x64.cc
+ x64/ic-x64.cc
+ x64/jump-target-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'],
'os:freebsd': ['platform-freebsd.cc', 'platform-posix.cc'],
'os:linux': ['platform-linux.cc', 'platform-posix.cc'],
diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc
index 25354b269..ffbe98eaf 100644
--- a/deps/v8/src/api.cc
+++ b/deps/v8/src/api.cc
@@ -2290,7 +2290,7 @@ void v8::Object::SetIndexedPropertiesToPixelData(uint8_t* data, int length) {
ON_BAILOUT("v8::SetElementsToPixelData()", return);
ENTER_V8;
HandleScope scope;
- if (!ApiCheck(i::Smi::IsValid(length),
+ if (!ApiCheck(length <= i::PixelArray::kMaxLength,
"v8::Object::SetIndexedPropertiesToPixelData()",
"length exceeds max acceptable value")) {
return;
@@ -2578,7 +2578,16 @@ void v8::Object::SetInternalField(int index, v8::Handle<Value> value) {
void v8::Object::SetPointerInInternalField(int index, void* value) {
- SetInternalField(index, External::Wrap(value));
+ i::Object* as_object = reinterpret_cast<i::Object*>(value);
+ if (as_object->IsSmi()) {
+ Utils::OpenHandle(this)->SetInternalField(index, as_object);
+ return;
+ }
+ HandleScope scope;
+ i::Handle<i::Proxy> proxy =
+ i::Factory::NewProxy(reinterpret_cast<i::Address>(value), i::TENURED);
+ if (!proxy.is_null())
+ Utils::OpenHandle(this)->SetInternalField(index, *proxy);
}
@@ -2760,7 +2769,9 @@ v8::Local<v8::Context> Context::GetEntered() {
v8::Local<v8::Context> Context::GetCurrent() {
if (IsDeadCheck("v8::Context::GetCurrent()")) return Local<Context>();
- i::Handle<i::Context> context(i::Top::global_context());
+ i::Handle<i::Object> current = i::Top::global_context();
+ if (current.is_null()) return Local<Context>();
+ i::Handle<i::Context> context = i::Handle<i::Context>::cast(current);
return Utils::ToLocal(context);
}
@@ -2837,36 +2848,39 @@ static void* ExternalValueImpl(i::Handle<i::Object> obj) {
}
-static const intptr_t kAlignedPointerMask = 3;
-
Local<Value> v8::External::Wrap(void* data) {
STATIC_ASSERT(sizeof(data) == sizeof(i::Address));
LOG_API("External::Wrap");
EnsureInitialized("v8::External::Wrap()");
ENTER_V8;
- if ((reinterpret_cast<intptr_t>(data) & kAlignedPointerMask) == 0) {
- uintptr_t data_ptr = reinterpret_cast<uintptr_t>(data);
- intptr_t data_value =
- static_cast<intptr_t>(data_ptr >> i::Internals::kAlignedPointerShift);
- STATIC_ASSERT(sizeof(data_ptr) == sizeof(data_value));
- if (i::Smi::IsIntptrValid(data_value)) {
- i::Handle<i::Object> obj(i::Smi::FromIntptr(data_value));
- return Utils::ToLocal(obj);
- }
+ i::Object* as_object = reinterpret_cast<i::Object*>(data);
+ if (as_object->IsSmi()) {
+ return Utils::ToLocal(i::Handle<i::Object>(as_object));
}
return ExternalNewImpl(data);
}
+void* v8::Object::SlowGetPointerFromInternalField(int index) {
+ i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
+ i::Object* value = obj->GetInternalField(index);
+ if (value->IsSmi()) {
+ return value;
+ } else if (value->IsProxy()) {
+ return reinterpret_cast<void*>(i::Proxy::cast(value)->proxy());
+ } else {
+ return NULL;
+ }
+}
+
+
void* v8::External::FullUnwrap(v8::Handle<v8::Value> wrapper) {
if (IsDeadCheck("v8::External::Unwrap()")) return 0;
i::Handle<i::Object> obj = Utils::OpenHandle(*wrapper);
void* result;
if (obj->IsSmi()) {
// The external value was an aligned pointer.
- uintptr_t value = static_cast<uintptr_t>(
- i::Smi::cast(*obj)->value()) << i::Internals::kAlignedPointerShift;
- result = reinterpret_cast<void*>(value);
+ result = *obj;
} else if (obj->IsProxy()) {
result = ExternalValueImpl(obj);
} else {
@@ -2912,6 +2926,18 @@ Local<String> v8::String::New(const char* data, int length) {
}
+Local<String> v8::String::Concat(Handle<String> left, Handle<String> right) {
+ EnsureInitialized("v8::String::New()");
+ LOG_API("String::New(char)");
+ ENTER_V8;
+ i::Handle<i::String> left_string = Utils::OpenHandle(*left);
+ i::Handle<i::String> right_string = Utils::OpenHandle(*right);
+ i::Handle<i::String> result = i::Factory::NewConsString(left_string,
+ right_string);
+ return Utils::ToLocal(result);
+}
+
+
Local<String> v8::String::NewUndetectable(const char* data, int length) {
EnsureInitialized("v8::String::NewUndetectable()");
LOG_API("String::NewUndetectable(char)");
diff --git a/deps/v8/src/arm/assembler-arm-inl.h b/deps/v8/src/arm/assembler-arm-inl.h
index 5417ed7d3..48cc09081 100644
--- a/deps/v8/src/arm/assembler-arm-inl.h
+++ b/deps/v8/src/arm/assembler-arm-inl.h
@@ -110,7 +110,7 @@ Address* RelocInfo::target_reference_address() {
Address RelocInfo::call_address() {
- ASSERT(IsCallInstruction());
+ ASSERT(IsPatchedReturnSequence());
// The 2 instructions offset assumes patched return sequence.
ASSERT(IsJSReturn(rmode()));
return Memory::Address_at(pc_ + 2 * Assembler::kInstrSize);
@@ -118,7 +118,7 @@ Address RelocInfo::call_address() {
void RelocInfo::set_call_address(Address target) {
- ASSERT(IsCallInstruction());
+ ASSERT(IsPatchedReturnSequence());
// The 2 instructions offset assumes patched return sequence.
ASSERT(IsJSReturn(rmode()));
Memory::Address_at(pc_ + 2 * Assembler::kInstrSize) = target;
@@ -131,7 +131,7 @@ Object* RelocInfo::call_object() {
Object** RelocInfo::call_object_address() {
- ASSERT(IsCallInstruction());
+ ASSERT(IsPatchedReturnSequence());
// The 2 instructions offset assumes patched return sequence.
ASSERT(IsJSReturn(rmode()));
return reinterpret_cast<Object**>(pc_ + 2 * Assembler::kInstrSize);
@@ -143,7 +143,7 @@ void RelocInfo::set_call_object(Object* target) {
}
-bool RelocInfo::IsCallInstruction() {
+bool RelocInfo::IsPatchedReturnSequence() {
// On ARM a "call instruction" is actually two instructions.
// mov lr, pc
// ldr pc, [pc, #XXX]
diff --git a/deps/v8/src/arm/codegen-arm.cc b/deps/v8/src/arm/codegen-arm.cc
index cdd32f30f..147c5e354 100644
--- a/deps/v8/src/arm/codegen-arm.cc
+++ b/deps/v8/src/arm/codegen-arm.cc
@@ -1539,191 +1539,200 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
}
-void CodeGenerator::VisitLoopStatement(LoopStatement* node) {
+void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) {
#ifdef DEBUG
int original_height = frame_->height();
#endif
VirtualFrame::SpilledScope spilled_scope;
- Comment cmnt(masm_, "[ LoopStatement");
+ Comment cmnt(masm_, "[ DoWhileStatement");
CodeForStatementPosition(node);
node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
-
- // Simple condition analysis. ALWAYS_TRUE and ALWAYS_FALSE represent a
- // known result for the test expression, with no side effects.
- enum { ALWAYS_TRUE, ALWAYS_FALSE, DONT_KNOW } info = DONT_KNOW;
- if (node->cond() == NULL) {
- ASSERT(node->type() == LoopStatement::FOR_LOOP);
- info = ALWAYS_TRUE;
- } else {
- Literal* lit = node->cond()->AsLiteral();
- if (lit != NULL) {
- if (lit->IsTrue()) {
- info = ALWAYS_TRUE;
- } else if (lit->IsFalse()) {
- info = ALWAYS_FALSE;
- }
- }
+ JumpTarget body(JumpTarget::BIDIRECTIONAL);
+
+ // 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()->set_direction(JumpTarget::BIDIRECTIONAL);
+ node->continue_target()->Bind();
+ break;
+ case ALWAYS_FALSE:
+ node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
+ break;
+ case DONT_KNOW:
+ node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
+ body.Bind();
+ break;
}
- switch (node->type()) {
- case LoopStatement::DO_LOOP: {
- JumpTarget body(JumpTarget::BIDIRECTIONAL);
+ CheckStack(); // TODO(1222600): ignore if body contains calls.
+ VisitAndSpill(node->body());
- // 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.
- if (info == ALWAYS_TRUE) {
- node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
+ // 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();
- } else if (info == ALWAYS_FALSE) {
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
- } else {
- ASSERT(info == DONT_KNOW);
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
- body.Bind();
}
-
- CheckStack(); // TODO(1222600): ignore if body contains calls.
- VisitAndSpill(node->body());
-
- // Compile the test.
- if (info == ALWAYS_TRUE) {
- if (has_valid_frame()) {
- // If control can fall off the end of the body, jump back to the
- // top.
- node->continue_target()->Jump();
- }
- } else if (info == 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();
- }
- } else {
- ASSERT(info == 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();
- }
+ 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()) {
+ LoadConditionAndSpill(node->cond(), NOT_INSIDE_TYPEOF,
+ &body, node->break_target(), true);
if (has_valid_frame()) {
- LoadConditionAndSpill(node->cond(), NOT_INSIDE_TYPEOF,
- &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);
- }
+ // A invalid frame here indicates that control did not
+ // fall out of the test expression.
+ Branch(true, &body);
}
}
break;
- }
+ }
- case LoopStatement::WHILE_LOOP: {
- // If the test is never true and has no side effects there is no need
- // to compile the test or body.
- if (info == ALWAYS_FALSE) break;
+ if (node->break_target()->is_linked()) {
+ node->break_target()->Bind();
+ }
+ ASSERT(!has_valid_frame() || frame_->height() == original_height);
+}
- // Label the top of the loop with the continue target for the backward
- // CFG edge.
- node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
- node->continue_target()->Bind();
- if (info == DONT_KNOW) {
- JumpTarget body;
- LoadConditionAndSpill(node->cond(), NOT_INSIDE_TYPEOF,
- &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();
- }
- }
+void CodeGenerator::VisitWhileStatement(WhileStatement* node) {
+#ifdef DEBUG
+ int original_height = frame_->height();
+#endif
+ VirtualFrame::SpilledScope spilled_scope;
+ Comment cmnt(masm_, "[ WhileStatement");
+ CodeForStatementPosition(node);
- if (has_valid_frame()) {
- CheckStack(); // TODO(1222600): ignore if body contains calls.
- VisitAndSpill(node->body());
+ // 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;
- // If control flow can fall out of the body, jump back to the top.
- if (has_valid_frame()) {
- node->continue_target()->Jump();
- }
- }
- break;
+ node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
+
+ // Label the top of the loop with the continue target for the backward
+ // CFG edge.
+ node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
+ node->continue_target()->Bind();
+
+ if (info == DONT_KNOW) {
+ JumpTarget body;
+ LoadConditionAndSpill(node->cond(), NOT_INSIDE_TYPEOF,
+ &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();
+ }
+ }
- case LoopStatement::FOR_LOOP: {
- JumpTarget loop(JumpTarget::BIDIRECTIONAL);
+ if (has_valid_frame()) {
+ CheckStack(); // TODO(1222600): ignore if body contains calls.
+ VisitAndSpill(node->body());
- if (node->init() != NULL) {
- VisitAndSpill(node->init());
- }
+ // 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();
+ }
+ ASSERT(!has_valid_frame() || frame_->height() == original_height);
+}
- // There is no need to compile the test or body.
- if (info == ALWAYS_FALSE) break;
- // If there is no update statement, label the top of the loop with the
- // continue target, otherwise with the loop target.
- if (node->next() == NULL) {
- node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
- node->continue_target()->Bind();
- } else {
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
- loop.Bind();
- }
+void CodeGenerator::VisitForStatement(ForStatement* node) {
+#ifdef DEBUG
+ int original_height = frame_->height();
+#endif
+ VirtualFrame::SpilledScope spilled_scope;
+ Comment cmnt(masm_, "[ ForStatement");
+ CodeForStatementPosition(node);
+ if (node->init() != NULL) {
+ VisitAndSpill(node->init());
+ }
- // If the test is always true, there is no need to compile it.
- if (info == DONT_KNOW) {
- JumpTarget body;
- LoadConditionAndSpill(node->cond(), NOT_INSIDE_TYPEOF,
- &body, node->break_target(), true);
- if (has_valid_frame()) {
- Branch(false, node->break_target());
- }
- if (has_valid_frame() || body.is_linked()) {
- body.Bind();
- }
- }
+ // 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()->set_direction(JumpTarget::FORWARD_ONLY);
+
+ // 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()->set_direction(JumpTarget::BIDIRECTIONAL);
+ node->continue_target()->Bind();
+ } else {
+ node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
+ loop.Bind();
+ }
+ // If the test is always true, there is no need to compile it.
+ if (info == DONT_KNOW) {
+ JumpTarget body;
+ LoadConditionAndSpill(node->cond(), NOT_INSIDE_TYPEOF,
+ &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.
+ VisitAndSpill(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()) {
- CheckStack(); // TODO(1222600): ignore if body contains calls.
- VisitAndSpill(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);
- VisitAndSpill(node->next());
- loop.Jump();
- }
- }
+ 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);
+ VisitAndSpill(node->next());
+ loop.Jump();
}
- break;
}
}
-
if (node->break_target()->is_linked()) {
node->break_target()->Bind();
}
- node->continue_target()->Unuse();
- node->break_target()->Unuse();
ASSERT(!has_valid_frame() || frame_->height() == original_height);
}
@@ -1918,12 +1927,12 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
}
-void CodeGenerator::VisitTryCatch(TryCatch* node) {
+void CodeGenerator::VisitTryCatchStatement(TryCatchStatement* node) {
#ifdef DEBUG
int original_height = frame_->height();
#endif
VirtualFrame::SpilledScope spilled_scope;
- Comment cmnt(masm_, "[ TryCatch");
+ Comment cmnt(masm_, "[ TryCatchStatement");
CodeForStatementPosition(node);
JumpTarget try_block;
@@ -2043,12 +2052,12 @@ void CodeGenerator::VisitTryCatch(TryCatch* node) {
}
-void CodeGenerator::VisitTryFinally(TryFinally* node) {
+void CodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* node) {
#ifdef DEBUG
int original_height = frame_->height();
#endif
VirtualFrame::SpilledScope spilled_scope;
- Comment cmnt(masm_, "[ TryFinally");
+ Comment cmnt(masm_, "[ TryFinallyStatement");
CodeForStatementPosition(node);
// State: Used to keep track of reason for entering the finally
diff --git a/deps/v8/src/arm/codegen-arm.h b/deps/v8/src/arm/codegen-arm.h
index 1eb0932eb..7b50b0104 100644
--- a/deps/v8/src/arm/codegen-arm.h
+++ b/deps/v8/src/arm/codegen-arm.h
@@ -147,6 +147,15 @@ class CodeGenerator: public AstVisitor {
Handle<Script> script,
bool is_eval);
+ // Printing of AST, etc. as requested by flags.
+ static void MakeCodePrologue(FunctionLiteral* fun);
+
+ // Allocate and install the code.
+ static Handle<Code> MakeCodeEpilogue(FunctionLiteral* fun,
+ MacroAssembler* masm,
+ Code::Flags flags,
+ Handle<Script> script);
+
#ifdef ENABLE_LOGGING_AND_PROFILING
static bool ShouldGenerateLog(Expression* type);
#endif
@@ -156,6 +165,8 @@ class CodeGenerator: public AstVisitor {
bool is_toplevel,
Handle<Script> script);
+ static void RecordPositions(MacroAssembler* masm, int pos);
+
// Accessors
MacroAssembler* masm() { return masm_; }
@@ -365,6 +376,14 @@ class CodeGenerator: public AstVisitor {
inline void GenerateMathSin(ZoneList<Expression*>* args);
inline void GenerateMathCos(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.
diff --git a/deps/v8/src/arm/debug-arm.cc b/deps/v8/src/arm/debug-arm.cc
index 4f45175a3..ef3365395 100644
--- a/deps/v8/src/arm/debug-arm.cc
+++ b/deps/v8/src/arm/debug-arm.cc
@@ -68,7 +68,7 @@ void BreakLocationIterator::ClearDebugBreakAtReturn() {
// A debug break in the exit code is identified by a call.
bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()));
- return rinfo->IsCallInstruction();
+ return rinfo->IsPatchedReturnSequence();
}
diff --git a/deps/v8/src/arm/fast-codegen-arm.cc b/deps/v8/src/arm/fast-codegen-arm.cc
new file mode 100644
index 000000000..d2e620cfd
--- /dev/null
+++ b/deps/v8/src/arm/fast-codegen-arm.cc
@@ -0,0 +1,176 @@
+// 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 "fast-codegen.h"
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm_)
+
+// Generate code for a JS function. On entry to the function the receiver
+// and arguments have been pushed on the stack left to right. The actual
+// argument count matches the formal parameter count expected by the
+// function.
+//
+// The live registers are:
+// o r1: the JS function object being called (ie, ourselves)
+// o cp: our context
+// o fp: our caller's frame pointer
+// o sp: stack pointer
+// o lr: return address
+//
+// The function builds a JS frame. Please see JavaScriptFrameConstants in
+// frames-arm.h for its layout.
+void FastCodeGenerator::Generate(FunctionLiteral* fun) {
+ function_ = fun;
+ // ARM does NOT call SetFunctionPosition.
+
+ __ stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit());
+ // Adjust fp to point to caller's fp.
+ __ add(fp, sp, Operand(2 * kPointerSize));
+
+ { Comment cmnt(masm_, "[ Allocate locals");
+ int locals_count = fun->scope()->num_stack_slots();
+ if (locals_count > 0) {
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ }
+ if (FLAG_check_stack) {
+ __ LoadRoot(r2, Heap::kStackLimitRootIndex);
+ }
+ for (int i = 0; i < locals_count; i++) {
+ __ push(ip);
+ }
+ }
+
+ if (FLAG_check_stack) {
+ // Put the lr setup instruction in the delay slot. The kInstrSize is
+ // added to the implicit 8 byte offset that always applies to operations
+ // with pc and gives a return address 12 bytes down.
+ Comment cmnt(masm_, "[ Stack check");
+ __ add(lr, pc, Operand(Assembler::kInstrSize));
+ __ cmp(sp, Operand(r2));
+ StackCheckStub stub;
+ __ mov(pc,
+ Operand(reinterpret_cast<intptr_t>(stub.GetCode().location()),
+ RelocInfo::CODE_TARGET),
+ LeaveCC,
+ lo);
+ }
+
+ { Comment cmnt(masm_, "[ Body");
+ VisitStatements(fun->body());
+ }
+
+ { Comment cmnt(masm_, "[ return <undefined>;");
+ // Emit a 'return undefined' in case control fell off the end of the
+ // body.
+ __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
+ SetReturnPosition(fun);
+ __ RecordJSReturn();
+ __ mov(sp, fp);
+ __ ldm(ia_w, sp, fp.bit() | lr.bit());
+ int num_parameters = function_->scope()->num_parameters();
+ __ add(sp, sp, Operand((num_parameters + 1) * kPointerSize));
+ __ Jump(lr);
+ }
+}
+
+
+void FastCodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) {
+ Comment cmnt(masm_, "[ ExpressionStatement");
+ SetStatementPosition(stmt);
+ Visit(stmt->expression());
+}
+
+
+void FastCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
+ Comment cmnt(masm_, "[ ReturnStatement");
+ SetStatementPosition(stmt);
+ Visit(stmt->expression());
+ __ pop(r0);
+ __ RecordJSReturn();
+ __ mov(sp, fp);
+ __ ldm(ia_w, sp, fp.bit() | lr.bit());
+ int num_parameters = function_->scope()->num_parameters();
+ __ add(sp, sp, Operand((num_parameters + 1) * kPointerSize));
+ __ Jump(lr);
+}
+
+
+void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
+ Comment cmnt(masm_, "[ VariableProxy");
+ Expression* rewrite = expr->var()->rewrite();
+ ASSERT(rewrite != NULL);
+
+ Slot* slot = rewrite->AsSlot();
+ ASSERT(slot != NULL);
+ { Comment cmnt(masm_, "[ Slot");
+ if (expr->location().is_temporary()) {
+ __ ldr(ip, MemOperand(fp, SlotOffset(slot)));
+ __ push(ip);
+ } else {
+ ASSERT(expr->location().is_nowhere());
+ }
+ }
+}
+
+
+void FastCodeGenerator::VisitLiteral(Literal* expr) {
+ Comment cmnt(masm_, "[ Literal");
+ if (expr->location().is_temporary()) {
+ __ mov(ip, Operand(expr->handle()));
+ __ push(ip);
+ } else {
+ ASSERT(expr->location().is_nowhere());
+ }
+}
+
+
+void FastCodeGenerator::VisitAssignment(Assignment* expr) {
+ Comment cmnt(masm_, "[ Assignment");
+ ASSERT(expr->op() == Token::ASSIGN || expr->op() == Token::INIT_VAR);
+
+ Visit(expr->value());
+
+ Variable* var = expr->target()->AsVariableProxy()->AsVariable();
+ ASSERT(var != NULL && var->slot() != NULL);
+
+ if (expr->location().is_temporary()) {
+ __ ldr(ip, MemOperand(sp));
+ } else {
+ ASSERT(expr->location().is_nowhere());
+ __ pop(ip);
+ }
+ __ str(ip, MemOperand(fp, SlotOffset(var->slot())));
+}
+
+
+} } // namespace v8::internal
diff --git a/deps/v8/src/arm/macro-assembler-arm.h b/deps/v8/src/arm/macro-assembler-arm.h
index ee9d70d31..e37bb5e1c 100644
--- a/deps/v8/src/arm/macro-assembler-arm.h
+++ b/deps/v8/src/arm/macro-assembler-arm.h
@@ -246,7 +246,6 @@ class MacroAssembler: public Assembler {
// Call a code stub.
void CallStub(CodeStub* stub, Condition cond = al);
- void CallJSExitStub(CodeStub* stub);
// Return from a code stub after popping its arguments.
void StubReturn(int argc);
diff --git a/deps/v8/src/array.js b/deps/v8/src/array.js
index f8e63d084..94d74a508 100644
--- a/deps/v8/src/array.js
+++ b/deps/v8/src/array.js
@@ -1058,6 +1058,10 @@ function ArrayReduceRight(callback, current) {
return current;
}
+// ES5, 15.4.3.2
+function ArrayIsArray(obj) {
+ return IS_ARRAY(obj);
+}
// -------------------------------------------------------------------
@@ -1075,6 +1079,11 @@ function SetupArray() {
// object.
%SetProperty($Array.prototype, "constructor", $Array, DONT_ENUM);
+ // Setup non-enumerable functions on the Array object.
+ InstallFunctions($Array, DONT_ENUM, $Array(
+ "isArray", ArrayIsArray
+ ));
+
// Setup non-enumerable functions of the Array.prototype object and
// set their names.
InstallFunctionsOnHiddenPrototype($Array.prototype, DONT_ENUM, $Array(
@@ -1098,8 +1107,9 @@ function SetupArray() {
"indexOf", ArrayIndexOf,
"lastIndexOf", ArrayLastIndexOf,
"reduce", ArrayReduce,
- "reduceRight", ArrayReduceRight));
-
+ "reduceRight", ArrayReduceRight
+ ));
+
// Manipulate the length of some of the functions to meet
// expectations set by ECMA-262 or Mozilla.
UpdateFunctionLengths({
diff --git a/deps/v8/src/assembler.cc b/deps/v8/src/assembler.cc
index d81b4b05e..34595f83f 100644
--- a/deps/v8/src/assembler.cc
+++ b/deps/v8/src/assembler.cc
@@ -343,9 +343,6 @@ void RelocIterator::next() {
if (SetMode(RelocInfo::EMBEDDED_OBJECT)) return;
} else if (tag == kCodeTargetTag) {
ReadTaggedPC();
- if (*(reinterpret_cast<int*>(rinfo_.pc())) == 0x61) {
- tag = 0;
- }
if (SetMode(RelocInfo::CODE_TARGET)) return;
} else if (tag == kPositionTag) {
ReadTaggedPC();
diff --git a/deps/v8/src/assembler.h b/deps/v8/src/assembler.h
index 323e06aff..21a66dd50 100644
--- a/deps/v8/src/assembler.h
+++ b/deps/v8/src/assembler.h
@@ -217,10 +217,10 @@ class RelocInfo BASE_EMBEDDED {
// Patch the code with a call.
void PatchCodeWithCall(Address target, int guard_bytes);
- // Check whether the current instruction is currently a call
- // sequence (whether naturally or a return sequence overwritten
- // to enter the debugger).
- INLINE(bool IsCallInstruction());
+
+ // Check whether this return sequence has been patched
+ // with a call to the debugger.
+ INLINE(bool IsPatchedReturnSequence());
#ifdef ENABLE_DISASSEMBLER
// Printing
diff --git a/deps/v8/src/ast.cc b/deps/v8/src/ast.cc
index 692bec01d..f6864b82e 100644
--- a/deps/v8/src/ast.cc
+++ b/deps/v8/src/ast.cc
@@ -91,20 +91,6 @@ void VariableProxy::BindTo(Variable* var) {
}
-#ifdef DEBUG
-
-const char* LoopStatement::OperatorString() const {
- switch (type()) {
- case DO_LOOP: return "DO";
- case FOR_LOOP: return "FOR";
- case WHILE_LOOP: return "WHILE";
- }
- return NULL;
-}
-
-#endif // DEBUG
-
-
Token::Value Assignment::binary_op() const {
switch (op_) {
case Token::ASSIGN_BIT_OR: return Token::BIT_OR;
@@ -187,6 +173,13 @@ void TargetCollector::AddTarget(BreakTarget* target) {
// Implementation of AstVisitor
+void AstVisitor::VisitDeclarations(ZoneList<Declaration*>* declarations) {
+ for (int i = 0; i < declarations->length(); i++) {
+ Visit(declarations->at(i));
+ }
+}
+
+
void AstVisitor::VisitStatements(ZoneList<Statement*>* statements) {
for (int i = 0; i < statements->length(); i++) {
Visit(statements->at(i));
diff --git a/deps/v8/src/ast.h b/deps/v8/src/ast.h
index 6a1cdf51c..42154f61c 100644
--- a/deps/v8/src/ast.h
+++ b/deps/v8/src/ast.h
@@ -28,14 +28,14 @@
#ifndef V8_AST_H_
#define V8_AST_H_
+#include "location.h"
#include "execution.h"
#include "factory.h"
+#include "jsregexp.h"
+#include "jump-target.h"
#include "runtime.h"
#include "token.h"
#include "variables.h"
-#include "macro-assembler.h"
-#include "jsregexp.h"
-#include "jump-target.h"
namespace v8 {
namespace internal {
@@ -64,10 +64,12 @@ namespace internal {
V(WithEnterStatement) \
V(WithExitStatement) \
V(SwitchStatement) \
- V(LoopStatement) \
+ V(DoWhileStatement) \
+ V(WhileStatement) \
+ V(ForStatement) \
V(ForInStatement) \
- V(TryCatch) \
- V(TryFinally) \
+ V(TryCatchStatement) \
+ V(TryFinallyStatement) \
V(DebuggerStatement)
#define EXPRESSION_NODE_LIST(V) \
@@ -160,6 +162,8 @@ class Statement: public AstNode {
class Expression: public AstNode {
public:
+ Expression() : location_(Location::Temporary()) {}
+
virtual Expression* AsExpression() { return this; }
virtual bool IsValidJSON() { return false; }
@@ -173,8 +177,12 @@ class Expression: public AstNode {
// Static type information for this expression.
SmiAnalysis* type() { return &type_; }
+ Location location() { return location_; }
+ void set_location(Location loc) { location_ = loc; }
+
private:
SmiAnalysis type_;
+ Location location_;
};
@@ -294,13 +302,59 @@ class IterationStatement: public BreakableStatement {
};
-class LoopStatement: public IterationStatement {
+class DoWhileStatement: public IterationStatement {
public:
- enum Type { DO_LOOP, FOR_LOOP, WHILE_LOOP };
+ explicit DoWhileStatement(ZoneStringList* labels)
+ : IterationStatement(labels), cond_(NULL) {
+ }
+
+ void Initialize(Expression* cond, Statement* body) {
+ IterationStatement::Initialize(body);
+ cond_ = cond;
+ }
+
+ virtual void Accept(AstVisitor* v);
- LoopStatement(ZoneStringList* labels, Type type)
+ Expression* cond() const { return cond_; }
+
+ private:
+ Expression* cond_;
+};
+
+
+class WhileStatement: public IterationStatement {
+ public:
+ explicit WhileStatement(ZoneStringList* labels)
+ : IterationStatement(labels),
+ cond_(NULL),
+ may_have_function_literal_(true) {
+ }
+
+ void Initialize(Expression* cond, Statement* body) {
+ IterationStatement::Initialize(body);
+ cond_ = cond;
+ }
+
+ virtual void Accept(AstVisitor* v);
+
+ Expression* cond() const { return cond_; }
+ bool may_have_function_literal() const {
+ return may_have_function_literal_;
+ }
+
+ private:
+ Expression* cond_;
+ // True if there is a function literal subexpression in the condition.
+ bool may_have_function_literal_;
+
+ friend class AstOptimizer;
+};
+
+
+class ForStatement: public IterationStatement {
+ public:
+ explicit ForStatement(ZoneStringList* labels)
: IterationStatement(labels),
- type_(type),
init_(NULL),
cond_(NULL),
next_(NULL),
@@ -311,8 +365,6 @@ class LoopStatement: public IterationStatement {
Expression* cond,
Statement* next,
Statement* body) {
- ASSERT(init == NULL || type_ == FOR_LOOP);
- ASSERT(next == NULL || type_ == FOR_LOOP);
IterationStatement::Initialize(body);
init_ = init;
cond_ = cond;
@@ -321,7 +373,6 @@ class LoopStatement: public IterationStatement {
virtual void Accept(AstVisitor* v);
- Type type() const { return type_; }
Statement* init() const { return init_; }
Expression* cond() const { return cond_; }
Statement* next() const { return next_; }
@@ -329,12 +380,7 @@ class LoopStatement: public IterationStatement {
return may_have_function_literal_;
}
-#ifdef DEBUG
- const char* OperatorString() const;
-#endif
-
private:
- Type type_;
Statement* init_;
Expression* cond_;
Statement* next_;
@@ -569,9 +615,11 @@ class TryStatement: public Statement {
};
-class TryCatch: public TryStatement {
+class TryCatchStatement: public TryStatement {
public:
- TryCatch(Block* try_block, Expression* catch_var, Block* catch_block)
+ TryCatchStatement(Block* try_block,
+ Expression* catch_var,
+ Block* catch_block)
: TryStatement(try_block),
catch_var_(catch_var),
catch_block_(catch_block) {
@@ -589,9 +637,9 @@ class TryCatch: public TryStatement {
};
-class TryFinally: public TryStatement {
+class TryFinallyStatement: public TryStatement {
public:
- TryFinally(Block* try_block, Block* finally_block)
+ TryFinallyStatement(Block* try_block, Block* finally_block)
: TryStatement(try_block),
finally_block_(finally_block) { }
@@ -1212,7 +1260,6 @@ class FunctionLiteral: public Expression {
Scope* scope,
ZoneList<Statement*>* body,
int materialized_literal_count,
- bool contains_array_literal,
int expected_property_count,
bool has_only_this_property_assignments,
bool has_only_simple_this_property_assignments,
@@ -1225,7 +1272,6 @@ class FunctionLiteral: public Expression {
scope_(scope),
body_(body),
materialized_literal_count_(materialized_literal_count),
- contains_array_literal_(contains_array_literal),
expected_property_count_(expected_property_count),
has_only_this_property_assignments_(has_only_this_property_assignments),
has_only_simple_this_property_assignments_(
@@ -1258,7 +1304,6 @@ class FunctionLiteral: public Expression {
bool is_expression() const { return is_expression_; }
int materialized_literal_count() { return materialized_literal_count_; }
- bool contains_array_literal() { return contains_array_literal_; }
int expected_property_count() { return expected_property_count_; }
bool has_only_this_property_assignments() {
return has_only_this_property_assignments_;
@@ -1293,7 +1338,6 @@ class FunctionLiteral: public Expression {
Scope* scope_;
ZoneList<Statement*>* body_;
int materialized_literal_count_;
- bool contains_array_literal_;
int expected_property_count_;
bool has_only_this_property_assignments_;
bool has_only_simple_this_property_assignments_;
@@ -1690,6 +1734,7 @@ class AstVisitor BASE_EMBEDDED {
void Visit(AstNode* node) { node->Accept(this); }
// Iteration
+ virtual void VisitDeclarations(ZoneList<Declaration*>* declarations);
virtual void VisitStatements(ZoneList<Statement*>* statements);
virtual void VisitExpressions(ZoneList<Expression*>* expressions);
diff --git a/deps/v8/src/checks.h b/deps/v8/src/checks.h
index b302e5bee..3b0c85135 100644
--- a/deps/v8/src/checks.h
+++ b/deps/v8/src/checks.h
@@ -80,6 +80,27 @@ static inline void CheckEqualsHelper(const char* file, int line,
}
}
+// Helper function used by the CHECK_EQ function when given int64_t
+// arguments. Should not be called directly.
+static inline void CheckEqualsHelper(const char* file, int line,
+ const char* expected_source,
+ int64_t expected,
+ const char* value_source,
+ int64_t value) {
+ if (expected != value) {
+ // Print int64_t values in hex, as two int32s,
+ // to avoid platform-dependencies.
+ V8_Fatal(file, line,
+ "CHECK_EQ(%s, %s) failed\n#"
+ " Expected: 0x%08x%08x\n# Found: 0x%08x%08x",
+ expected_source, value_source,
+ static_cast<uint32_t>(expected >> 32),
+ static_cast<uint32_t>(expected),
+ static_cast<uint32_t>(value >> 32),
+ static_cast<uint32_t>(value));
+ }
+}
+
// Helper function used by the CHECK_NE function when given int
// arguments. Should not be called directly.
diff --git a/deps/v8/src/code-stubs.cc b/deps/v8/src/code-stubs.cc
index 9c24c60b7..586c9480c 100644
--- a/deps/v8/src/code-stubs.cc
+++ b/deps/v8/src/code-stubs.cc
@@ -132,8 +132,6 @@ const char* CodeStub::MajorName(CodeStub::Major major_key) {
return "SetProperty";
case InvokeBuiltin:
return "InvokeBuiltin";
- case JSExit:
- return "JSExit";
case ConvertToDouble:
return "ConvertToDouble";
case WriteInt32ToHeapNumber:
diff --git a/deps/v8/src/code-stubs.h b/deps/v8/src/code-stubs.h
index ae86c20ef..91d951f2f 100644
--- a/deps/v8/src/code-stubs.h
+++ b/deps/v8/src/code-stubs.h
@@ -56,7 +56,6 @@ class CodeStub BASE_EMBEDDED {
GetProperty, // ARM only
SetProperty, // ARM only
InvokeBuiltin, // ARM only
- JSExit, // ARM only
RegExpCEntry, // ARM only
NUMBER_OF_IDS
};
diff --git a/deps/v8/src/codegen.cc b/deps/v8/src/codegen.cc
index a18fa0fec..096a1a191 100644
--- a/deps/v8/src/codegen.cc
+++ b/deps/v8/src/codegen.cc
@@ -125,102 +125,114 @@ void CodeGenerator::DeleteFrame() {
}
-// Generate the code. Takes a function literal, generates code for it, assemble
-// all the pieces into a Code object. This function is only to be called by
-// the compiler.cc code.
-Handle<Code> CodeGenerator::MakeCode(FunctionLiteral* flit,
- Handle<Script> script,
- bool is_eval) {
-#ifdef ENABLE_DISASSEMBLER
- bool print_code = Bootstrapper::IsActive()
- ? FLAG_print_builtin_code
- : FLAG_print_code;
-#endif
-
+void CodeGenerator::MakeCodePrologue(FunctionLiteral* fun) {
#ifdef DEBUG
bool print_source = false;
bool print_ast = false;
+ bool print_json_ast = false;
const char* ftype;
if (Bootstrapper::IsActive()) {
print_source = FLAG_print_builtin_source;
print_ast = FLAG_print_builtin_ast;
+ print_json_ast = FLAG_print_builtin_json_ast;
ftype = "builtin";
} else {
print_source = FLAG_print_source;
print_ast = FLAG_print_ast;
+ print_json_ast = FLAG_print_json_ast;
ftype = "user-defined";
}
if (FLAG_trace_codegen || print_source || print_ast) {
PrintF("*** Generate code for %s function: ", ftype);
- flit->name()->ShortPrint();
+ fun->name()->ShortPrint();
PrintF(" ***\n");
}
if (print_source) {
- PrintF("--- Source from AST ---\n%s\n", PrettyPrinter().PrintProgram(flit));
+ PrintF("--- Source from AST ---\n%s\n", PrettyPrinter().PrintProgram(fun));
}
if (print_ast) {
- PrintF("--- AST ---\n%s\n", AstPrinter().PrintProgram(flit));
+ PrintF("--- AST ---\n%s\n", AstPrinter().PrintProgram(fun));
}
-#endif // DEBUG
- // Generate code.
- const int initial_buffer_size = 4 * KB;
- CodeGenerator cgen(initial_buffer_size, script, is_eval);
- CodeGeneratorScope scope(&cgen);
- cgen.GenCode(flit);
- if (cgen.HasStackOverflow()) {
- ASSERT(!Top::has_pending_exception());
- return Handle<Code>::null();
+ if (print_json_ast) {
+ JsonAstBuilder builder;
+ PrintF("%s", builder.BuildProgram(fun));
}
+#endif // DEBUG
+}
- // Allocate and install the code. Time the rest of this function as
- // code creation.
- HistogramTimerScope timer(&Counters::code_creation);
+
+Handle<Code> CodeGenerator::MakeCodeEpilogue(FunctionLiteral* fun,
+ MacroAssembler* masm,
+ Code::Flags flags,
+ Handle<Script> script) {
+ // Allocate and install the code.
CodeDesc desc;
- cgen.masm()->GetCode(&desc);
- ZoneScopeInfo sinfo(flit->scope());
- InLoopFlag in_loop = (cgen.loop_nesting() != 0) ? IN_LOOP : NOT_IN_LOOP;
- Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, in_loop);
- Handle<Code> code = Factory::NewCode(desc,
- &sinfo,
- flags,
- cgen.masm()->CodeObject());
+ masm->GetCode(&desc);
+ ZoneScopeInfo sinfo(fun->scope());
+ Handle<Code> code =
+ Factory::NewCode(desc, &sinfo, flags, masm->CodeObject());
// Add unresolved entries in the code to the fixup list.
- Bootstrapper::AddFixup(*code, cgen.masm());
+ Bootstrapper::AddFixup(*code, masm);
#ifdef ENABLE_DISASSEMBLER
+ bool print_code = Bootstrapper::IsActive()
+ ? FLAG_print_builtin_code
+ : FLAG_print_code;
if (print_code) {
// Print the source code if available.
if (!script->IsUndefined() && !script->source()->IsUndefined()) {
PrintF("--- Raw source ---\n");
StringInputBuffer stream(String::cast(script->source()));
- stream.Seek(flit->start_position());
- // flit->end_position() points to the last character in the stream. We
+ stream.Seek(fun->start_position());
+ // fun->end_position() points to the last character in the stream. We
// need to compensate by adding one to calculate the length.
- int source_len = flit->end_position() - flit->start_position() + 1;
+ int source_len = fun->end_position() - fun->start_position() + 1;
for (int i = 0; i < source_len; i++) {
if (stream.has_more()) PrintF("%c", stream.GetNext());
}
PrintF("\n\n");
}
PrintF("--- Code ---\n");
- code->Disassemble(*flit->name()->ToCString());
+ code->Disassemble(*fun->name()->ToCString());
}
#endif // ENABLE_DISASSEMBLER
if (!code.is_null()) {
Counters::total_compiled_code_size.Increment(code->instruction_size());
}
-
return code;
}
+// Generate the code. Takes a function literal, generates code for it, assemble
+// all the pieces into a Code object. This function is only to be called by
+// the compiler.cc code.
+Handle<Code> CodeGenerator::MakeCode(FunctionLiteral* fun,
+ Handle<Script> script,
+ bool is_eval) {
+ MakeCodePrologue(fun);
+ // Generate code.
+ const int kInitialBufferSize = 4 * KB;
+ CodeGenerator cgen(kInitialBufferSize, script, is_eval);
+ CodeGeneratorScope scope(&cgen);
+ cgen.GenCode(fun);
+ if (cgen.HasStackOverflow()) {
+ ASSERT(!Top::has_pending_exception());
+ return Handle<Code>::null();
+ }
+
+ InLoopFlag in_loop = (cgen.loop_nesting() != 0) ? IN_LOOP : NOT_IN_LOOP;
+ Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, in_loop);
+ return MakeCodeEpilogue(fun, cgen.masm(), flags, script);
+}
+
+
#ifdef ENABLE_LOGGING_AND_PROFILING
bool CodeGenerator::ShouldGenerateLog(Expression* type) {
@@ -314,7 +326,6 @@ Handle<JSFunction> CodeGenerator::BuildBoilerplate(FunctionLiteral* node) {
Handle<JSFunction> function =
Factory::NewFunctionBoilerplate(node->name(),
node->materialized_literal_count(),
- node->contains_array_literal(),
code);
CodeGenerator::SetFunctionInfo(function, node, false, script_);
@@ -469,26 +480,45 @@ bool CodeGenerator::PatchInlineRuntimeEntry(Handle<String> name,
}
-static inline void RecordPositions(CodeGenerator* cgen, int pos) {
+// 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;
+}
+
+
+void CodeGenerator::RecordPositions(MacroAssembler* masm, int pos) {
if (pos != RelocInfo::kNoPosition) {
- cgen->masm()->RecordStatementPosition(pos);
- cgen->masm()->RecordPosition(pos);
+ masm->RecordStatementPosition(pos);
+ masm->RecordPosition(pos);
}
}
void CodeGenerator::CodeForFunctionPosition(FunctionLiteral* fun) {
- if (FLAG_debug_info) RecordPositions(this, fun->start_position());
+ if (FLAG_debug_info) RecordPositions(masm(), fun->start_position());
}
void CodeGenerator::CodeForReturnPosition(FunctionLiteral* fun) {
- if (FLAG_debug_info) RecordPositions(this, fun->end_position());
+ if (FLAG_debug_info) RecordPositions(masm(), fun->end_position());
}
void CodeGenerator::CodeForStatementPosition(Statement* stmt) {
- if (FLAG_debug_info) RecordPositions(this, stmt->statement_pos());
+ if (FLAG_debug_info) RecordPositions(masm(), stmt->statement_pos());
}
diff --git a/deps/v8/src/codegen.h b/deps/v8/src/codegen.h
index d03f4b60b..1209f36ec 100644
--- a/deps/v8/src/codegen.h
+++ b/deps/v8/src/codegen.h
@@ -36,6 +36,8 @@
// The contract to the shared code is that the the CodeGenerator is a subclass
// of Visitor and that the following methods are available publicly:
// MakeCode
+// MakeCodePrologue
+// MakeCodeEpilogue
// SetFunctionInfo
// masm
// frame
@@ -46,6 +48,7 @@
// AddDeferred
// in_spilled_code
// set_in_spilled_code
+// RecordPositions
//
// These methods are either used privately by the shared code or implemented as
// shared code:
@@ -61,6 +64,7 @@
// FindInlineRuntimeLUT
// CheckForInlineRuntimeCall
// PatchInlineRuntimeEntry
+// AnalyzeCondition
// CodeForFunctionPosition
// CodeForReturnPosition
// CodeForStatementPosition
diff --git a/deps/v8/src/compilation-cache.cc b/deps/v8/src/compilation-cache.cc
index 8dd9ec16b..54273673a 100644
--- a/deps/v8/src/compilation-cache.cc
+++ b/deps/v8/src/compilation-cache.cc
@@ -43,20 +43,22 @@ static const int kEvalGlobalGenerations = 1;
static const int kEvalContextualGenerations = 1;
static const int kRegExpGenerations = 1;
#else
+// The number of ScriptGenerations is carefully chosen based on histograms.
+// See issue 458: http://code.google.com/p/v8/issues/detail?id=458
static const int kScriptGenerations = 5;
static const int kEvalGlobalGenerations = 2;
static const int kEvalContextualGenerations = 2;
static const int kRegExpGenerations = 2;
#endif
-// Initial of each compilation cache table allocated.
+// Initial size of each compilation cache table allocated.
static const int kInitialCacheSize = 64;
// The compilation cache consists of several generational sub-caches which uses
// this class as a base class. A sub-cache contains a compilation cache tables
-// for each generation of the sub-cache. As the same source code string has
-// different compiled code for scripts and evals. Internally, we use separate
-// sub-caches to avoid getting the wrong kind of result when looking up.
+// for each generation of the sub-cache. Since the same source code string has
+// different compiled code for scripts and evals, we use separate sub-caches
+// for different compilation modes, to avoid retrieving the wrong result.
class CompilationSubCache {
public:
explicit CompilationSubCache(int generations): generations_(generations) {
diff --git a/deps/v8/src/compiler.cc b/deps/v8/src/compiler.cc
index 6ba7a9a9d..2e55683b2 100644
--- a/deps/v8/src/compiler.cc
+++ b/deps/v8/src/compiler.cc
@@ -32,6 +32,7 @@
#include "compilation-cache.h"
#include "compiler.h"
#include "debug.h"
+#include "fast-codegen.h"
#include "oprofile-agent.h"
#include "rewriter.h"
#include "scopes.h"
@@ -40,6 +41,29 @@
namespace v8 {
namespace internal {
+
+class CodeGenSelector: public AstVisitor {
+ public:
+ enum CodeGenTag { NORMAL, FAST };
+
+ CodeGenSelector() : has_supported_syntax_(true) {}
+
+ CodeGenTag Select(FunctionLiteral* fun);
+
+ private:
+ void VisitStatements(ZoneList<Statement*>* stmts);
+
+ // AST node visit functions.
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+ AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+ bool has_supported_syntax_;
+
+ DISALLOW_COPY_AND_ASSIGN(CodeGenSelector);
+};
+
+
static Handle<Code> MakeCode(FunctionLiteral* literal,
Handle<Script> script,
Handle<Context> context,
@@ -79,8 +103,15 @@ static Handle<Code> MakeCode(FunctionLiteral* literal,
}
// Generate code and return it.
- Handle<Code> result = CodeGenerator::MakeCode(literal, script, is_eval);
- return result;
+ if (FLAG_fast_compiler) {
+ CodeGenSelector selector;
+ CodeGenSelector::CodeGenTag code_gen = selector.Select(literal);
+ if (code_gen == CodeGenSelector::FAST) {
+ return FastCodeGenerator::MakeCode(literal, script);
+ }
+ ASSERT(code_gen == CodeGenSelector::NORMAL);
+ }
+ return CodeGenerator::MakeCode(literal, script, is_eval);
}
@@ -197,7 +228,6 @@ static Handle<JSFunction> MakeFunction(bool is_global,
Handle<JSFunction> fun =
Factory::NewFunctionBoilerplate(lit->name(),
lit->materialized_literal_count(),
- lit->contains_array_literal(),
code);
ASSERT_EQ(RelocInfo::kNoPosition, lit->function_token_position());
@@ -417,4 +447,271 @@ bool Compiler::CompileLazy(Handle<SharedFunctionInfo> shared,
}
+CodeGenSelector::CodeGenTag CodeGenSelector::Select(FunctionLiteral* fun) {
+ Scope* scope = fun->scope();
+
+ if (!scope->is_global_scope()) return NORMAL;
+ ASSERT(scope->num_heap_slots() == 0);
+ ASSERT(scope->arguments() == NULL);
+
+ if (!scope->declarations()->is_empty()) return NORMAL;
+ if (fun->materialized_literal_count() > 0) return NORMAL;
+ if (fun->body()->is_empty()) return NORMAL;
+
+ has_supported_syntax_ = true;
+ VisitStatements(fun->body());
+ return has_supported_syntax_ ? FAST : NORMAL;
+}
+
+
+#define BAILOUT(reason) \
+ do { \
+ if (FLAG_trace_bailout) { \
+ PrintF("%s\n", reason); \
+ } \
+ has_supported_syntax_ = false; \
+ return; \
+ } while (false)
+
+
+#define CHECK_BAILOUT \
+ do { \
+ if (!has_supported_syntax_) return; \
+ } while (false)
+
+
+void CodeGenSelector::VisitStatements(ZoneList<Statement*>* stmts) {
+ for (int i = 0, len = stmts->length(); i < len; i++) {
+ CHECK_BAILOUT;
+ Visit(stmts->at(i));
+ }
+}
+
+
+void CodeGenSelector::VisitDeclaration(Declaration* decl) {
+ BAILOUT("Declaration");
+}
+
+
+void CodeGenSelector::VisitBlock(Block* stmt) {
+ BAILOUT("Block");
+}
+
+
+void CodeGenSelector::VisitExpressionStatement(ExpressionStatement* stmt) {
+ Expression* expr = stmt->expression();
+ Visit(expr);
+ CHECK_BAILOUT;
+ expr->set_location(Location::Nowhere());
+}
+
+
+void CodeGenSelector::VisitEmptyStatement(EmptyStatement* stmt) {
+ BAILOUT("EmptyStatement");
+}
+
+
+void CodeGenSelector::VisitIfStatement(IfStatement* stmt) {
+ BAILOUT("IfStatement");
+}
+
+
+void CodeGenSelector::VisitContinueStatement(ContinueStatement* stmt) {
+ BAILOUT("ContinueStatement");
+}
+
+
+void CodeGenSelector::VisitBreakStatement(BreakStatement* stmt) {
+ BAILOUT("BreakStatement");
+}
+
+
+void CodeGenSelector::VisitReturnStatement(ReturnStatement* stmt) {
+ Visit(stmt->expression());
+}
+
+
+void CodeGenSelector::VisitWithEnterStatement(WithEnterStatement* stmt) {
+ BAILOUT("WithEnterStatement");
+}
+
+
+void CodeGenSelector::VisitWithExitStatement(WithExitStatement* stmt) {
+ BAILOUT("WithExitStatement");
+}
+
+
+void CodeGenSelector::VisitSwitchStatement(SwitchStatement* stmt) {
+ BAILOUT("SwitchStatement");
+}
+
+
+void CodeGenSelector::VisitDoWhileStatement(DoWhileStatement* stmt) {
+ BAILOUT("DoWhileStatement");
+}
+
+
+void CodeGenSelector::VisitWhileStatement(WhileStatement* stmt) {
+ BAILOUT("WhileStatement");
+}
+
+
+void CodeGenSelector::VisitForStatement(ForStatement* stmt) {
+ BAILOUT("ForStatement");
+}
+
+
+void CodeGenSelector::VisitForInStatement(ForInStatement* stmt) {
+ BAILOUT("ForInStatement");
+}
+
+
+void CodeGenSelector::VisitTryCatchStatement(TryCatchStatement* stmt) {
+ BAILOUT("TryCatchStatement");
+}
+
+
+void CodeGenSelector::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
+ BAILOUT("TryFinallyStatement");
+}
+
+
+void CodeGenSelector::VisitDebuggerStatement(DebuggerStatement* stmt) {
+ BAILOUT("DebuggerStatement");
+}
+
+
+void CodeGenSelector::VisitFunctionLiteral(FunctionLiteral* expr) {
+ BAILOUT("FunctionLiteral");
+}
+
+
+void CodeGenSelector::VisitFunctionBoilerplateLiteral(
+ FunctionBoilerplateLiteral* expr) {
+ BAILOUT("FunctionBoilerplateLiteral");
+}
+
+
+void CodeGenSelector::VisitConditional(Conditional* expr) {
+ BAILOUT("Conditional");
+}
+
+
+void CodeGenSelector::VisitSlot(Slot* expr) {
+ Slot::Type type = expr->type();
+ if (type != Slot::PARAMETER && type != Slot::LOCAL) {
+ BAILOUT("non-parameter/non-local slot reference");
+ }
+}
+
+
+void CodeGenSelector::VisitVariableProxy(VariableProxy* expr) {
+ Expression* rewrite = expr->var()->rewrite();
+ if (rewrite == NULL) BAILOUT("global variable reference");
+ Visit(rewrite);
+}
+
+
+void CodeGenSelector::VisitLiteral(Literal* expr) {
+ // All literals are supported.
+}
+
+
+void CodeGenSelector::VisitRegExpLiteral(RegExpLiteral* expr) {
+ BAILOUT("RegExpLiteral");
+}
+
+
+void CodeGenSelector::VisitObjectLiteral(ObjectLiteral* expr) {
+ BAILOUT("ObjectLiteral");
+}
+
+
+void CodeGenSelector::VisitArrayLiteral(ArrayLiteral* expr) {
+ BAILOUT("ArrayLiteral");
+}
+
+
+void CodeGenSelector::VisitCatchExtensionObject(CatchExtensionObject* expr) {
+ BAILOUT("CatchExtensionObject");
+}
+
+
+void CodeGenSelector::VisitAssignment(Assignment* expr) {
+ // We support plain non-compound assignments to parameters and
+ // non-context (stack-allocated) locals.
+ if (expr->starts_initialization_block()) BAILOUT("initialization block");
+
+ Token::Value op = expr->op();
+ if (op == Token::INIT_CONST) BAILOUT("initialize constant");
+ if (op != Token::ASSIGN && op != Token::INIT_VAR) {
+ BAILOUT("compound assignment");
+ }
+
+ Variable* var = expr->target()->AsVariableProxy()->AsVariable();
+ if (var == NULL || var->is_global()) BAILOUT("non-variable assignment");
+
+ ASSERT(var->slot() != NULL);
+ Slot::Type type = var->slot()->type();
+ if (type != Slot::PARAMETER && type != Slot::LOCAL) {
+ BAILOUT("non-parameter/non-local slot assignment");
+ }
+
+ Visit(expr->value());
+}
+
+
+void CodeGenSelector::VisitThrow(Throw* expr) {
+ BAILOUT("Throw");
+}
+
+
+void CodeGenSelector::VisitProperty(Property* expr) {
+ BAILOUT("Property");
+}
+
+
+void CodeGenSelector::VisitCall(Call* expr) {
+ BAILOUT("Call");
+}
+
+
+void CodeGenSelector::VisitCallNew(CallNew* expr) {
+ BAILOUT("CallNew");
+}
+
+
+void CodeGenSelector::VisitCallRuntime(CallRuntime* expr) {
+ BAILOUT("CallRuntime");
+}
+
+
+void CodeGenSelector::VisitUnaryOperation(UnaryOperation* expr) {
+ BAILOUT("UnaryOperation");
+}
+
+
+void CodeGenSelector::VisitCountOperation(CountOperation* expr) {
+ BAILOUT("CountOperation");
+}
+
+
+void CodeGenSelector::VisitBinaryOperation(BinaryOperation* expr) {
+ BAILOUT("BinaryOperation");
+}
+
+
+void CodeGenSelector::VisitCompareOperation(CompareOperation* expr) {
+ BAILOUT("CompareOperation");
+}
+
+
+void CodeGenSelector::VisitThisFunction(ThisFunction* expr) {
+ BAILOUT("ThisFunction");
+}
+
+#undef BAILOUT
+#undef CHECK_BAILOUT
+
+
} } // namespace v8::internal
diff --git a/deps/v8/src/d8-posix.cc b/deps/v8/src/d8-posix.cc
index fe130cef7..2535ce05e 100644
--- a/deps/v8/src/d8-posix.cc
+++ b/deps/v8/src/d8-posix.cc
@@ -311,7 +311,7 @@ static Handle<Value> GetStdout(int child_fd,
int read_timeout,
int total_timeout) {
Handle<String> accumulator = String::Empty();
- const char* source = "function(a, b) { return a + b; }";
+ const char* source = "(function(a, b) { return a + b; })";
Handle<Value> cons_as_obj(Script::Compile(String::New(source))->Run());
Handle<Function> cons_function(Function::Cast(*cons_as_obj));
Handle<Value> cons_args[2];
diff --git a/deps/v8/src/d8.js b/deps/v8/src/d8.js
index 14b50603c..be4a0519c 100644
--- a/deps/v8/src/d8.js
+++ b/deps/v8/src/d8.js
@@ -130,7 +130,7 @@ function DebugMessageDetails(message) {
}
function DebugEventDetails(response) {
- details = {text:'', running:false};
+ details = {text:'', running:false}
// Get the running state.
details.running = response.running();
diff --git a/deps/v8/src/dateparser-inl.h b/deps/v8/src/dateparser-inl.h
index 3d4161d19..d5921d568 100644
--- a/deps/v8/src/dateparser-inl.h
+++ b/deps/v8/src/dateparser-inl.h
@@ -28,6 +28,8 @@
#ifndef V8_DATEPARSER_INL_H_
#define V8_DATEPARSER_INL_H_
+#include "dateparser.h"
+
namespace v8 {
namespace internal {
diff --git a/deps/v8/src/debug-delay.js b/deps/v8/src/debug-delay.js
index cb789beb9..d9447bd27 100644
--- a/deps/v8/src/debug-delay.js
+++ b/deps/v8/src/debug-delay.js
@@ -795,8 +795,8 @@ ExecutionState.prototype.selectedFrame = function() {
return this.selected_frame;
};
-ExecutionState.prototype.debugCommandProcessor = function(protocol) {
- return new DebugCommandProcessor(this, protocol);
+ExecutionState.prototype.debugCommandProcessor = function(opt_is_running) {
+ return new DebugCommandProcessor(this, opt_is_running);
};
@@ -1081,9 +1081,9 @@ function MakeScriptObject_(script, include_source) {
};
-function DebugCommandProcessor(exec_state) {
+function DebugCommandProcessor(exec_state, opt_is_running) {
this.exec_state_ = exec_state;
- this.running_ = false;
+ this.running_ = opt_is_running || false;
};
@@ -1107,7 +1107,8 @@ function ProtocolMessage(request) {
this.type = 'event';
}
this.success = true;
- this.running = false;
+ // Handler may set this field to control debugger state.
+ this.running = undefined;
}
@@ -1168,11 +1169,7 @@ ProtocolMessage.prototype.toJSONProtocol = function() {
if (this.message) {
json.message = this.message;
}
- if (this.running) {
- json.running = true;
- } else {
- json.running = false;
- }
+ json.running = this.running;
return JSON.stringify(json);
}
@@ -1244,6 +1241,8 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request)
this.scriptsRequest_(request, response);
} else if (request.command == 'threads') {
this.threadsRequest_(request, response);
+ } else if (request.command == 'suspend') {
+ this.suspendRequest_(request, response);
} else {
throw new Error('Unknown command "' + request.command + '" in request');
}
@@ -1258,7 +1257,11 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request)
// Return the response as a JSON encoded string.
try {
- this.running_ = response.running; // Store the running state.
+ if (!IS_UNDEFINED(response.running)) {
+ // Response controls running state.
+ this.running_ = response.running;
+ }
+ response.running = this.running_;
return response.toJSONProtocol();
} catch (e) {
// Failed to generate response - return generic error.
@@ -1907,6 +1910,12 @@ DebugCommandProcessor.prototype.threadsRequest_ = function(request, response) {
};
+DebugCommandProcessor.prototype.suspendRequest_ = function(request, response) {
+ // TODO(peter.rybin): probably we need some body field here.
+ response.running = false;
+};
+
+
// Check whether the previously processed command caused the VM to become
// running.
DebugCommandProcessor.prototype.isRunning = function() {
diff --git a/deps/v8/src/debug.cc b/deps/v8/src/debug.cc
index c82b53abb..775b09aef 100644
--- a/deps/v8/src/debug.cc
+++ b/deps/v8/src/debug.cc
@@ -1614,7 +1614,7 @@ void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) {
if (RelocInfo::IsJSReturn(it.rinfo()->rmode())) {
at_js_return = (it.rinfo()->pc() ==
addr - Assembler::kPatchReturnSequenceAddressOffset);
- break_at_js_return_active = it.rinfo()->IsCallInstruction();
+ break_at_js_return_active = it.rinfo()->IsPatchedReturnSequence();
}
it.next();
}
@@ -2214,21 +2214,31 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event,
return;
}
- // Get the DebugCommandProcessor.
- v8::Local<v8::Object> api_exec_state =
- v8::Utils::ToLocal(Handle<JSObject>::cast(exec_state));
- v8::Local<v8::String> fun_name =
- v8::String::New("debugCommandProcessor");
- v8::Local<v8::Function> fun =
- v8::Function::Cast(*api_exec_state->Get(fun_name));
v8::TryCatch try_catch;
- v8::Local<v8::Object> cmd_processor =
- v8::Object::Cast(*fun->Call(api_exec_state, 0, NULL));
- if (try_catch.HasCaught()) {
- PrintLn(try_catch.Exception());
- return;
+
+ // DebugCommandProcessor goes here.
+ v8::Local<v8::Object> cmd_processor;
+ {
+ v8::Local<v8::Object> api_exec_state =
+ v8::Utils::ToLocal(Handle<JSObject>::cast(exec_state));
+ v8::Local<v8::String> fun_name =
+ v8::String::New("debugCommandProcessor");
+ v8::Local<v8::Function> fun =
+ v8::Function::Cast(*api_exec_state->Get(fun_name));
+
+ v8::Handle<v8::Boolean> running =
+ auto_continue ? v8::True() : v8::False();
+ static const int kArgc = 1;
+ v8::Handle<Value> argv[kArgc] = { running };
+ cmd_processor = v8::Object::Cast(*fun->Call(api_exec_state, kArgc, argv));
+ if (try_catch.HasCaught()) {
+ PrintLn(try_catch.Exception());
+ return;
+ }
}
+ bool running = auto_continue;
+
// Process requests from the debugger.
while (true) {
// Wait for new command in the queue.
@@ -2269,7 +2279,6 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event,
// Get the response.
v8::Local<v8::String> response;
- bool running = false;
if (!try_catch.HasCaught()) {
// Get response string.
if (!response_val->IsUndefined()) {
@@ -2312,7 +2321,7 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event,
// Return from debug event processing if either the VM is put into the
// runnning state (through a continue command) or auto continue is active
// and there are no more commands queued.
- if (running || (auto_continue && !HasCommands())) {
+ if (running && !HasCommands()) {
return;
}
}
diff --git a/deps/v8/src/execution.cc b/deps/v8/src/execution.cc
index 8bc6b74e1..229b8df97 100644
--- a/deps/v8/src/execution.cc
+++ b/deps/v8/src/execution.cc
@@ -96,8 +96,11 @@ static Handle<Object> Invoke(bool construct,
JSEntryFunction entry = FUNCTION_CAST<JSEntryFunction>(code->entry());
// Call the function through the right JS entry stub.
- value = CALL_GENERATED_CODE(entry, func->code()->entry(), *func,
- *receiver, argc, args);
+ byte* entry_address= func->code()->entry();
+ JSFunction* function = *func;
+ Object* receiver_pointer = *receiver;
+ value = CALL_GENERATED_CODE(entry, entry_address, function,
+ receiver_pointer, argc, args);
}
#ifdef DEBUG
@@ -383,7 +386,8 @@ void StackGuard::ThreadLocal::Initialize() {
if (initial_climit_ == kIllegalLimit) {
// Takes the address of the limit variable in order to find out where
// the top of stack is right now.
- intptr_t limit = reinterpret_cast<intptr_t>(&limit) - kLimitSize;
+ uintptr_t limit = reinterpret_cast<uintptr_t>(&limit) - kLimitSize;
+ ASSERT(reinterpret_cast<uintptr_t>(&limit) > kLimitSize);
initial_jslimit_ = SimulatorStack::JsLimitFromCLimit(limit);
jslimit_ = SimulatorStack::JsLimitFromCLimit(limit);
initial_climit_ = limit;
diff --git a/deps/v8/src/execution.h b/deps/v8/src/execution.h
index 55307f71f..ac00aa46f 100644
--- a/deps/v8/src/execution.h
+++ b/deps/v8/src/execution.h
@@ -216,6 +216,7 @@ class StackGuard : public AllStatic {
static void DisableInterrupts();
static const uintptr_t kLimitSize = kPointerSize * 128 * KB;
+
#ifdef V8_TARGET_ARCH_X64
static const uintptr_t kInterruptLimit = V8_UINT64_C(0xfffffffffffffffe);
static const uintptr_t kIllegalLimit = V8_UINT64_C(0xfffffffffffffff8);
diff --git a/deps/v8/src/factory.cc b/deps/v8/src/factory.cc
index 622055c30..5251e344b 100644
--- a/deps/v8/src/factory.cc
+++ b/deps/v8/src/factory.cc
@@ -477,7 +477,6 @@ Handle<JSFunction> Factory::NewFunction(Handle<String> name,
Handle<JSFunction> Factory::NewFunctionBoilerplate(Handle<String> name,
int number_of_literals,
- bool contains_array_literal,
Handle<Code> code) {
Handle<JSFunction> function = NewFunctionBoilerplate(name);
function->set_code(*code);
@@ -485,7 +484,7 @@ Handle<JSFunction> Factory::NewFunctionBoilerplate(Handle<String> name,
// If the function contains object, regexp or array literals,
// allocate extra space for a literals array prefix containing the
// object, regexp and array constructor functions.
- if (number_of_literals > 0 || contains_array_literal) {
+ if (number_of_literals > 0) {
literals_array_size += JSFunction::kLiteralsPrefixSize;
}
Handle<FixedArray> literals =
diff --git a/deps/v8/src/factory.h b/deps/v8/src/factory.h
index 0596fbf00..7223f081e 100644
--- a/deps/v8/src/factory.h
+++ b/deps/v8/src/factory.h
@@ -264,7 +264,6 @@ class Factory : public AllStatic {
static Handle<JSFunction> NewFunctionBoilerplate(Handle<String> name,
int number_of_literals,
- bool contains_array_literal,
Handle<Code> code);
static Handle<JSFunction> NewFunctionBoilerplate(Handle<String> name);
diff --git a/deps/v8/src/fast-codegen.cc b/deps/v8/src/fast-codegen.cc
new file mode 100644
index 000000000..4ec6a524f
--- /dev/null
+++ b/deps/v8/src/fast-codegen.cc
@@ -0,0 +1,269 @@
+// 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 "fast-codegen.h"
+
+namespace v8 {
+namespace internal {
+
+Handle<Code> FastCodeGenerator::MakeCode(FunctionLiteral* fun,
+ Handle<Script> script) {
+ CodeGenerator::MakeCodePrologue(fun);
+ const int kInitialBufferSize = 4 * KB;
+ MacroAssembler masm(NULL, kInitialBufferSize);
+ FastCodeGenerator cgen(&masm);
+ cgen.Generate(fun);
+ if (cgen.HasStackOverflow()) {
+ ASSERT(!Top::has_pending_exception());
+ return Handle<Code>::null();
+ }
+ Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, NOT_IN_LOOP);
+ return CodeGenerator::MakeCodeEpilogue(fun, &masm, flags, script);
+}
+
+
+int FastCodeGenerator::SlotOffset(Slot* slot) {
+ // Offset is negative because higher indexes are at lower addresses.
+ int offset = -slot->index() * kPointerSize;
+ // Adjust by a (parameter or local) base offset.
+ switch (slot->type()) {
+ case Slot::PARAMETER:
+ offset += (function_->scope()->num_parameters() + 1) * kPointerSize;
+ break;
+ case Slot::LOCAL:
+ offset += JavaScriptFrameConstants::kLocal0Offset;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return offset;
+}
+
+void FastCodeGenerator::SetFunctionPosition(FunctionLiteral* fun) {
+ if (FLAG_debug_info) {
+ CodeGenerator::RecordPositions(masm_, fun->start_position());
+ }
+}
+
+
+void FastCodeGenerator::SetReturnPosition(FunctionLiteral* fun) {
+ if (FLAG_debug_info) {
+ CodeGenerator::RecordPositions(masm_, fun->end_position());
+ }
+}
+
+
+void FastCodeGenerator::SetStatementPosition(Statement* stmt) {
+ if (FLAG_debug_info) {
+ CodeGenerator::RecordPositions(masm_, stmt->statement_pos());
+ }
+}
+
+
+void FastCodeGenerator::SetSourcePosition(int pos) {
+ if (FLAG_debug_info && pos != RelocInfo::kNoPosition) {
+ masm_->RecordPosition(pos);
+ }
+}
+
+
+void FastCodeGenerator::VisitDeclaration(Declaration* decl) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitBlock(Block* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitEmptyStatement(EmptyStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitIfStatement(IfStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitContinueStatement(ContinueStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitWithEnterStatement(WithEnterStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitWithExitStatement(WithExitStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitForStatement(ForStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitFunctionBoilerplateLiteral(
+ FunctionBoilerplateLiteral* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitConditional(Conditional* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitSlot(Slot* expr) {
+ // Slots do not appear directly in the AST.
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitThrow(Throw* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitProperty(Property* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitCall(Call* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitCallNew(CallNew* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitCountOperation(CountOperation* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) {
+ UNREACHABLE();
+}
+
+
+} } // namespace v8::internal
diff --git a/deps/v8/src/fast-codegen.h b/deps/v8/src/fast-codegen.h
new file mode 100644
index 000000000..e6bb6436a
--- /dev/null
+++ b/deps/v8/src/fast-codegen.h
@@ -0,0 +1,71 @@
+// 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_FAST_CODEGEN_H_
+#define V8_FAST_CODEGEN_H_
+
+#include "v8.h"
+
+#include "ast.h"
+
+namespace v8 {
+namespace internal {
+
+
+class FastCodeGenerator: public AstVisitor {
+ public:
+ explicit FastCodeGenerator(MacroAssembler* masm)
+ : masm_(masm), function_(NULL) {
+ }
+
+ static Handle<Code> MakeCode(FunctionLiteral* fun, Handle<Script> script);
+
+ void Generate(FunctionLiteral* fun);
+
+ private:
+ int SlotOffset(Slot* slot);
+
+ void SetFunctionPosition(FunctionLiteral* fun);
+ void SetReturnPosition(FunctionLiteral* fun);
+ void SetStatementPosition(Statement* stmt);
+ void SetSourcePosition(int pos);
+
+ // AST node visit functions.
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+ AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+ MacroAssembler* masm_;
+ FunctionLiteral* function_;
+
+ DISALLOW_COPY_AND_ASSIGN(FastCodeGenerator);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_FAST_CODEGEN_H_
diff --git a/deps/v8/src/flag-definitions.h b/deps/v8/src/flag-definitions.h
index 91c5bcada..2a964abd0 100644
--- a/deps/v8/src/flag-definitions.h
+++ b/deps/v8/src/flag-definitions.h
@@ -96,7 +96,7 @@ private:
//
#define FLAG FLAG_FULL
-// assembler-ia32.cc / assembler-arm.cc
+// assembler-ia32.cc / assembler-arm.cc / assembler-x64.cc
DEFINE_bool(debug_code, false,
"generate extra code (comments, assertions) for debugging")
DEFINE_bool(emit_branch_hints, false, "emit branch hints")
@@ -104,6 +104,16 @@ DEFINE_bool(push_pop_elimination, true,
"eliminate redundant push/pops in assembly code")
DEFINE_bool(print_push_pop_elimination, false,
"print elimination of redundant push/pops in assembly code")
+DEFINE_bool(enable_sse2, true,
+ "enable use of SSE2 instructions if available")
+DEFINE_bool(enable_sse3, true,
+ "enable use of SSE3 instructions if available")
+DEFINE_bool(enable_cmov, true,
+ "enable use of CMOV instruction if available")
+DEFINE_bool(enable_rdtsc, true,
+ "enable use of RDTSC instruction if available")
+DEFINE_bool(enable_sahf, true,
+ "enable use of SAHF instruction if available (X64 only)")
// bootstrapper.cc
DEFINE_string(expose_natives_as, NULL, "expose natives in global object")
@@ -132,7 +142,11 @@ DEFINE_bool(debug_info, true, "add debug information to compiled functions")
// compiler.cc
DEFINE_bool(strict, false, "strict error checking")
DEFINE_int(min_preparse_length, 1024,
- "Minimum length for automatic enable preparsing")
+ "minimum length for automatic enable preparsing")
+DEFINE_bool(fast_compiler, true,
+ "use the fast-mode compiler for some top-level code")
+DEFINE_bool(trace_bailout, false,
+ "print reasons for failing to use fast compilation")
// compilation-cache.cc
DEFINE_bool(compilation_cache, true, "enable compilation cache")
@@ -263,6 +277,9 @@ DEFINE_bool(print_builtin_source, false,
"pretty print source code for builtins")
DEFINE_bool(print_ast, false, "print source AST")
DEFINE_bool(print_builtin_ast, false, "print source AST for builtins")
+DEFINE_bool(print_json_ast, false, "print source AST as JSON")
+DEFINE_bool(print_builtin_json_ast, false,
+ "print source AST for builtins as JSON")
DEFINE_bool(trace_calls, false, "trace calls")
DEFINE_bool(trace_builtin_calls, false, "trace builtins calls")
DEFINE_string(stop_at, "", "function name where to insert a breakpoint")
@@ -333,6 +350,7 @@ DEFINE_bool(log_gc, false,
DEFINE_bool(log_handles, false, "Log global handle events.")
DEFINE_bool(log_state_changes, false, "Log state changes.")
DEFINE_bool(log_suspect, false, "Log suspect operations.")
+DEFINE_bool(log_producers, false, "Log stack traces of JS objects allocations.")
DEFINE_bool(compress_log, false,
"Compress log to save space (makes log less human-readable).")
DEFINE_bool(prof, false,
diff --git a/deps/v8/src/global-handles.cc b/deps/v8/src/global-handles.cc
index e51c4aadf..f4b69fcdd 100644
--- a/deps/v8/src/global-handles.cc
+++ b/deps/v8/src/global-handles.cc
@@ -264,6 +264,16 @@ void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
}
+void GlobalHandles::IterateWeakRoots(WeakReferenceGuest f,
+ WeakReferenceCallback callback) {
+ for (Node* current = head_; current != NULL; current = current->next()) {
+ if (current->IsWeak() && current->callback() == callback) {
+ f(current->object_, current->parameter());
+ }
+ }
+}
+
+
void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
for (Node* current = head_; current != NULL; current = current->next()) {
if (current->state_ == Node::WEAK) {
diff --git a/deps/v8/src/global-handles.h b/deps/v8/src/global-handles.h
index 9e63ba7a9..feb95bf2a 100644
--- a/deps/v8/src/global-handles.h
+++ b/deps/v8/src/global-handles.h
@@ -54,6 +54,8 @@ class ObjectGroup : public Malloced {
};
+typedef void (*WeakReferenceGuest)(Object* object, void* parameter);
+
class GlobalHandles : public AllStatic {
public:
// Creates a new global handle that is alive until Destroy is called.
@@ -99,6 +101,10 @@ class GlobalHandles : public AllStatic {
// Iterates over all weak roots in heap.
static void IterateWeakRoots(ObjectVisitor* v);
+ // Iterates over weak roots that are bound to a given callback.
+ static void IterateWeakRoots(WeakReferenceGuest f,
+ WeakReferenceCallback callback);
+
// Find all weak handles satisfying the callback predicate, mark
// them as pending.
static void IdentifyWeakHandles(WeakSlotCallback f);
diff --git a/deps/v8/src/heap-profiler.cc b/deps/v8/src/heap-profiler.cc
index ecb691987..8f55ce1ce 100644
--- a/deps/v8/src/heap-profiler.cc
+++ b/deps/v8/src/heap-profiler.cc
@@ -28,6 +28,8 @@
#include "v8.h"
#include "heap-profiler.h"
+#include "frames-inl.h"
+#include "global-handles.h"
#include "string-stream.h"
namespace v8 {
@@ -327,6 +329,11 @@ void ConstructorHeapProfile::PrintStats() {
}
+static const char* GetConstructorName(const char* name) {
+ return name[0] != '\0' ? name : "(anonymous)";
+}
+
+
void JSObjectsCluster::Print(StringStream* accumulator) const {
ASSERT(!is_null());
if (constructor_ == FromSpecialCase(ROOTS)) {
@@ -338,7 +345,7 @@ void JSObjectsCluster::Print(StringStream* accumulator) const {
} else {
SmartPointer<char> s_name(
constructor_->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL));
- accumulator->Add("%s", (*s_name)[0] != '\0' ? *s_name : "(anonymous)");
+ accumulator->Add("%s", GetConstructorName(*s_name));
if (instance_ != NULL) {
accumulator->Add(":%p", static_cast<void*>(instance_));
}
@@ -574,6 +581,23 @@ void HeapProfiler::CollectStats(HeapObject* obj, HistogramInfo* info) {
}
+static void StackWeakReferenceCallback(Persistent<Value> object,
+ void* trace) {
+ DeleteArray(static_cast<Address*>(trace));
+ object.Dispose();
+}
+
+
+static void PrintProducerStackTrace(Object* obj, void* trace) {
+ if (!obj->IsJSObject()) return;
+ String* constructor = JSObject::cast(obj)->constructor_name();
+ SmartPointer<char> s_name(
+ constructor->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL));
+ LOG(HeapSampleJSProducerEvent(GetConstructorName(*s_name),
+ reinterpret_cast<Address*>(trace)));
+}
+
+
void HeapProfiler::WriteSample() {
LOG(HeapSampleBeginEvent("Heap", "allocated"));
LOG(HeapSampleStats(
@@ -616,10 +640,40 @@ void HeapProfiler::WriteSample() {
js_cons_profile.PrintStats();
js_retainer_profile.PrintStats();
+ GlobalHandles::IterateWeakRoots(PrintProducerStackTrace,
+ StackWeakReferenceCallback);
+
LOG(HeapSampleEndEvent("Heap", "allocated"));
}
+bool ProducerHeapProfile::can_log_ = false;
+
+void ProducerHeapProfile::Setup() {
+ can_log_ = true;
+}
+
+void ProducerHeapProfile::RecordJSObjectAllocation(Object* obj) {
+ if (!can_log_ || !FLAG_log_producers) return;
+ int framesCount = 0;
+ for (JavaScriptFrameIterator it; !it.done(); it.Advance()) {
+ ++framesCount;
+ }
+ if (framesCount == 0) return;
+ ++framesCount; // Reserve place for the terminator item.
+ Vector<Address> stack(NewArray<Address>(framesCount), framesCount);
+ int i = 0;
+ for (JavaScriptFrameIterator it; !it.done(); it.Advance()) {
+ stack[i++] = it.frame()->pc();
+ }
+ stack[i] = NULL;
+ Handle<Object> handle = GlobalHandles::Create(obj);
+ GlobalHandles::MakeWeak(handle.location(),
+ static_cast<void*>(stack.start()),
+ StackWeakReferenceCallback);
+}
+
+
#endif // ENABLE_LOGGING_AND_PROFILING
diff --git a/deps/v8/src/heap-profiler.h b/deps/v8/src/heap-profiler.h
index 7fda883f8..bd875df23 100644
--- a/deps/v8/src/heap-profiler.h
+++ b/deps/v8/src/heap-profiler.h
@@ -256,6 +256,14 @@ class RetainerHeapProfile BASE_EMBEDDED {
};
+class ProducerHeapProfile : public AllStatic {
+ public:
+ static void Setup();
+ static void RecordJSObjectAllocation(Object* obj);
+ private:
+ static bool can_log_;
+};
+
#endif // ENABLE_LOGGING_AND_PROFILING
} } // namespace v8::internal
diff --git a/deps/v8/src/heap.cc b/deps/v8/src/heap.cc
index 0295a99c3..817a50f16 100644
--- a/deps/v8/src/heap.cc
+++ b/deps/v8/src/heap.cc
@@ -1679,8 +1679,8 @@ Object* Heap::AllocateConsString(String* first, String* second) {
&& second->IsAsciiRepresentation();
// Make sure that an out of memory exception is thrown if the length
- // of the new cons string is too large to fit in a Smi.
- if (length > Smi::kMaxValue || length < -0) {
+ // of the new cons string is too large.
+ if (length > String::kMaxLength || length < 0) {
Top::context()->mark_out_of_memory();
return Failure::OutOfMemoryException();
}
@@ -2021,6 +2021,7 @@ Object* Heap::Allocate(Map* map, AllocationSpace space) {
TargetSpaceId(map->instance_type()));
if (result->IsFailure()) return result;
HeapObject::cast(result)->set_map(map);
+ ProducerHeapProfile::RecordJSObjectAllocation(result);
return result;
}
@@ -2342,6 +2343,7 @@ Object* Heap::CopyJSObject(JSObject* source) {
JSObject::cast(clone)->set_properties(FixedArray::cast(prop));
}
// Return the new clone.
+ ProducerHeapProfile::RecordJSObjectAllocation(clone);
return clone;
}
@@ -3308,6 +3310,9 @@ bool Heap::Setup(bool create_heap_objects) {
LOG(IntEvent("heap-capacity", Capacity()));
LOG(IntEvent("heap-available", Available()));
+ // This should be called only after initial objects have been created.
+ ProducerHeapProfile::Setup();
+
return true;
}
diff --git a/deps/v8/src/ia32/assembler-ia32-inl.h b/deps/v8/src/ia32/assembler-ia32-inl.h
index 1de20f4e3..5fa75ec8d 100644
--- a/deps/v8/src/ia32/assembler-ia32-inl.h
+++ b/deps/v8/src/ia32/assembler-ia32-inl.h
@@ -52,7 +52,7 @@ void RelocInfo::apply(intptr_t delta) {
if (rmode_ == RUNTIME_ENTRY || IsCodeTarget(rmode_)) {
int32_t* p = reinterpret_cast<int32_t*>(pc_);
*p -= delta; // relocate entry
- } else if (rmode_ == JS_RETURN && IsCallInstruction()) {
+ } else if (rmode_ == JS_RETURN && IsPatchedReturnSequence()) {
// Special handling of js_return when a break point is set (call
// instruction has been inserted).
int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1);
@@ -114,36 +114,36 @@ Address* RelocInfo::target_reference_address() {
Address RelocInfo::call_address() {
- ASSERT(IsCallInstruction());
+ ASSERT(IsPatchedReturnSequence());
return Assembler::target_address_at(pc_ + 1);
}
void RelocInfo::set_call_address(Address target) {
- ASSERT(IsCallInstruction());
+ ASSERT(IsPatchedReturnSequence());
Assembler::set_target_address_at(pc_ + 1, target);
}
Object* RelocInfo::call_object() {
- ASSERT(IsCallInstruction());
+ ASSERT(IsPatchedReturnSequence());
return *call_object_address();
}
Object** RelocInfo::call_object_address() {
- ASSERT(IsCallInstruction());
+ ASSERT(IsPatchedReturnSequence());
return reinterpret_cast<Object**>(pc_ + 1);
}
void RelocInfo::set_call_object(Object* target) {
- ASSERT(IsCallInstruction());
+ ASSERT(IsPatchedReturnSequence());
*call_object_address() = target;
}
-bool RelocInfo::IsCallInstruction() {
+bool RelocInfo::IsPatchedReturnSequence() {
return *pc_ == 0xE8;
}
diff --git a/deps/v8/src/ia32/assembler-ia32.cc b/deps/v8/src/ia32/assembler-ia32.cc
index b8dda17b6..bc28710f9 100644
--- a/deps/v8/src/ia32/assembler-ia32.cc
+++ b/deps/v8/src/ia32/assembler-ia32.cc
@@ -1166,6 +1166,19 @@ void Assembler::shr_cl(Register dst) {
}
+void Assembler::subb(const Operand& op, int8_t imm8) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ if (op.is_reg(eax)) {
+ EMIT(0x2c);
+ } else {
+ EMIT(0x80);
+ emit_operand(ebp, op); // ebp == 5
+ }
+ EMIT(imm8);
+}
+
+
void Assembler::sub(const Operand& dst, const Immediate& x) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
diff --git a/deps/v8/src/ia32/assembler-ia32.h b/deps/v8/src/ia32/assembler-ia32.h
index 610017bef..4719f2dcf 100644
--- a/deps/v8/src/ia32/assembler-ia32.h
+++ b/deps/v8/src/ia32/assembler-ia32.h
@@ -367,6 +367,10 @@ class CpuFeatures : public AllStatic {
static void Probe();
// Check whether a feature is supported by the target CPU.
static bool IsSupported(Feature f) {
+ if (f == SSE2 && !FLAG_enable_sse2) return false;
+ if (f == SSE3 && !FLAG_enable_sse3) return false;
+ if (f == CMOV && !FLAG_enable_cmov) return false;
+ if (f == RDTSC && !FLAG_enable_rdtsc) return false;
return (supported_ & (static_cast<uint64_t>(1) << f)) != 0;
}
// Check whether a feature is currently enabled.
@@ -590,6 +594,7 @@ class Assembler : public Malloced {
void shr(Register dst);
void shr_cl(Register dst);
+ void subb(const Operand& dst, int8_t imm8);
void sub(const Operand& dst, const Immediate& x);
void sub(Register dst, const Operand& src);
void sub(const Operand& dst, Register src);
diff --git a/deps/v8/src/ia32/codegen-ia32.cc b/deps/v8/src/ia32/codegen-ia32.cc
index 0e314b9fc..938c8e6fc 100644
--- a/deps/v8/src/ia32/codegen-ia32.cc
+++ b/deps/v8/src/ia32/codegen-ia32.cc
@@ -824,10 +824,8 @@ class DeferredInlineBinaryOperation: public DeferredCode {
void DeferredInlineBinaryOperation::Generate() {
- __ push(left_);
- __ push(right_);
- GenericBinaryOpStub stub(op_, mode_, SMI_CODE_INLINED);
- __ CallStub(&stub);
+ GenericBinaryOpStub stub(op_, mode_, NO_SMI_CODE_IN_STUB);
+ stub.GenerateCall(masm_, left_, right_);
if (!dst_.is(eax)) __ mov(dst_, eax);
}
@@ -856,16 +854,16 @@ void CodeGenerator::GenericBinaryOperation(Token::Value op,
// 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.
flags = (loop_nesting() > 0)
- ? SMI_CODE_INLINED
- : SMI_CODE_IN_STUB;
+ ? NO_SMI_CODE_IN_STUB
+ : NO_GENERIC_BINARY_FLAGS;
break;
default:
// By default only inline the Smi check code for likely smis if this
// operation is part of a loop.
flags = ((loop_nesting() > 0) && type->IsLikelySmi())
- ? SMI_CODE_INLINED
- : SMI_CODE_IN_STUB;
+ ? NO_SMI_CODE_IN_STUB
+ : NO_GENERIC_BINARY_FLAGS;
break;
}
@@ -924,16 +922,15 @@ void CodeGenerator::GenericBinaryOperation(Token::Value op,
return;
}
- if (flags == SMI_CODE_INLINED && !generate_no_smi_code) {
+ if (((flags & NO_SMI_CODE_IN_STUB) != 0) && !generate_no_smi_code) {
LikelySmiBinaryOperation(op, &left, &right, overwrite_mode);
} else {
frame_->Push(&left);
frame_->Push(&right);
// If we know the arguments aren't smis, use the binary operation stub
// that does not check for the fast smi case.
- // The same stub is used for NO_SMI_CODE and SMI_CODE_INLINED.
if (generate_no_smi_code) {
- flags = SMI_CODE_INLINED;
+ flags = NO_SMI_CODE_IN_STUB;
}
GenericBinaryOpStub stub(op, overwrite_mode, flags);
Result answer = frame_->CallStub(&stub, 2);
@@ -1376,14 +1373,12 @@ class DeferredInlineSmiOperation: public DeferredCode {
void DeferredInlineSmiOperation::Generate() {
- __ push(src_);
- __ push(Immediate(value_));
// For mod we don't generate all the Smi code inline.
GenericBinaryOpStub stub(
op_,
overwrite_mode_,
- (op_ == Token::MOD) ? SMI_CODE_IN_STUB : SMI_CODE_INLINED);
- __ CallStub(&stub);
+ (op_ == Token::MOD) ? NO_GENERIC_BINARY_FLAGS : NO_SMI_CODE_IN_STUB);
+ stub.GenerateCall(masm_, src_, value_);
if (!dst_.is(eax)) __ mov(dst_, eax);
}
@@ -1417,10 +1412,8 @@ class DeferredInlineSmiOperationReversed: public DeferredCode {
void DeferredInlineSmiOperationReversed::Generate() {
- __ push(Immediate(value_));
- __ push(src_);
- GenericBinaryOpStub igostub(op_, overwrite_mode_, SMI_CODE_INLINED);
- __ CallStub(&igostub);
+ GenericBinaryOpStub igostub(op_, overwrite_mode_, NO_SMI_CODE_IN_STUB);
+ igostub.GenerateCall(masm_, value_, src_);
if (!dst_.is(eax)) __ mov(dst_, eax);
}
@@ -1449,10 +1442,8 @@ class DeferredInlineSmiAdd: public DeferredCode {
void DeferredInlineSmiAdd::Generate() {
// Undo the optimistic add operation and call the shared stub.
__ sub(Operand(dst_), Immediate(value_));
- __ push(dst_);
- __ push(Immediate(value_));
- GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, SMI_CODE_INLINED);
- __ CallStub(&igostub);
+ GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, NO_SMI_CODE_IN_STUB);
+ igostub.GenerateCall(masm_, dst_, value_);
if (!dst_.is(eax)) __ mov(dst_, eax);
}
@@ -1481,10 +1472,8 @@ class DeferredInlineSmiAddReversed: public DeferredCode {
void DeferredInlineSmiAddReversed::Generate() {
// Undo the optimistic add operation and call the shared stub.
__ sub(Operand(dst_), Immediate(value_));
- __ push(Immediate(value_));
- __ push(dst_);
- GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, SMI_CODE_INLINED);
- __ CallStub(&igostub);
+ GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, NO_SMI_CODE_IN_STUB);
+ igostub.GenerateCall(masm_, value_, dst_);
if (!dst_.is(eax)) __ mov(dst_, eax);
}
@@ -1514,10 +1503,8 @@ class DeferredInlineSmiSub: public DeferredCode {
void DeferredInlineSmiSub::Generate() {
// Undo the optimistic sub operation and call the shared stub.
__ add(Operand(dst_), Immediate(value_));
- __ push(dst_);
- __ push(Immediate(value_));
- GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, SMI_CODE_INLINED);
- __ CallStub(&igostub);
+ GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, NO_SMI_CODE_IN_STUB);
+ igostub.GenerateCall(masm_, dst_, value_);
if (!dst_.is(eax)) __ mov(dst_, eax);
}
@@ -2711,288 +2698,332 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
}
-void CodeGenerator::VisitLoopStatement(LoopStatement* node) {
+void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) {
ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ LoopStatement");
+ 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;
+ }
- // Simple condition analysis. ALWAYS_TRUE and ALWAYS_FALSE represent a
- // known result for the test expression, with no side effects.
- enum { ALWAYS_TRUE, ALWAYS_FALSE, DONT_KNOW } info = DONT_KNOW;
- if (node->cond() == NULL) {
- ASSERT(node->type() == LoopStatement::FOR_LOOP);
- info = ALWAYS_TRUE;
- } else {
- Literal* lit = node->cond()->AsLiteral();
- if (lit != NULL) {
- if (lit->IsTrue()) {
- info = ALWAYS_TRUE;
- } else if (lit->IsFalse()) {
- info = ALWAYS_FALSE;
+ 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()) {
+ ControlDestination dest(&body, node->break_target(), false);
+ LoadCondition(node->cond(), NOT_INSIDE_TYPEOF, &dest, true);
+ }
+ if (node->break_target()->is_linked()) {
+ node->break_target()->Bind();
+ }
+ break;
}
- switch (node->type()) {
- case LoopStatement::DO_LOOP: {
- JumpTarget body(JumpTarget::BIDIRECTIONAL);
- IncrementLoopNesting();
+ DecrementLoopNesting();
+}
+
- // Label the top of the loop for the backward jump if necessary.
- if (info == ALWAYS_TRUE) {
- // Use the continue target.
- node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
- node->continue_target()->Bind();
- } else if (info == ALWAYS_FALSE) {
- // No need to label it.
+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 {
- // Continue is the test, so use the backward body target.
- ASSERT(info == DONT_KNOW);
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
- body.Bind();
+ // 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();
}
-
- CheckStack(); // TODO(1222600): ignore if body contains calls.
- Visit(node->body());
-
- // Compile the test.
- if (info == 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();
+ // 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(), NOT_INSIDE_TYPEOF, &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;
}
- } else if (info == 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();
- }
+ // 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;
+ }
- } else {
- ASSERT(info == 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.
+ 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(), NOT_INSIDE_TYPEOF, &dest, true);
}
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
+ } 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();
+}
- case LoopStatement::WHILE_LOOP: {
- // 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();
- IncrementLoopNesting();
+void CodeGenerator::VisitForStatement(ForStatement* node) {
+ ASSERT(!in_spilled_code());
+ Comment cmnt(masm_, "[ ForStatement");
+ CodeForStatementPosition(node);
- // If the condition is always false and has no side effects, we
- // do not need to compile anything.
- if (info == ALWAYS_FALSE) break;
+ // Compile the init expression if present.
+ if (node->init() != NULL) {
+ Visit(node->init());
+ }
- JumpTarget body;
- if (test_at_bottom) {
- body.set_direction(JumpTarget::BIDIRECTIONAL);
- }
+ // 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;
- // Based on the condition analysis, compile the test as necessary.
- if (info == ALWAYS_TRUE) {
- // We will not compile the test expression. Label the top of
- // the loop with the continue target.
+ // 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 {
- ASSERT(info == DONT_KNOW); // ALWAYS_FALSE cannot reach here.
- 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(), NOT_INSIDE_TYPEOF, &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()) break;
-
- // 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();
- }
+ // Otherwise use the backward loop target.
+ node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
+ loop.Bind();
}
-
- CheckStack(); // TODO(1222600): ignore if body contains calls.
- Visit(node->body());
-
- // Based on the condition analysis, compile the backward jump as
- // necessary.
- if (info == 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) {
+ // 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 {
- ASSERT(info == DONT_KNOW); // ALWAYS_FALSE cannot reach here.
- 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(), NOT_INSIDE_TYPEOF, &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();
- }
- }
+ // 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(), NOT_INSIDE_TYPEOF, &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;
+ }
- // 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();
+ // 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;
+ }
- case LoopStatement::FOR_LOOP: {
- // 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();
-
- // Compile the init expression if present.
- if (node->init() != NULL) {
- Visit(node->init());
- }
-
- IncrementLoopNesting();
-
- // If the condition is always false and has no side effects, we
- // do not need to compile anything else.
- if (info == ALWAYS_FALSE) break;
+ CheckStack(); // TODO(1222600): ignore if body contains calls.
+ Visit(node->body());
- // Target for backward edge if no test at the bottom, otherwise
- // unused.
- JumpTarget loop(JumpTarget::BIDIRECTIONAL);
+ // If there is an update expression, compile it if necessary.
+ if (node->next() != NULL) {
+ if (node->continue_target()->is_linked()) {
+ node->continue_target()->Bind();
+ }
- // 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);
- }
+ // 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 test as necessary.
- if (info == ALWAYS_TRUE) {
- // We will not compile the test expression. Label the top of
- // the loop.
+ // Based on the condition analysis, compile the backward jump as
+ // necessary.
+ switch (info) {
+ case ALWAYS_TRUE:
+ if (has_valid_frame()) {
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();
- }
- } else {
- ASSERT(info == 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();
+ node->continue_target()->Jump();
} 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(), NOT_INSIDE_TYPEOF, &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()) break;
-
- // 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();
+ loop.Jump();
}
}
-
- 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) {
+ 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 update by falling out of the body or
- // by a continue.
+ // 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()) {
- // 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());
+ // The break target is the fall-through (body is a backward
+ // jump from here).
+ ControlDestination dest(&body, node->break_target(), false);
+ LoadCondition(node->cond(), NOT_INSIDE_TYPEOF, &dest, true);
}
- }
-
- // Based on the condition analysis, compile the backward jump as
- // necessary.
- if (info == ALWAYS_TRUE) {
+ } else {
+ // Otherwise, jump back to the test at the top.
if (has_valid_frame()) {
if (node->next() == NULL) {
node->continue_target()->Jump();
@@ -3000,47 +3031,19 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) {
loop.Jump();
}
}
- } else {
- ASSERT(info == DONT_KNOW); // ALWAYS_FALSE cannot reach here.
- 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(), NOT_INSIDE_TYPEOF, &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();
- }
- }
- }
- }
-
- // 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();
}
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();
- node->continue_target()->Unuse();
- node->break_target()->Unuse();
}
@@ -3234,10 +3237,10 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
}
-void CodeGenerator::VisitTryCatch(TryCatch* node) {
+void CodeGenerator::VisitTryCatchStatement(TryCatchStatement* node) {
ASSERT(!in_spilled_code());
VirtualFrame::SpilledScope spilled_scope;
- Comment cmnt(masm_, "[ TryCatch");
+ Comment cmnt(masm_, "[ TryCatchStatement");
CodeForStatementPosition(node);
JumpTarget try_block;
@@ -3370,10 +3373,10 @@ void CodeGenerator::VisitTryCatch(TryCatch* node) {
}
-void CodeGenerator::VisitTryFinally(TryFinally* node) {
+void CodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* node) {
ASSERT(!in_spilled_code());
VirtualFrame::SpilledScope spilled_scope;
- Comment cmnt(masm_, "[ TryFinally");
+ Comment cmnt(masm_, "[ TryFinallyStatement");
CodeForStatementPosition(node);
// State: Used to keep track of reason for entering the finally
@@ -5338,8 +5341,8 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
switch (op) {
case Token::SUB: {
bool overwrite =
- (node->AsBinaryOperation() != NULL &&
- node->AsBinaryOperation()->ResultOverwriteAllowed());
+ (node->expression()->AsBinaryOperation() != NULL &&
+ node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
UnarySubStub stub(overwrite);
// TODO(1222589): remove dependency of TOS being cached inside stub
Result operand = frame_->Pop();
@@ -6523,6 +6526,116 @@ void ToBooleanStub::Generate(MacroAssembler* masm) {
}
+void GenericBinaryOpStub::GenerateCall(
+ MacroAssembler* masm,
+ Register left,
+ Register right) {
+ if (!ArgsInRegistersSupported()) {
+ // Only pass arguments in registers if there is no smi code in the stub.
+ __ push(left);
+ __ push(right);
+ } else {
+ // The calling convention with registers is left in edx and right in eax.
+ __ IncrementCounter(&Counters::generic_binary_stub_calls_regs, 1);
+ if (!(left.is(edx) && right.is(eax))) {
+ if (left.is(eax) && right.is(edx)) {
+ if (IsOperationCommutative()) {
+ SetArgsReversed();
+ } else {
+ __ xchg(left, right);
+ }
+ } else if (left.is(edx)) {
+ __ mov(eax, right);
+ } else if (left.is(eax)) {
+ if (IsOperationCommutative()) {
+ __ mov(edx, right);
+ SetArgsReversed();
+ } else {
+ __ mov(edx, left);
+ __ mov(eax, right);
+ }
+ } else if (right.is(edx)) {
+ if (IsOperationCommutative()) {
+ __ mov(eax, left);
+ SetArgsReversed();
+ } else {
+ __ mov(eax, right);
+ __ mov(edx, left);
+ }
+ } else if (right.is(eax)) {
+ __ mov(edx, left);
+ } else {
+ __ mov(edx, left);
+ __ mov(eax, right);
+ }
+ }
+
+ // Update flags to indicate that arguments are in registers.
+ SetArgsInRegisters();
+ }
+
+ // Call the stub.
+ __ CallStub(this);
+}
+
+
+void GenericBinaryOpStub::GenerateCall(
+ MacroAssembler* masm,
+ Register left,
+ Smi* right) {
+ if (!ArgsInRegistersSupported()) {
+ // Only pass arguments in registers if there is no smi code in the stub.
+ __ push(left);
+ __ push(Immediate(right));
+ } else {
+ // Adapt arguments to the calling convention left in edx and right in eax.
+ if (left.is(edx)) {
+ __ mov(eax, Immediate(right));
+ } else if (left.is(eax) && IsOperationCommutative()) {
+ __ mov(edx, Immediate(right));
+ SetArgsReversed();
+ } else {
+ __ mov(edx, left);
+ __ mov(eax, Immediate(right));
+ }
+
+ // Update flags to indicate that arguments are in registers.
+ SetArgsInRegisters();
+ }
+
+ // Call the stub.
+ __ CallStub(this);
+}
+
+
+void GenericBinaryOpStub::GenerateCall(
+ MacroAssembler* masm,
+ Smi* left,
+ Register right) {
+ if (flags_ != NO_SMI_CODE_IN_STUB) {
+ // Only pass arguments in registers if there is no smi code in the stub.
+ __ push(Immediate(left));
+ __ push(right);
+ } else {
+ // Adapt arguments to the calling convention left in edx and right in eax.
+ bool is_commutative = (op_ == (Token::ADD) || (op_ == Token::MUL));
+ if (right.is(eax)) {
+ __ mov(edx, Immediate(left));
+ } else if (right.is(edx) && is_commutative) {
+ __ mov(eax, Immediate(left));
+ } else {
+ __ mov(edx, Immediate(left));
+ __ mov(eax, right);
+ }
+ // Update flags to indicate that arguments are in registers.
+ SetArgsInRegisters();
+ }
+
+ // Call the stub.
+ __ CallStub(this);
+}
+
+
void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) {
// Perform fast-case smi code for the operation (eax <op> ebx) and
// leave result in register eax.
@@ -6670,22 +6783,23 @@ void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) {
void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
Label call_runtime;
- if (flags_ == SMI_CODE_IN_STUB) {
- // The fast case smi code wasn't inlined in the stub caller
- // code. Generate it here to speed up common operations.
+ __ IncrementCounter(&Counters::generic_binary_stub_calls, 1);
+
+ // 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 (HasSmiCodeInStub()) {
Label slow;
- __ mov(ebx, Operand(esp, 1 * kPointerSize)); // get y
- __ mov(eax, Operand(esp, 2 * kPointerSize)); // get x
+ __ mov(ebx, Operand(esp, 1 * kPointerSize));
+ __ mov(eax, Operand(esp, 2 * kPointerSize));
GenerateSmiCode(masm, &slow);
- __ ret(2 * kPointerSize); // remove both operands
-
+ GenerateReturn(masm);
// Too bad. The fast case smi code didn't succeed.
__ bind(&slow);
}
- // Setup registers.
- __ mov(eax, Operand(esp, 1 * kPointerSize)); // get y
- __ mov(edx, Operand(esp, 2 * kPointerSize)); // get x
+ // Make sure the arguments are in edx and eax.
+ GenerateLoadArguments(masm);
// Floating point case.
switch (op_) {
@@ -6719,19 +6833,24 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
__ test(eax, Immediate(kSmiTagMask));
__ j(not_zero, &skip_allocation, not_taken);
// Fall through!
- case NO_OVERWRITE:
+ case NO_OVERWRITE: {
+ // Allocate a heap number for the result. Keep eax and edx intact
+ // for the possible runtime call.
FloatingPointHelper::AllocateHeapNumber(masm,
&call_runtime,
ecx,
- edx,
- eax);
+ no_reg,
+ ebx);
+ // 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();
}
__ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
- __ ret(2 * kPointerSize);
-
+ GenerateReturn(masm);
} else { // SSE2 not available, use FPU.
FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx);
// Allocate a heap number, if needed.
@@ -6747,11 +6866,16 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
__ 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.
FloatingPointHelper::AllocateHeapNumber(masm,
&call_runtime,
ecx,
- edx,
- eax);
+ no_reg,
+ ebx);
+ // 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();
@@ -6766,7 +6890,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
default: UNREACHABLE();
}
__ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
- __ ret(2 * kPointerSize);
+ GenerateReturn(masm);
}
}
case Token::MOD: {
@@ -6899,8 +7023,20 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
}
// If all else fails, use the runtime system to get the correct
- // result.
+ // result. If arguments was passed in registers now place them on the
+ // stack in the correct order.
__ bind(&call_runtime);
+ if (HasArgumentsInRegisters()) {
+ __ pop(ecx);
+ if (HasArgumentsReversed()) {
+ __ push(eax);
+ __ push(edx);
+ } else {
+ __ push(edx);
+ __ push(eax);
+ }
+ __ push(ecx);
+ }
switch (op_) {
case Token::ADD: {
// Test for string arguments before calling runtime.
@@ -6977,6 +7113,26 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
}
+void GenericBinaryOpStub::GenerateLoadArguments(MacroAssembler* masm) {
+ // If arguments are not passed in registers read them from the stack.
+ if (!HasArgumentsInRegisters()) {
+ __ 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 (!HasArgumentsInRegisters()) {
+ __ ret(2 * kPointerSize); // Remove both operands
+ } else {
+ __ ret(0);
+ }
+}
+
+
void FloatingPointHelper::AllocateHeapNumber(MacroAssembler* masm,
Label* need_gc,
Register scratch1,
diff --git a/deps/v8/src/ia32/codegen-ia32.h b/deps/v8/src/ia32/codegen-ia32.h
index 142a5a104..ec4a8be16 100644
--- a/deps/v8/src/ia32/codegen-ia32.h
+++ b/deps/v8/src/ia32/codegen-ia32.h
@@ -294,6 +294,15 @@ class CodeGenerator: public AstVisitor {
Handle<Script> script,
bool is_eval);
+ // Printing of AST, etc. as requested by flags.
+ static void MakeCodePrologue(FunctionLiteral* fun);
+
+ // Allocate and install the code.
+ static Handle<Code> MakeCodeEpilogue(FunctionLiteral* fun,
+ MacroAssembler* masm,
+ Code::Flags flags,
+ Handle<Script> script);
+
#ifdef ENABLE_LOGGING_AND_PROFILING
static bool ShouldGenerateLog(Expression* type);
#endif
@@ -303,6 +312,8 @@ class CodeGenerator: public AstVisitor {
bool is_toplevel,
Handle<Script> script);
+ static void RecordPositions(MacroAssembler* masm, int pos);
+
// Accessors
MacroAssembler* masm() { return masm_; }
@@ -548,6 +559,14 @@ class CodeGenerator: public AstVisitor {
inline void GenerateMathSin(ZoneList<Expression*>* args);
inline void GenerateMathCos(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.
@@ -604,47 +623,62 @@ class CodeGenerator: public AstVisitor {
};
-// Flag that indicates whether or not the code that handles smi arguments
-// should be placed in the stub, inlined, or omitted entirely.
+// Flag that indicates whether how to generate code for the stub.
enum GenericBinaryFlags {
- SMI_CODE_IN_STUB,
- SMI_CODE_INLINED
+ 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,
+ GenericBinaryOpStub(Token::Value operation,
OverwriteMode mode,
GenericBinaryFlags flags)
- : op_(op), mode_(mode), flags_(flags) {
+ : op_(operation),
+ mode_(mode),
+ flags_(flags),
+ args_in_registers_(false),
+ args_reversed_(false) {
use_sse3_ = CpuFeatures::IsSupported(CpuFeatures::SSE3);
ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
}
- void GenerateSmiCode(MacroAssembler* masm, Label* slow);
+ // 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);
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_;
const char* GetName();
#ifdef DEBUG
void Print() {
- PrintF("GenericBinaryOpStub (op %s), (mode %d, flags %d)\n",
+ PrintF("GenericBinaryOpStub (op %s), "
+ "(mode %d, flags %d, registers %d, reversed %d)\n",
Token::String(op_),
static_cast<int>(mode_),
- static_cast<int>(flags_));
+ static_cast<int>(flags_),
+ static_cast<int>(args_in_registers_),
+ static_cast<int>(args_reversed_));
}
#endif
- // Minor key encoding in 16 bits FSOOOOOOOOOOOOMM.
+ // Minor key encoding in 16 bits FRASOOOOOOOOOOMM.
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
- class OpBits: public BitField<Token::Value, 2, 12> {};
- class SSE3Bits: public BitField<bool, 14, 1> {};
+ class OpBits: public BitField<Token::Value, 2, 10> {};
+ class SSE3Bits: public BitField<bool, 12, 1> {};
+ class ArgsInRegistersBits: public BitField<bool, 13, 1> {};
+ class ArgsReversedBits: public BitField<bool, 14, 1> {};
class FlagBits: public BitField<GenericBinaryFlags, 15, 1> {};
Major MajorKey() { return GenericBinaryOp; }
@@ -653,9 +687,30 @@ class GenericBinaryOpStub: public CodeStub {
return OpBits::encode(op_)
| ModeBits::encode(mode_)
| FlagBits::encode(flags_)
- | SSE3Bits::encode(use_sse3_);
+ | SSE3Bits::encode(use_sse3_)
+ | ArgsInRegistersBits::encode(args_in_registers_)
+ | ArgsReversedBits::encode(args_reversed_);
}
+
void Generate(MacroAssembler* masm);
+ void GenerateSmiCode(MacroAssembler* masm, Label* slow);
+ void GenerateLoadArguments(MacroAssembler* masm);
+ void GenerateReturn(MacroAssembler* masm);
+
+ bool ArgsInRegistersSupported() {
+ return ((op_ == Token::ADD) || (op_ == Token::SUB)
+ || (op_ == Token::MUL) || (op_ == Token::DIV))
+ && flags_ != NO_SMI_CODE_IN_STUB;
+ }
+ 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 HasArgumentsInRegisters() { return args_in_registers_; }
+ bool HasArgumentsReversed() { return args_reversed_; }
};
diff --git a/deps/v8/src/ia32/debug-ia32.cc b/deps/v8/src/ia32/debug-ia32.cc
index 7e0dfd148..2d20117aa 100644
--- a/deps/v8/src/ia32/debug-ia32.cc
+++ b/deps/v8/src/ia32/debug-ia32.cc
@@ -63,7 +63,7 @@ void BreakLocationIterator::ClearDebugBreakAtReturn() {
// having been patched with a call instruction.
bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()));
- return rinfo->IsCallInstruction();
+ return rinfo->IsPatchedReturnSequence();
}
diff --git a/deps/v8/src/ia32/disasm-ia32.cc b/deps/v8/src/ia32/disasm-ia32.cc
index 458844e05..adedf3489 100644
--- a/deps/v8/src/ia32/disasm-ia32.cc
+++ b/deps/v8/src/ia32/disasm-ia32.cc
@@ -124,6 +124,14 @@ static const char* set_conditional_mnem[] = {
};
+static const char* conditional_move_mnem[] = {
+ /*0*/ "cmovo", "cmovno", "cmovc", "cmovnc",
+ /*4*/ "cmovz", "cmovnz", "cmovna", "cmova",
+ /*8*/ "cmovs", "cmovns", "cmovpe", "cmovpo",
+ /*12*/ "cmovl", "cmovnl", "cmovng", "cmovg"
+};
+
+
enum InstructionType {
NO_INSTR,
ZERO_OPERANDS_INSTR,
@@ -311,6 +319,7 @@ class DisassemblerIA32 {
int JumpConditional(byte* data, const char* comment);
int JumpConditionalShort(byte* data, const char* comment);
int SetCC(byte* data);
+ int CMov(byte* data);
int FPUInstruction(byte* data);
void AppendToBuffer(const char* format, ...);
@@ -615,6 +624,16 @@ int DisassemblerIA32::SetCC(byte* data) {
// Returns number of bytes used, including *data.
+int DisassemblerIA32::CMov(byte* data) {
+ assert(*data == 0x0F);
+ byte cond = *(data + 1) & 0x0F;
+ const char* mnem = conditional_move_mnem[cond];
+ int op_size = PrintOperands(mnem, REG_OPER_OP_ORDER, data + 2);
+ return 2 + op_size; // includes 0x0F
+}
+
+
+// Returns number of bytes used, including *data.
int DisassemblerIA32::FPUInstruction(byte* data) {
byte b1 = *data;
byte b2 = *(data + 1);
@@ -861,6 +880,8 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
data += PrintOperands(f0mnem, REG_OPER_OP_ORDER, data);
} else if ((f0byte & 0xF0) == 0x90) {
data += SetCC(data);
+ } else if ((f0byte & 0xF0) == 0x40) {
+ data += CMov(data);
} else {
data += 2;
if (f0byte == 0xAB || f0byte == 0xA5 || f0byte == 0xAD) {
@@ -956,6 +977,19 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
AppendToBuffer("mov_w ");
data += PrintRightOperand(data);
AppendToBuffer(",%s", NameOfCPURegister(regop));
+ } else if (*data == 0x0F) {
+ data++;
+ if (*data == 0x2F) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("comisd %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ } else {
+ UnimplementedInstruction();
+ }
} else {
UnimplementedInstruction();
}
diff --git a/deps/v8/src/ia32/fast-codegen-ia32.cc b/deps/v8/src/ia32/fast-codegen-ia32.cc
new file mode 100644
index 000000000..ee1b92d09
--- /dev/null
+++ b/deps/v8/src/ia32/fast-codegen-ia32.cc
@@ -0,0 +1,163 @@
+// 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 "fast-codegen.h"
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm_)
+
+// Generate code for a JS function. On entry to the function the receiver
+// and arguments have been pushed on the stack left to right, with the
+// return address on top of them. The actual argument count matches the
+// formal parameter count expected by the function.
+//
+// The live registers are:
+// o edi: the JS function object being called (ie, ourselves)
+// o esi: our context
+// o ebp: our caller's frame pointer
+// o esp: stack pointer (pointing to return address)
+//
+// The function builds a JS frame. Please see JavaScriptFrameConstants in
+// frames-ia32.h for its layout.
+void FastCodeGenerator::Generate(FunctionLiteral* fun) {
+ function_ = fun;
+ SetFunctionPosition(fun);
+
+ __ push(ebp); // Caller's frame pointer.
+ __ mov(ebp, esp);
+ __ push(esi); // Callee's context.
+ __ push(edi); // Callee's JS Function.
+
+ { Comment cmnt(masm_, "[ Allocate locals");
+ int locals_count = fun->scope()->num_stack_slots();
+ for (int i = 0; i < locals_count; i++) {
+ __ push(Immediate(Factory::undefined_value()));
+ }
+ }
+
+ { Comment cmnt(masm_, "[ Stack check");
+ Label ok;
+ ExternalReference stack_guard_limit =
+ ExternalReference::address_of_stack_guard_limit();
+ __ cmp(esp, Operand::StaticVariable(stack_guard_limit));
+ __ j(above_equal, &ok, taken);
+ StackCheckStub stub;
+ __ CallStub(&stub);
+ __ bind(&ok);
+ }
+
+ { Comment cmnt(masm_, "[ Body");
+ VisitStatements(fun->body());
+ }
+
+ { Comment cmnt(masm_, "[ return <undefined>;");
+ // Emit a 'return undefined' in case control fell off the end of the
+ // body.
+ __ mov(eax, Factory::undefined_value());
+ SetReturnPosition(fun);
+ __ RecordJSReturn();
+ // Do not use the leave instruction here because it is too short to
+ // patch with the code required by the debugger.
+ __ mov(esp, ebp);
+ __ pop(ebp);
+ __ ret((fun->scope()->num_parameters() + 1) * kPointerSize);
+ }
+}
+
+
+void FastCodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) {
+ Comment cmnt(masm_, "[ ExpressionStatement");
+ SetStatementPosition(stmt);
+ Visit(stmt->expression());
+}
+
+
+void FastCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
+ Comment cmnt(masm_, "[ ReturnStatement");
+ SetStatementPosition(stmt);
+ Visit(stmt->expression());
+ __ pop(eax);
+ __ RecordJSReturn();
+ // Do not use the leave instruction here because it is too short to
+ // patch with the code required by the debugger.
+ __ mov(esp, ebp);
+ __ pop(ebp);
+ __ ret((function_->scope()->num_parameters() + 1) * kPointerSize);
+}
+
+
+void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
+ Comment cmnt(masm_, "[ VariableProxy");
+ Expression* rewrite = expr->var()->rewrite();
+ ASSERT(rewrite != NULL);
+
+ Slot* slot = rewrite->AsSlot();
+ ASSERT(slot != NULL);
+ { Comment cmnt(masm_, "[ Slot");
+ if (expr->location().is_temporary()) {
+ __ push(Operand(ebp, SlotOffset(slot)));
+ } else {
+ ASSERT(expr->location().is_nowhere());
+ }
+ }
+}
+
+
+void FastCodeGenerator::VisitLiteral(Literal* expr) {
+ Comment cmnt(masm_, "[ Literal");
+ if (expr->location().is_temporary()) {
+ __ push(Immediate(expr->handle()));
+ } else {
+ ASSERT(expr->location().is_nowhere());
+ }
+}
+
+
+void FastCodeGenerator::VisitAssignment(Assignment* expr) {
+ Comment cmnt(masm_, "[ Assignment");
+ ASSERT(expr->op() == Token::ASSIGN || expr->op() == Token::INIT_VAR);
+ Visit(expr->value());
+
+ Variable* var = expr->target()->AsVariableProxy()->AsVariable();
+ ASSERT(var != NULL && var->slot() != NULL);
+
+ if (expr->location().is_temporary()) {
+ __ mov(eax, Operand(esp, 0));
+ __ mov(Operand(ebp, SlotOffset(var->slot())), eax);
+ } else {
+ ASSERT(expr->location().is_nowhere());
+ __ pop(Operand(ebp, SlotOffset(var->slot())));
+ }
+}
+
+
+} } // namespace v8::internal
diff --git a/deps/v8/src/ia32/ic-ia32.cc b/deps/v8/src/ia32/ic-ia32.cc
index f7369a8b7..af0568033 100644
--- a/deps/v8/src/ia32/ic-ia32.cc
+++ b/deps/v8/src/ia32/ic-ia32.cc
@@ -298,7 +298,6 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
__ shl(eax, kSmiTagSize);
__ ret(0);
-
// Slow case: Load name and receiver from stack and jump to runtime.
__ bind(&slow);
__ IncrementCounter(&Counters::keyed_load_generic_slow, 1);
@@ -424,14 +423,11 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
__ mov(edx, eax); // Save the value.
__ sar(eax, kSmiTagSize); // Untag the value.
{ // Clamp the value to [0..255].
- Label done, is_negative;
+ Label done;
__ test(eax, Immediate(0xFFFFFF00));
__ j(zero, &done);
- __ j(negative, &is_negative);
- __ mov(eax, Immediate(255));
- __ jmp(&done);
- __ bind(&is_negative);
- __ xor_(eax, Operand(eax)); // Clear eax.
+ __ setcc(negative, eax); // 1 if negative, 0 if positive.
+ __ dec_b(eax); // 0 if negative, 255 if positive.
__ bind(&done);
}
__ mov(ecx, FieldOperand(ecx, PixelArray::kExternalPointerOffset));
@@ -458,7 +454,6 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
__ sub(Operand(ebx), Immediate(1 << kSmiTagSize)); // decrement ebx again
__ jmp(&fast);
-
// Array case: Get the length and the elements array from the JS
// array. Check that the array is in fast mode; if it is the
// length is always a smi.
diff --git a/deps/v8/src/ia32/virtual-frame-ia32.cc b/deps/v8/src/ia32/virtual-frame-ia32.cc
index 1b8232f44..980cec8eb 100644
--- a/deps/v8/src/ia32/virtual-frame-ia32.cc
+++ b/deps/v8/src/ia32/virtual-frame-ia32.cc
@@ -161,15 +161,16 @@ void VirtualFrame::SyncRange(int begin, int end) {
// on the stack.
int start = Min(begin, stack_pointer_ + 1);
- // If positive we have to adjust the stack pointer.
- int delta = end - stack_pointer_;
- if (delta > 0) {
- stack_pointer_ = end;
- __ sub(Operand(esp), Immediate(delta * kPointerSize));
- }
-
+ // 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()) SyncElementBelowStackPointer(i);
+ if (!elements_[i].is_synced()) {
+ if (i <= stack_pointer_) {
+ SyncElementBelowStackPointer(i);
+ } else {
+ SyncElementByPushing(i);
+ }
+ }
}
}
@@ -454,14 +455,16 @@ void VirtualFrame::Enter() {
Comment cmnt(masm(), "[ Enter JS frame");
#ifdef DEBUG
- // 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).");
+ 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);
diff --git a/deps/v8/src/jsregexp.cc b/deps/v8/src/jsregexp.cc
index e518662d0..c77f32d1e 100644
--- a/deps/v8/src/jsregexp.cc
+++ b/deps/v8/src/jsregexp.cc
@@ -45,13 +45,10 @@
#ifdef V8_NATIVE_REGEXP
#if V8_TARGET_ARCH_IA32
-#include "ia32/macro-assembler-ia32.h"
#include "ia32/regexp-macro-assembler-ia32.h"
#elif V8_TARGET_ARCH_X64
-#include "x64/macro-assembler-x64.h"
#include "x64/regexp-macro-assembler-x64.h"
#elif V8_TARGET_ARCH_ARM
-#include "arm/macro-assembler-arm.h"
#include "arm/regexp-macro-assembler-arm.h"
#else
#error Unsupported target architecture.
diff --git a/deps/v8/src/jsregexp.h b/deps/v8/src/jsregexp.h
index 3bc30b6a8..84f8d98ce 100644
--- a/deps/v8/src/jsregexp.h
+++ b/deps/v8/src/jsregexp.h
@@ -28,6 +28,8 @@
#ifndef V8_JSREGEXP_H_
#define V8_JSREGEXP_H_
+#include "macro-assembler.h"
+
namespace v8 {
namespace internal {
diff --git a/deps/v8/src/jump-target.h b/deps/v8/src/jump-target.h
index 0c42f1b71..0933ee781 100644
--- a/deps/v8/src/jump-target.h
+++ b/deps/v8/src/jump-target.h
@@ -28,6 +28,8 @@
#ifndef V8_JUMP_TARGET_H_
#define V8_JUMP_TARGET_H_
+#include "macro-assembler.h"
+
namespace v8 {
namespace internal {
diff --git a/deps/v8/src/location.h b/deps/v8/src/location.h
new file mode 100644
index 000000000..59cd88a07
--- /dev/null
+++ b/deps/v8/src/location.h
@@ -0,0 +1,56 @@
+// 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_LOCATION_H_
+#define V8_LOCATION_H_
+
+#include "utils.h"
+
+namespace v8 {
+namespace internal {
+
+class Location BASE_EMBEDDED {
+ public:
+ static Location Temporary() { return Location(TEMP); }
+ static Location Nowhere() { return Location(NOWHERE); }
+ static Location Constant() { return Location(CONSTANT); }
+
+ bool is_temporary() { return type_ == TEMP; }
+ bool is_nowhere() { return type_ == NOWHERE; }
+
+ private:
+ enum Type { TEMP, NOWHERE, CONSTANT };
+
+ explicit Location(Type type) : type_(type) {}
+
+ Type type_;
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_LOCATION_H_
diff --git a/deps/v8/src/log.cc b/deps/v8/src/log.cc
index 1c82f8525..d1d9a31e4 100644
--- a/deps/v8/src/log.cc
+++ b/deps/v8/src/log.cc
@@ -934,6 +934,21 @@ void Logger::HeapSampleJSRetainersEvent(
}
+void Logger::HeapSampleJSProducerEvent(const char* constructor,
+ Address* stack) {
+#ifdef ENABLE_LOGGING_AND_PROFILING
+ if (!Log::IsEnabled() || !FLAG_log_gc) return;
+ LogMessageBuilder msg;
+ msg.Append("heap-js-prod-item,%s", constructor);
+ while (*stack != NULL) {
+ msg.Append(",0x%" V8PRIxPTR, *stack++);
+ }
+ msg.Append("\n");
+ msg.WriteToLogFile();
+#endif
+}
+
+
void Logger::DebugTag(const char* call_site_tag) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (!Log::IsEnabled() || !FLAG_log) return;
diff --git a/deps/v8/src/log.h b/deps/v8/src/log.h
index 07a0429ac..13d45d2e3 100644
--- a/deps/v8/src/log.h
+++ b/deps/v8/src/log.h
@@ -223,6 +223,8 @@ class Logger {
int number, int bytes);
static void HeapSampleJSRetainersEvent(const char* constructor,
const char* event);
+ static void HeapSampleJSProducerEvent(const char* constructor,
+ Address* stack);
static void HeapSampleStats(const char* space, const char* kind,
int capacity, int used);
diff --git a/deps/v8/src/macros.py b/deps/v8/src/macros.py
index c75f0ea48..ddd2f13bc 100644
--- a/deps/v8/src/macros.py
+++ b/deps/v8/src/macros.py
@@ -118,9 +118,7 @@ macro NUMBER_OF_CAPTURES(array) = ((array)[0]);
# a type error is thrown.
macro DATE_VALUE(arg) = (%_ClassOf(arg) === 'Date' ? %_ValueOf(arg) : ThrowDateTypeError());
-# Last input and last subject are after the captures so we can omit them on
-# results returned from global searches. Beware - these evaluate their
-# arguments twice.
+# Last input and last subject of regexp matches.
macro LAST_SUBJECT(array) = ((array)[1]);
macro LAST_INPUT(array) = ((array)[2]);
diff --git a/deps/v8/src/mark-compact.cc b/deps/v8/src/mark-compact.cc
index a20245c38..5a3ab8905 100644
--- a/deps/v8/src/mark-compact.cc
+++ b/deps/v8/src/mark-compact.cc
@@ -279,7 +279,7 @@ class MarkingVisitor : public ObjectVisitor {
void VisitDebugTarget(RelocInfo* rinfo) {
ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()) &&
- rinfo->IsCallInstruction());
+ rinfo->IsPatchedReturnSequence());
HeapObject* code = Code::GetCodeFromTargetAddress(rinfo->call_address());
MarkCompactCollector::MarkObject(code);
}
@@ -1382,7 +1382,8 @@ class UpdatingVisitor: public ObjectVisitor {
}
void VisitDebugTarget(RelocInfo* rinfo) {
- ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()) && rinfo->IsCallInstruction());
+ ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()) &&
+ rinfo->IsPatchedReturnSequence());
Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
VisitPointer(&target);
rinfo->set_call_address(
diff --git a/deps/v8/src/mirror-delay.js b/deps/v8/src/mirror-delay.js
index c4ab7b8ee..cde553432 100644
--- a/deps/v8/src/mirror-delay.js
+++ b/deps/v8/src/mirror-delay.js
@@ -764,7 +764,7 @@ ObjectMirror.prototype.referencedBy = function(opt_max_objects) {
ObjectMirror.prototype.toText = function() {
var name;
var ctor = this.constructorFunction();
- if (ctor.isUndefined()) {
+ if (!ctor.isFunction()) {
name = this.className();
} else {
name = ctor.name();
diff --git a/deps/v8/src/objects-debug.cc b/deps/v8/src/objects-debug.cc
index 288cc21ec..afa51f672 100644
--- a/deps/v8/src/objects-debug.cc
+++ b/deps/v8/src/objects-debug.cc
@@ -29,7 +29,6 @@
#include "disassembler.h"
#include "disasm.h"
-#include "macro-assembler.h"
#include "jsregexp.h"
namespace v8 {
diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h
index 22e12ac7d..cb7b7c881 100644
--- a/deps/v8/src/objects-inl.h
+++ b/deps/v8/src/objects-inl.h
@@ -744,15 +744,17 @@ int Smi::value() {
Smi* Smi::FromInt(int value) {
ASSERT(Smi::IsValid(value));
+ int smi_shift_bits = kSmiTagSize + kSmiShiftSize;
intptr_t tagged_value =
- (static_cast<intptr_t>(value) << kSmiTagSize) | kSmiTag;
+ (static_cast<intptr_t>(value) << smi_shift_bits) | kSmiTag;
return reinterpret_cast<Smi*>(tagged_value);
}
Smi* Smi::FromIntptr(intptr_t value) {
ASSERT(Smi::IsValid(value));
- return reinterpret_cast<Smi*>((value << kSmiTagSize) | kSmiTag);
+ int smi_shift_bits = kSmiTagSize + kSmiShiftSize;
+ return reinterpret_cast<Smi*>((value << smi_shift_bits) | kSmiTag);
}
@@ -776,7 +778,7 @@ int Failure::requested() const {
kFailureTypeTagSize + kSpaceTagSize - kObjectAlignmentBits;
STATIC_ASSERT(kShiftBits >= 0);
ASSERT(type() == RETRY_AFTER_GC);
- return value() >> kShiftBits;
+ return static_cast<int>(value() >> kShiftBits);
}
@@ -802,29 +804,31 @@ Failure* Failure::OutOfMemoryException() {
}
-int Failure::value() const {
- return static_cast<int>(reinterpret_cast<intptr_t>(this) >> kFailureTagSize);
+intptr_t Failure::value() const {
+ return reinterpret_cast<intptr_t>(this) >> kFailureTagSize;
}
Failure* Failure::RetryAfterGC(int requested_bytes) {
// Assert that the space encoding fits in the three bytes allotted for it.
ASSERT((LAST_SPACE & ~kSpaceTagMask) == 0);
- int requested = requested_bytes >> kObjectAlignmentBits;
+ intptr_t requested = requested_bytes >> kObjectAlignmentBits;
+ int tag_bits = kSpaceTagSize + kFailureTypeTagSize;
+ if (((requested << tag_bits) >> tag_bits) != requested) {
+ // No room for entire requested size in the bits. Round down to
+ // maximally representable size.
+ requested = static_cast<intptr_t>(
+ (~static_cast<uintptr_t>(0)) >> (tag_bits + 1));
+ }
int value = (requested << kSpaceTagSize) | NEW_SPACE;
- ASSERT(value >> kSpaceTagSize == requested);
- ASSERT(Smi::IsValid(value));
- ASSERT(value == ((value << kFailureTypeTagSize) >> kFailureTypeTagSize));
- ASSERT(Smi::IsValid(value << kFailureTypeTagSize));
return Construct(RETRY_AFTER_GC, value);
}
-Failure* Failure::Construct(Type type, int value) {
- int info = (value << kFailureTypeTagSize) | type;
+Failure* Failure::Construct(Type type, intptr_t value) {
+ intptr_t info = (static_cast<intptr_t>(value) << kFailureTypeTagSize) | type;
ASSERT(((info << kFailureTagSize) >> kFailureTagSize) == info);
- return reinterpret_cast<Failure*>(
- (static_cast<intptr_t>(info) << kFailureTagSize) | kFailureTag);
+ return reinterpret_cast<Failure*>((info << kFailureTagSize) | kFailureTag);
}
@@ -832,6 +836,11 @@ bool Smi::IsValid(intptr_t value) {
#ifdef DEBUG
bool in_range = (value >= kMinValue) && (value <= kMaxValue);
#endif
+
+#ifdef V8_TARGET_ARCH_X64
+ // To be representable as a long smi, the value must be a 32-bit integer.
+ bool result = (value == static_cast<int32_t>(value));
+#else
// To be representable as an tagged small integer, the two
// most-significant bits of 'value' must be either 00 or 11 due to
// sign-extension. To check this we add 01 to the two
@@ -843,20 +852,8 @@ bool Smi::IsValid(intptr_t value) {
// in fact doesn't work correctly with gcc4.1.1 in some cases: The
// compiler may produce undefined results in case of signed integer
// overflow. The computation must be done w/ unsigned ints.
- bool result =
- ((static_cast<unsigned int>(value) + 0x40000000U) & 0x80000000U) == 0;
- ASSERT(result == in_range);
- return result;
-}
-
-
-bool Smi::IsIntptrValid(intptr_t value) {
-#ifdef DEBUG
- bool in_range = (value >= kMinValue) && (value <= kMaxValue);
+ bool result = (static_cast<uintptr_t>(value + 0x40000000U) < 0x80000000U);
#endif
- // See Smi::IsValid(int) for description.
- bool result =
- ((static_cast<uintptr_t>(value) + 0x40000000U) < 0x80000000U);
ASSERT(result == in_range);
return result;
}
diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc
index 834589a01..ab1d35fbb 100644
--- a/deps/v8/src/objects.cc
+++ b/deps/v8/src/objects.cc
@@ -618,12 +618,12 @@ void Smi::SmiPrint(StringStream* accumulator) {
void Failure::FailurePrint(StringStream* accumulator) {
- accumulator->Add("Failure(%d)", value());
+ accumulator->Add("Failure(%p)", reinterpret_cast<void*>(value()));
}
void Failure::FailurePrint() {
- PrintF("Failure(%d)", value());
+ PrintF("Failure(%p)", reinterpret_cast<void*>(value()));
}
@@ -4983,7 +4983,8 @@ void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) {
void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
- ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()) && rinfo->IsCallInstruction());
+ ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()) &&
+ rinfo->IsPatchedReturnSequence());
Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
Object* old_target = target;
VisitPointer(&target);
@@ -5009,7 +5010,7 @@ void Code::CodeIterateBody(ObjectVisitor* v) {
#ifdef ENABLE_DEBUGGER_SUPPORT
} else if (Debug::has_break_points() &&
RelocInfo::IsJSReturn(rmode) &&
- it.rinfo()->IsCallInstruction()) {
+ it.rinfo()->IsPatchedReturnSequence()) {
v->VisitDebugTarget(it.rinfo());
#endif
} else if (rmode == RelocInfo::RUNTIME_ENTRY) {
@@ -5047,7 +5048,7 @@ void Code::CopyFrom(const CodeDesc& desc) {
desc.reloc_size);
// unbox handles and relocate
- int delta = instruction_start() - desc.buffer;
+ intptr_t delta = instruction_start() - desc.buffer;
int mode_mask = RelocInfo::kCodeTargetMask |
RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
RelocInfo::kApplyMask;
@@ -6562,6 +6563,10 @@ class RegExpKey : public HashTableKey {
: string_(string),
flags_(Smi::FromInt(flags.value())) { }
+ // Rather than storing the key in the hash table, a pointer to the
+ // stored value is stored where the key should be. IsMatch then
+ // compares the search key to the found object, rather than comparing
+ // a key to a key.
bool IsMatch(Object* obj) {
FixedArray* val = FixedArray::cast(obj);
return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex)))
@@ -7221,6 +7226,8 @@ Object* CompilationCacheTable::PutRegExp(String* src,
CompilationCacheTable* cache =
reinterpret_cast<CompilationCacheTable*>(obj);
int entry = cache->FindInsertionEntry(key.Hash());
+ // We store the value in the key slot, and compare the search key
+ // to the stored value with a custon IsMatch function during lookups.
cache->set(EntryToIndex(entry), value);
cache->set(EntryToIndex(entry) + 1, value);
cache->ElementAdded();
diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h
index c78c73b3e..deb0971b7 100644
--- a/deps/v8/src/objects.h
+++ b/deps/v8/src/objects.h
@@ -32,6 +32,9 @@
#include "code-stubs.h"
#include "smart-pointer.h"
#include "unicode-inl.h"
+#if V8_TARGET_ARCH_ARM
+#include "arm/constants-arm.h"
+#endif
//
// All object types in the V8 JavaScript are described in this file.
@@ -904,10 +907,10 @@ class Object BASE_EMBEDDED {
// Smi represents integer Numbers that can be stored in 31 bits.
// Smis are immediate which means they are NOT allocated in the heap.
-// Smi stands for small integer.
// The this pointer has the following format: [31 bit signed int] 0
-// On 64-bit, the top 32 bits of the pointer is allowed to have any
-// value.
+// For long smis it has the following format:
+// [32 bit signed int] [31 bits zero padding] 0
+// Smi stands for small integer.
class Smi: public Object {
public:
// Returns the integer value.
@@ -921,8 +924,6 @@ class Smi: public Object {
// Returns whether value can be represented in a Smi.
static inline bool IsValid(intptr_t value);
- static inline bool IsIntptrValid(intptr_t);
-
// Casting.
static inline Smi* cast(Object* object);
@@ -933,10 +934,8 @@ class Smi: public Object {
void SmiVerify();
#endif
- static const int kSmiNumBits = 31;
- // Min and max limits for Smi values.
- static const int kMinValue = -(1 << (kSmiNumBits - 1));
- static const int kMaxValue = (1 << (kSmiNumBits - 1)) - 1;
+ static const int kMinValue = (-1 << (kSmiValueSize - 1));
+ static const int kMaxValue = -(kMinValue + 1);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Smi);
@@ -949,10 +948,10 @@ class Smi: public Object {
//
// Failures are a single word, encoded as follows:
// +-------------------------+---+--+--+
-// |rrrrrrrrrrrrrrrrrrrrrrrrr|sss|tt|11|
+// |...rrrrrrrrrrrrrrrrrrrrrr|sss|tt|11|
// +-------------------------+---+--+--+
-// 3 7 6 4 32 10
-// 1
+// 7 6 4 32 10
+//
//
// The low two bits, 0-1, are the failure tag, 11. The next two bits,
// 2-3, are a failure type tag 'tt' with possible values:
@@ -1014,8 +1013,8 @@ class Failure: public Object {
#endif
private:
- inline int value() const;
- static inline Failure* Construct(Type type, int value = 0);
+ inline intptr_t value() const;
+ static inline Failure* Construct(Type type, intptr_t value = 0);
DISALLOW_IMPLICIT_CONSTRUCTORS(Failure);
};
@@ -1291,7 +1290,7 @@ class HeapNumber: public HeapObject {
// 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) || __ARM_EABI__
+#if !defined(V8_HOST_ARCH_ARM) || defined(USE_ARM_EABI)
static const int kMantissaOffset = kValueOffset;
static const int kExponentOffset = kValueOffset + 4;
#else
@@ -2036,33 +2035,33 @@ class DescriptorArray: public FixedArray {
// // The Element size indicates number of elements per entry.
// static const int kEntrySize = ..;
// };
-// table. The prefix size indicates an amount of memory in the
+// The prefix size indicates an amount of memory in the
// beginning of the backing storage that can be used for non-element
// information by subclasses.
template<typename Shape, typename Key>
class HashTable: public FixedArray {
public:
- // Returns the number of elements in the dictionary.
+ // Returns the number of elements in the hash table.
int NumberOfElements() {
return Smi::cast(get(kNumberOfElementsIndex))->value();
}
- // Returns the capacity of the dictionary.
+ // Returns the capacity of the hash table.
int Capacity() {
return Smi::cast(get(kCapacityIndex))->value();
}
// ElementAdded should be called whenever an element is added to a
- // dictionary.
+ // hash table.
void ElementAdded() { SetNumberOfElements(NumberOfElements() + 1); }
// ElementRemoved should be called whenever an element is removed from
- // a dictionary.
+ // a hash table.
void ElementRemoved() { SetNumberOfElements(NumberOfElements() - 1); }
void ElementsRemoved(int n) { SetNumberOfElements(NumberOfElements() - n); }
- // Returns a new array for dictionary usage. Might return Failure.
+ // Returns a new HashTable object. Might return Failure.
static Object* Allocate(int at_least_space_for);
// Returns the key at entry.
@@ -2112,7 +2111,7 @@ class HashTable: public FixedArray {
return (entry * kEntrySize) + kElementsStartIndex;
}
- // Update the number of elements in the dictionary.
+ // Update the number of elements in the hash table.
void SetNumberOfElements(int nof) {
fast_set(this, kNumberOfElementsIndex, Smi::FromInt(nof));
}
@@ -2148,7 +2147,7 @@ class HashTableKey {
virtual uint32_t Hash() = 0;
// Returns the hash value for object.
virtual uint32_t HashForObject(Object* key) = 0;
- // Returns the key object for storing into the dictionary.
+ // Returns the key object for storing into the hash table.
// If allocations fails a failure object is returned.
virtual Object* AsObject() = 0;
// Required.
@@ -2495,6 +2494,9 @@ class PixelArray: public Array {
void PixelArrayVerify();
#endif // DEBUG
+ // Maximal acceptable length for a pixel array.
+ static const int kMaxLength = 0x3fffffff;
+
// PixelArray headers are not quadword aligned.
static const int kExternalPointerOffset = Array::kAlignedSize;
static const int kHeaderSize = kExternalPointerOffset + kPointerSize;
@@ -3576,6 +3578,7 @@ class CompilationCacheShape {
static const int kEntrySize = 2;
};
+
class CompilationCacheTable: public HashTable<CompilationCacheShape,
HashTableKey*> {
public:
@@ -3849,6 +3852,8 @@ class String: public HeapObject {
static const int kShortLengthShift = kHashShift + kShortStringTag;
static const int kMediumLengthShift = kHashShift + kMediumStringTag;
static const int kLongLengthShift = kHashShift + kLongStringTag;
+ // Maximal string length that can be stored in the hash/length field.
+ static const int kMaxLength = (1 << (32 - kLongLengthShift)) - 1;
// Limit for truncation in short printing.
static const int kMaxShortPrintLength = 1024;
diff --git a/deps/v8/src/parser.cc b/deps/v8/src/parser.cc
index 3b246878e..02fcfdc59 100644
--- a/deps/v8/src/parser.cc
+++ b/deps/v8/src/parser.cc
@@ -177,8 +177,8 @@ class Parser {
Statement* ParseWithStatement(ZoneStringList* labels, bool* ok);
CaseClause* ParseCaseClause(bool* default_seen_ptr, bool* ok);
SwitchStatement* ParseSwitchStatement(ZoneStringList* labels, bool* ok);
- LoopStatement* ParseDoStatement(ZoneStringList* labels, bool* ok);
- LoopStatement* ParseWhileStatement(ZoneStringList* labels, bool* ok);
+ DoWhileStatement* ParseDoWhileStatement(ZoneStringList* labels, bool* ok);
+ WhileStatement* ParseWhileStatement(ZoneStringList* labels, bool* ok);
Statement* ParseForStatement(ZoneStringList* labels, bool* ok);
Statement* ParseThrowStatement(bool* ok);
Expression* MakeCatchContext(Handle<String> id, VariableProxy* value);
@@ -675,9 +675,6 @@ class TemporaryScope BASE_EMBEDDED {
}
int materialized_literal_count() { return materialized_literal_count_; }
- void set_contains_array_literal() { contains_array_literal_ = true; }
- bool contains_array_literal() { return contains_array_literal_; }
-
void SetThisPropertyAssignmentInfo(
bool only_this_property_assignments,
bool only_simple_this_property_assignments,
@@ -700,17 +697,11 @@ class TemporaryScope BASE_EMBEDDED {
void AddProperty() { expected_property_count_++; }
int expected_property_count() { return expected_property_count_; }
private:
- // Captures the number of nodes that need materialization in the
- // function. regexp literals, and boilerplate for object literals.
+ // Captures the number of literals that need materialization in the
+ // function. Includes regexp literals, and boilerplate for object
+ // and array literals.
int materialized_literal_count_;
- // Captures whether or not the function contains array literals. If
- // the function contains array literals, we have to allocate space
- // for the array constructor in the literals array of the function.
- // This array constructor is used when creating the actual array
- // literals.
- bool contains_array_literal_;
-
// Properties count estimation.
int expected_property_count_;
@@ -728,7 +719,6 @@ class TemporaryScope BASE_EMBEDDED {
TemporaryScope::TemporaryScope(Parser* parser)
: materialized_literal_count_(0),
- contains_array_literal_(false),
expected_property_count_(0),
only_this_property_assignments_(false),
only_simple_this_property_assignments_(false),
@@ -1236,7 +1226,6 @@ FunctionLiteral* Parser::ParseProgram(Handle<String> source,
top_scope_,
body.elements(),
temp_scope.materialized_literal_count(),
- temp_scope.contains_array_literal(),
temp_scope.expected_property_count(),
temp_scope.only_this_property_assignments(),
temp_scope.only_simple_this_property_assignments(),
@@ -1692,7 +1681,7 @@ Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) {
break;
case Token::DO:
- stmt = ParseDoStatement(labels, ok);
+ stmt = ParseDoWhileStatement(labels, ok);
break;
case Token::WHILE:
@@ -1903,7 +1892,7 @@ Statement* Parser::ParseNativeDeclaration(bool* ok) {
const int literals = fun->NumberOfLiterals();
Handle<Code> code = Handle<Code>(fun->shared()->code());
Handle<JSFunction> boilerplate =
- Factory::NewFunctionBoilerplate(name, literals, false, code);
+ Factory::NewFunctionBoilerplate(name, literals, code);
// Copy the function data to the boilerplate. Used by
// builtins.cc:HandleApiCall to perform argument type checks and to
@@ -2361,7 +2350,7 @@ Block* Parser::WithHelper(Expression* obj,
exit->AddStatement(NEW(WithExitStatement()));
// Return a try-finally statement.
- TryFinally* wrapper = NEW(TryFinally(body, exit));
+ TryFinallyStatement* wrapper = NEW(TryFinallyStatement(body, exit));
wrapper->set_escaping_targets(collector.targets());
result->AddStatement(wrapper);
}
@@ -2537,7 +2526,8 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
// 'try { try { } catch { } } finally { }'
if (!is_pre_parsing_ && catch_block != NULL && finally_block != NULL) {
- TryCatch* statement = NEW(TryCatch(try_block, catch_var, catch_block));
+ TryCatchStatement* statement =
+ NEW(TryCatchStatement(try_block, catch_var, catch_block));
statement->set_escaping_targets(collector.targets());
try_block = NEW(Block(NULL, 1, false));
try_block->AddStatement(statement);
@@ -2548,11 +2538,11 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
if (!is_pre_parsing_) {
if (catch_block != NULL) {
ASSERT(finally_block == NULL);
- result = NEW(TryCatch(try_block, catch_var, catch_block));
+ result = NEW(TryCatchStatement(try_block, catch_var, catch_block));
result->set_escaping_targets(collector.targets());
} else {
ASSERT(finally_block != NULL);
- result = NEW(TryFinally(try_block, finally_block));
+ result = NEW(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));
@@ -2565,11 +2555,12 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
}
-LoopStatement* Parser::ParseDoStatement(ZoneStringList* labels, bool* ok) {
+DoWhileStatement* Parser::ParseDoWhileStatement(ZoneStringList* labels,
+ bool* ok) {
// DoStatement ::
// 'do' Statement 'while' '(' Expression ')' ';'
- LoopStatement* loop = NEW(LoopStatement(labels, LoopStatement::DO_LOOP));
+ DoWhileStatement* loop = NEW(DoWhileStatement(labels));
Target target(this, loop);
Expect(Token::DO, CHECK_OK);
@@ -2585,16 +2576,16 @@ LoopStatement* Parser::ParseDoStatement(ZoneStringList* labels, bool* ok) {
// ExpectSemicolon() functionality here.
if (peek() == Token::SEMICOLON) Consume(Token::SEMICOLON);
- if (loop) loop->Initialize(NULL, cond, NULL, body);
+ if (loop != NULL) loop->Initialize(cond, body);
return loop;
}
-LoopStatement* Parser::ParseWhileStatement(ZoneStringList* labels, bool* ok) {
+WhileStatement* Parser::ParseWhileStatement(ZoneStringList* labels, bool* ok) {
// WhileStatement ::
// 'while' '(' Expression ')' Statement
- LoopStatement* loop = NEW(LoopStatement(labels, LoopStatement::WHILE_LOOP));
+ WhileStatement* loop = NEW(WhileStatement(labels));
Target target(this, loop);
Expect(Token::WHILE, CHECK_OK);
@@ -2603,7 +2594,7 @@ LoopStatement* Parser::ParseWhileStatement(ZoneStringList* labels, bool* ok) {
Expect(Token::RPAREN, CHECK_OK);
Statement* body = ParseStatement(NULL, CHECK_OK);
- if (loop) loop->Initialize(NULL, cond, NULL, body);
+ if (loop != NULL) loop->Initialize(cond, body);
return loop;
}
@@ -2676,7 +2667,7 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
}
// Standard 'for' loop
- LoopStatement* loop = NEW(LoopStatement(labels, LoopStatement::FOR_LOOP));
+ ForStatement* loop = NEW(ForStatement(labels));
Target target(this, loop);
// Parsed initializer at this point.
@@ -3304,7 +3295,6 @@ Expression* Parser::ParseArrayLiteral(bool* ok) {
Expect(Token::RBRACK, CHECK_OK);
// Update the scope information before the pre-parsing bailout.
- temp_scope_->set_contains_array_literal();
int literal_index = temp_scope_->NextMaterializedLiteralIndex();
if (is_pre_parsing_) return NULL;
@@ -3634,7 +3624,6 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
int materialized_literal_count;
int expected_property_count;
- bool contains_array_literal;
bool only_this_property_assignments;
bool only_simple_this_property_assignments;
Handle<FixedArray> this_property_assignments;
@@ -3648,12 +3637,10 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
only_this_property_assignments = false;
only_simple_this_property_assignments = false;
this_property_assignments = Factory::empty_fixed_array();
- contains_array_literal = entry.contains_array_literal();
} else {
ParseSourceElements(&body, Token::RBRACE, CHECK_OK);
materialized_literal_count = temp_scope.materialized_literal_count();
expected_property_count = temp_scope.expected_property_count();
- contains_array_literal = temp_scope.contains_array_literal();
only_this_property_assignments =
temp_scope.only_this_property_assignments();
only_simple_this_property_assignments =
@@ -3669,7 +3656,6 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
entry.set_end_pos(end_pos);
entry.set_literal_count(materialized_literal_count);
entry.set_property_count(expected_property_count);
- entry.set_contains_array_literal(contains_array_literal);
}
FunctionLiteral* function_literal =
@@ -3677,7 +3663,6 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
top_scope_,
body.elements(),
materialized_literal_count,
- contains_array_literal,
expected_property_count,
only_this_property_assignments,
only_simple_this_property_assignments,
diff --git a/deps/v8/src/parser.h b/deps/v8/src/parser.h
index 86e1f74fb..7328e8119 100644
--- a/deps/v8/src/parser.h
+++ b/deps/v8/src/parser.h
@@ -70,16 +70,9 @@ class FunctionEntry BASE_EMBEDDED {
int property_count() { return backing_[kPropertyCountOffset]; }
void set_property_count(int value) { backing_[kPropertyCountOffset] = value; }
- bool contains_array_literal() {
- return backing_[kContainsArrayLiteralOffset] != 0;
- }
- void set_contains_array_literal(bool value) {
- backing_[kContainsArrayLiteralOffset] = value ? 1 : 0;
- }
-
bool is_valid() { return backing_.length() > 0; }
- static const int kSize = 5;
+ static const int kSize = 4;
private:
Vector<unsigned> backing_;
@@ -87,7 +80,6 @@ class FunctionEntry BASE_EMBEDDED {
static const int kEndPosOffset = 1;
static const int kLiteralCountOffset = 2;
static const int kPropertyCountOffset = 3;
- static const int kContainsArrayLiteralOffset = 4;
};
diff --git a/deps/v8/src/platform-win32.cc b/deps/v8/src/platform-win32.cc
index d4a183d94..26e5ce524 100644
--- a/deps/v8/src/platform-win32.cc
+++ b/deps/v8/src/platform-win32.cc
@@ -1794,7 +1794,6 @@ class Sampler::PlatformData : public Malloced {
context.ContextFlags = CONTEXT_FULL;
if (GetThreadContext(profiled_thread_, &context) != 0) {
#if V8_HOST_ARCH_X64
- UNIMPLEMENTED();
sample.pc = context.Rip;
sample.sp = context.Rsp;
sample.fp = context.Rbp;
diff --git a/deps/v8/src/prettyprinter.cc b/deps/v8/src/prettyprinter.cc
index bf66c4b5c..10c1ea871 100644
--- a/deps/v8/src/prettyprinter.cc
+++ b/deps/v8/src/prettyprinter.cc
@@ -147,46 +147,42 @@ void PrettyPrinter::VisitSwitchStatement(SwitchStatement* node) {
}
-void PrettyPrinter::VisitLoopStatement(LoopStatement* node) {
+void PrettyPrinter::VisitDoWhileStatement(DoWhileStatement* node) {
PrintLabels(node->labels());
- switch (node->type()) {
- case LoopStatement::DO_LOOP:
- ASSERT(node->init() == NULL);
- ASSERT(node->next() == NULL);
- Print("do ");
- Visit(node->body());
- Print(" while (");
- Visit(node->cond());
- Print(");");
- break;
+ Print("do ");
+ Visit(node->body());
+ Print(" while (");
+ Visit(node->cond());
+ Print(");");
+}
- case LoopStatement::FOR_LOOP:
- Print("for (");
- if (node->init() != NULL) {
- Visit(node->init());
- Print(" ");
- } else {
- Print("; ");
- }
- if (node->cond() != NULL)
- Visit(node->cond());
- Print("; ");
- if (node->next() != NULL)
- Visit(node->next()); // prints extra ';', unfortunately
- // to fix: should use Expression for next
- Print(") ");
- Visit(node->body());
- break;
- case LoopStatement::WHILE_LOOP:
- ASSERT(node->init() == NULL);
- ASSERT(node->next() == NULL);
- Print("while (");
- Visit(node->cond());
- Print(") ");
- Visit(node->body());
- break;
+void PrettyPrinter::VisitWhileStatement(WhileStatement* node) {
+ PrintLabels(node->labels());
+ Print("while (");
+ Visit(node->cond());
+ Print(") ");
+ Visit(node->body());
+}
+
+
+void PrettyPrinter::VisitForStatement(ForStatement* node) {
+ PrintLabels(node->labels());
+ Print("for (");
+ if (node->init() != NULL) {
+ Visit(node->init());
+ Print(" ");
+ } else {
+ Print("; ");
+ }
+ if (node->cond() != NULL) Visit(node->cond());
+ Print("; ");
+ if (node->next() != NULL) {
+ Visit(node->next()); // prints extra ';', unfortunately
+ // to fix: should use Expression for next
}
+ Print(") ");
+ Visit(node->body());
}
@@ -201,7 +197,7 @@ void PrettyPrinter::VisitForInStatement(ForInStatement* node) {
}
-void PrettyPrinter::VisitTryCatch(TryCatch* node) {
+void PrettyPrinter::VisitTryCatchStatement(TryCatchStatement* node) {
Print("try ");
Visit(node->try_block());
Print(" catch (");
@@ -211,7 +207,7 @@ void PrettyPrinter::VisitTryCatch(TryCatch* node) {
}
-void PrettyPrinter::VisitTryFinally(TryFinally* node) {
+void PrettyPrinter::VisitTryFinallyStatement(TryFinallyStatement* node) {
Print("try ");
Visit(node->try_block());
Print(" finally ");
@@ -841,12 +837,28 @@ void AstPrinter::VisitSwitchStatement(SwitchStatement* node) {
}
-void AstPrinter::VisitLoopStatement(LoopStatement* node) {
- IndentedScope indent(node->OperatorString());
+void AstPrinter::VisitDoWhileStatement(DoWhileStatement* node) {
+ IndentedScope indent("DO");
+ PrintLabelsIndented(NULL, node->labels());
+ PrintIndentedVisit("BODY", node->body());
+ PrintIndentedVisit("COND", node->cond());
+}
+
+
+void AstPrinter::VisitWhileStatement(WhileStatement* node) {
+ IndentedScope indent("WHILE");
+ PrintLabelsIndented(NULL, node->labels());
+ PrintIndentedVisit("COND", node->cond());
+ PrintIndentedVisit("BODY", node->body());
+}
+
+
+void AstPrinter::VisitForStatement(ForStatement* node) {
+ IndentedScope indent("FOR");
PrintLabelsIndented(NULL, node->labels());
if (node->init()) PrintIndentedVisit("INIT", node->init());
if (node->cond()) PrintIndentedVisit("COND", node->cond());
- if (node->body()) PrintIndentedVisit("BODY", node->body());
+ PrintIndentedVisit("BODY", node->body());
if (node->next()) PrintIndentedVisit("NEXT", node->next());
}
@@ -859,7 +871,7 @@ void AstPrinter::VisitForInStatement(ForInStatement* node) {
}
-void AstPrinter::VisitTryCatch(TryCatch* node) {
+void AstPrinter::VisitTryCatchStatement(TryCatchStatement* node) {
IndentedScope indent("TRY CATCH");
PrintIndentedVisit("TRY", node->try_block());
PrintIndentedVisit("CATCHVAR", node->catch_var());
@@ -867,7 +879,7 @@ void AstPrinter::VisitTryCatch(TryCatch* node) {
}
-void AstPrinter::VisitTryFinally(TryFinally* node) {
+void AstPrinter::VisitTryFinallyStatement(TryFinallyStatement* node) {
IndentedScope indent("TRY FINALLY");
PrintIndentedVisit("TRY", node->try_block());
PrintIndentedVisit("FINALLY", node->finally_block());
@@ -1088,6 +1100,414 @@ void AstPrinter::VisitThisFunction(ThisFunction* node) {
}
+TagScope::TagScope(JsonAstBuilder* builder, const char* name)
+ : builder_(builder), next_(builder->tag()), has_body_(false) {
+ if (next_ != NULL) {
+ next_->use();
+ builder->Print(",\n");
+ }
+ builder->set_tag(this);
+ builder->PrintIndented("[");
+ builder->Print("\"%s\"", name);
+ builder->increase_indent(JsonAstBuilder::kTagIndentSize);
+}
+
+
+TagScope::~TagScope() {
+ builder_->decrease_indent(JsonAstBuilder::kTagIndentSize);
+ if (has_body_) {
+ builder_->Print("\n");
+ builder_->PrintIndented("]");
+ } else {
+ builder_->Print("]");
+ }
+ builder_->set_tag(next_);
+}
+
+
+AttributesScope::AttributesScope(JsonAstBuilder* builder)
+ : builder_(builder), attribute_count_(0) {
+ builder->set_attributes(this);
+ builder->tag()->use();
+ builder->Print(",\n");
+ builder->PrintIndented("{");
+ builder->increase_indent(JsonAstBuilder::kAttributesIndentSize);
+}
+
+
+AttributesScope::~AttributesScope() {
+ builder_->decrease_indent(JsonAstBuilder::kAttributesIndentSize);
+ if (attribute_count_ > 1) {
+ builder_->Print("\n");
+ builder_->PrintIndented("}");
+ } else {
+ builder_->Print("}");
+ }
+ builder_->set_attributes(NULL);
+}
+
+
+const char* JsonAstBuilder::BuildProgram(FunctionLiteral* program) {
+ Init();
+ Visit(program);
+ Print("\n");
+ return Output();
+}
+
+
+void JsonAstBuilder::AddAttributePrefix(const char* name) {
+ if (attributes()->is_used()) {
+ Print(",\n");
+ PrintIndented("\"");
+ } else {
+ Print("\"");
+ }
+ Print("%s\":", name);
+ attributes()->use();
+}
+
+
+void JsonAstBuilder::AddAttribute(const char* name, Handle<String> value) {
+ SmartPointer<char> value_string = value->ToCString();
+ AddAttributePrefix(name);
+ Print("\"%s\"", *value_string);
+}
+
+
+void JsonAstBuilder::AddAttribute(const char* name, const char* value) {
+ AddAttributePrefix(name);
+ Print("\"%s\"", value);
+}
+
+
+void JsonAstBuilder::AddAttribute(const char* name, int value) {
+ AddAttributePrefix(name);
+ Print("%d", value);
+}
+
+
+void JsonAstBuilder::AddAttribute(const char* name, bool value) {
+ AddAttributePrefix(name);
+ Print(value ? "true" : "false");
+}
+
+
+void JsonAstBuilder::VisitBlock(Block* stmt) {
+ TagScope tag(this, "Block");
+ VisitStatements(stmt->statements());
+}
+
+
+void JsonAstBuilder::VisitExpressionStatement(ExpressionStatement* stmt) {
+ TagScope tag(this, "ExpressionStatement");
+ Visit(stmt->expression());
+}
+
+
+void JsonAstBuilder::VisitEmptyStatement(EmptyStatement* stmt) {
+ TagScope tag(this, "EmptyStatement");
+}
+
+
+void JsonAstBuilder::VisitIfStatement(IfStatement* stmt) {
+ TagScope tag(this, "IfStatement");
+ Visit(stmt->condition());
+ Visit(stmt->then_statement());
+ Visit(stmt->else_statement());
+}
+
+
+void JsonAstBuilder::VisitContinueStatement(ContinueStatement* stmt) {
+ TagScope tag(this, "ContinueStatement");
+}
+
+
+void JsonAstBuilder::VisitBreakStatement(BreakStatement* stmt) {
+ TagScope tag(this, "BreakStatement");
+}
+
+
+void JsonAstBuilder::VisitReturnStatement(ReturnStatement* stmt) {
+ TagScope tag(this, "ReturnStatement");
+ Visit(stmt->expression());
+}
+
+
+void JsonAstBuilder::VisitWithEnterStatement(WithEnterStatement* stmt) {
+ TagScope tag(this, "WithEnterStatement");
+ Visit(stmt->expression());
+}
+
+
+void JsonAstBuilder::VisitWithExitStatement(WithExitStatement* stmt) {
+ TagScope tag(this, "WithExitStatement");
+}
+
+
+void JsonAstBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
+ TagScope tag(this, "SwitchStatement");
+}
+
+
+void JsonAstBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
+ TagScope tag(this, "DoWhileStatement");
+ Visit(stmt->body());
+ Visit(stmt->cond());
+}
+
+
+void JsonAstBuilder::VisitWhileStatement(WhileStatement* stmt) {
+ TagScope tag(this, "WhileStatement");
+ Visit(stmt->cond());
+ Visit(stmt->body());
+}
+
+
+void JsonAstBuilder::VisitForStatement(ForStatement* stmt) {
+ TagScope tag(this, "ForStatement");
+ if (stmt->init() != NULL) Visit(stmt->init());
+ if (stmt->cond() != NULL) Visit(stmt->cond());
+ Visit(stmt->body());
+ if (stmt->next() != NULL) Visit(stmt->next());
+}
+
+
+void JsonAstBuilder::VisitForInStatement(ForInStatement* stmt) {
+ TagScope tag(this, "ForInStatement");
+ Visit(stmt->each());
+ Visit(stmt->enumerable());
+ Visit(stmt->body());
+}
+
+
+void JsonAstBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
+ TagScope tag(this, "TryCatchStatement");
+ Visit(stmt->try_block());
+ Visit(stmt->catch_var());
+ Visit(stmt->catch_block());
+}
+
+
+void JsonAstBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
+ TagScope tag(this, "TryFinallyStatement");
+ Visit(stmt->try_block());
+ Visit(stmt->finally_block());
+}
+
+
+void JsonAstBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) {
+ TagScope tag(this, "DebuggerStatement");
+}
+
+
+void JsonAstBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
+ TagScope tag(this, "FunctionLiteral");
+ {
+ AttributesScope attributes(this);
+ AddAttribute("name", expr->name());
+ }
+ VisitDeclarations(expr->scope()->declarations());
+ VisitStatements(expr->body());
+}
+
+
+void JsonAstBuilder::VisitFunctionBoilerplateLiteral(
+ FunctionBoilerplateLiteral* expr) {
+ TagScope tag(this, "FunctionBoilerplateLiteral");
+}
+
+
+void JsonAstBuilder::VisitConditional(Conditional* expr) {
+ TagScope tag(this, "Conditional");
+}
+
+
+void JsonAstBuilder::VisitSlot(Slot* expr) {
+ TagScope tag(this, "Slot");
+ {
+ AttributesScope attributes(this);
+ switch (expr->type()) {
+ case Slot::PARAMETER:
+ AddAttribute("type", "PARAMETER");
+ break;
+ case Slot::LOCAL:
+ AddAttribute("type", "LOCAL");
+ break;
+ case Slot::CONTEXT:
+ AddAttribute("type", "CONTEXT");
+ break;
+ case Slot::LOOKUP:
+ AddAttribute("type", "LOOKUP");
+ break;
+ case Slot::GLOBAL:
+ AddAttribute("type", "GLOBAL");
+ break;
+ }
+ AddAttribute("index", expr->index());
+ }
+}
+
+
+void JsonAstBuilder::VisitVariableProxy(VariableProxy* expr) {
+ if (expr->var()->rewrite() == NULL) {
+ TagScope tag(this, "VariableProxy");
+ {
+ AttributesScope attributes(this);
+ AddAttribute("name", expr->name());
+ AddAttribute("mode", Variable::Mode2String(expr->var()->mode()));
+ }
+ } else {
+ Visit(expr->var()->rewrite());
+ }
+}
+
+
+void JsonAstBuilder::VisitLiteral(Literal* expr) {
+ TagScope tag(this, "Literal");
+ {
+ AttributesScope attributes(this);
+ Handle<Object> handle = expr->handle();
+ if (handle->IsString()) {
+ AddAttribute("handle", Handle<String>(String::cast(*handle)));
+ } else if (handle->IsSmi()) {
+ AddAttribute("handle", Smi::cast(*handle)->value());
+ }
+ }
+}
+
+
+void JsonAstBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
+ TagScope tag(this, "RegExpLiteral");
+}
+
+
+void JsonAstBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
+ TagScope tag(this, "ObjectLiteral");
+}
+
+
+void JsonAstBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
+ TagScope tag(this, "ArrayLiteral");
+}
+
+
+void JsonAstBuilder::VisitCatchExtensionObject(CatchExtensionObject* expr) {
+ TagScope tag(this, "CatchExtensionObject");
+ Visit(expr->key());
+ Visit(expr->value());
+}
+
+
+void JsonAstBuilder::VisitAssignment(Assignment* expr) {
+ TagScope tag(this, "Assignment");
+ {
+ AttributesScope attributes(this);
+ AddAttribute("op", Token::Name(expr->op()));
+ }
+ Visit(expr->target());
+ Visit(expr->value());
+}
+
+
+void JsonAstBuilder::VisitThrow(Throw* expr) {
+ TagScope tag(this, "Throw");
+ Visit(expr->exception());
+}
+
+
+void JsonAstBuilder::VisitProperty(Property* expr) {
+ TagScope tag(this, "Property");
+ {
+ AttributesScope attributes(this);
+ AddAttribute("type", expr->is_synthetic() ? "SYNTHETIC" : "NORMAL");
+ }
+ Visit(expr->obj());
+ Visit(expr->key());
+}
+
+
+void JsonAstBuilder::VisitCall(Call* expr) {
+ TagScope tag(this, "Call");
+ Visit(expr->expression());
+ VisitExpressions(expr->arguments());
+}
+
+
+void JsonAstBuilder::VisitCallNew(CallNew* expr) {
+ TagScope tag(this, "CallNew");
+ Visit(expr->expression());
+ VisitExpressions(expr->arguments());
+}
+
+
+void JsonAstBuilder::VisitCallRuntime(CallRuntime* expr) {
+ TagScope tag(this, "CallRuntime");
+ {
+ AttributesScope attributes(this);
+ AddAttribute("name", expr->name());
+ }
+ VisitExpressions(expr->arguments());
+}
+
+
+void JsonAstBuilder::VisitUnaryOperation(UnaryOperation* expr) {
+ TagScope tag(this, "UnaryOperation");
+ {
+ AttributesScope attributes(this);
+ AddAttribute("op", Token::Name(expr->op()));
+ }
+ Visit(expr->expression());
+}
+
+
+void JsonAstBuilder::VisitCountOperation(CountOperation* expr) {
+ TagScope tag(this, "CountOperation");
+ {
+ AttributesScope attributes(this);
+ AddAttribute("is_prefix", expr->is_prefix());
+ AddAttribute("op", Token::Name(expr->op()));
+ }
+ Visit(expr->expression());
+}
+
+
+void JsonAstBuilder::VisitBinaryOperation(BinaryOperation* expr) {
+ TagScope tag(this, "BinaryOperation");
+ {
+ AttributesScope attributes(this);
+ AddAttribute("op", Token::Name(expr->op()));
+ }
+ Visit(expr->left());
+ Visit(expr->right());
+}
+
+
+void JsonAstBuilder::VisitCompareOperation(CompareOperation* expr) {
+ TagScope tag(this, "CompareOperation");
+ {
+ AttributesScope attributes(this);
+ AddAttribute("op", Token::Name(expr->op()));
+ }
+ Visit(expr->left());
+ Visit(expr->right());
+}
+
+
+void JsonAstBuilder::VisitThisFunction(ThisFunction* expr) {
+ TagScope tag(this, "ThisFunction");
+}
+
+
+void JsonAstBuilder::VisitDeclaration(Declaration* decl) {
+ TagScope tag(this, "Declaration");
+ {
+ AttributesScope attributes(this);
+ AddAttribute("mode", Variable::Mode2String(decl->mode()));
+ }
+ Visit(decl->proxy());
+ if (decl->fun() != NULL) Visit(decl->fun());
+}
+
#endif // DEBUG
diff --git a/deps/v8/src/prettyprinter.h b/deps/v8/src/prettyprinter.h
index 8a6d1fbf8..f885cb31f 100644
--- a/deps/v8/src/prettyprinter.h
+++ b/deps/v8/src/prettyprinter.h
@@ -46,14 +46,15 @@ class PrettyPrinter: public AstVisitor {
const char* PrintExpression(FunctionLiteral* program);
const char* PrintProgram(FunctionLiteral* program);
+ void Print(const char* format, ...);
+
// Print a node to stdout.
static void PrintOut(AstNode* node);
// Individual nodes
-#define DEF_VISIT(type) \
- virtual void Visit##type(type* node);
- AST_NODE_LIST(DEF_VISIT)
-#undef DEF_VISIT
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+ AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
private:
char* output_; // output string buffer
@@ -62,7 +63,6 @@ class PrettyPrinter: public AstVisitor {
protected:
void Init();
- void Print(const char* format, ...);
const char* Output() const { return output_; }
virtual void PrintStatements(ZoneList<Statement*>* statements);
@@ -85,10 +85,9 @@ class AstPrinter: public PrettyPrinter {
const char* PrintProgram(FunctionLiteral* program);
// Individual nodes
-#define DEF_VISIT(type) \
- virtual void Visit##type(type* node);
- AST_NODE_LIST(DEF_VISIT)
-#undef DEF_VISIT
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+ AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
private:
friend class IndentedScope;
void PrintIndented(const char* txt);
@@ -112,6 +111,107 @@ class AstPrinter: public PrettyPrinter {
static int indent_;
};
+
+// Forward declaration of helper classes.
+class TagScope;
+class AttributesScope;
+
+// Build a C string containing a JSON representation of a function's
+// AST. The representation is based on JsonML (www.jsonml.org).
+class JsonAstBuilder: public PrettyPrinter {
+ public:
+ JsonAstBuilder()
+ : indent_(0), top_tag_scope_(NULL), attributes_scope_(NULL) {
+ }
+ virtual ~JsonAstBuilder() {}
+
+ // Controls the indentation of subsequent lines of a tag body after
+ // the first line.
+ static const int kTagIndentSize = 2;
+
+ // Controls the indentation of subsequent lines of an attributes
+ // blocks's body after the first line.
+ static const int kAttributesIndentSize = 1;
+
+ // Construct a JSON representation of a function literal.
+ const char* BuildProgram(FunctionLiteral* program);
+
+ // Print text indented by the current indentation level.
+ void PrintIndented(const char* text) { Print("%*s%s", indent_, "", text); }
+
+ // Change the indentation level.
+ void increase_indent(int amount) { indent_ += amount; }
+ void decrease_indent(int amount) { indent_ -= amount; }
+
+ // The builder maintains a stack of opened AST node constructors.
+ // Each node constructor corresponds to a JsonML tag.
+ TagScope* tag() { return top_tag_scope_; }
+ void set_tag(TagScope* scope) { top_tag_scope_ = scope; }
+
+ // The builder maintains a pointer to the currently opened attributes
+ // of current AST node or NULL if the attributes are not opened.
+ AttributesScope* attributes() { return attributes_scope_; }
+ void set_attributes(AttributesScope* scope) { attributes_scope_ = scope; }
+
+ // Add an attribute to the currently opened attributes.
+ void AddAttribute(const char* name, Handle<String> value);
+ void AddAttribute(const char* name, const char* value);
+ void AddAttribute(const char* name, int value);
+ void AddAttribute(const char* name, bool value);
+
+ // AST node visit functions.
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+ AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+ private:
+ int indent_;
+ TagScope* top_tag_scope_;
+ AttributesScope* attributes_scope_;
+
+ // Utility function used by AddAttribute implementations.
+ void AddAttributePrefix(const char* name);
+};
+
+
+// The JSON AST builder keeps a stack of open element tags (AST node
+// constructors from the current iteration point to the root of the
+// AST). TagScope is a helper class to manage the opening and closing
+// of tags, the indentation of their bodies, and comma separating their
+// contents.
+class TagScope BASE_EMBEDDED {
+ public:
+ TagScope(JsonAstBuilder* builder, const char* name);
+ ~TagScope();
+
+ void use() { has_body_ = true; }
+
+ private:
+ JsonAstBuilder* builder_;
+ TagScope* next_;
+ bool has_body_;
+};
+
+
+// AttributesScope is a helper class to manage the opening and closing
+// of attribute blocks, the indentation of their bodies, and comma
+// separating their contents. JsonAstBuilder::AddAttribute adds an
+// attribute to the currently open AttributesScope. They cannot be
+// nested so the builder keeps an optional single scope rather than a
+// stack.
+class AttributesScope BASE_EMBEDDED {
+ public:
+ explicit AttributesScope(JsonAstBuilder* builder);
+ ~AttributesScope();
+
+ bool is_used() { return attribute_count_ > 0; }
+ void use() { ++attribute_count_; }
+
+ private:
+ JsonAstBuilder* builder_;
+ int attribute_count_;
+};
+
#endif // DEBUG
} } // namespace v8::internal
diff --git a/deps/v8/src/rewriter.cc b/deps/v8/src/rewriter.cc
index 11fc071f0..de1b95b99 100644
--- a/deps/v8/src/rewriter.cc
+++ b/deps/v8/src/rewriter.cc
@@ -100,7 +100,21 @@ void AstOptimizer::VisitIfStatement(IfStatement* node) {
}
-void AstOptimizer::VisitLoopStatement(LoopStatement* node) {
+void AstOptimizer::VisitDoWhileStatement(DoWhileStatement* node) {
+ Visit(node->cond());
+ Visit(node->body());
+}
+
+
+void AstOptimizer::VisitWhileStatement(WhileStatement* node) {
+ has_function_literal_ = false;
+ Visit(node->cond());
+ node->may_have_function_literal_ = has_function_literal_;
+ Visit(node->body());
+}
+
+
+void AstOptimizer::VisitForStatement(ForStatement* node) {
if (node->init() != NULL) {
Visit(node->init());
}
@@ -109,9 +123,7 @@ void AstOptimizer::VisitLoopStatement(LoopStatement* node) {
Visit(node->cond());
node->may_have_function_literal_ = has_function_literal_;
}
- if (node->body() != NULL) {
- Visit(node->body());
- }
+ Visit(node->body());
if (node->next() != NULL) {
Visit(node->next());
}
@@ -125,14 +137,14 @@ void AstOptimizer::VisitForInStatement(ForInStatement* node) {
}
-void AstOptimizer::VisitTryCatch(TryCatch* node) {
+void AstOptimizer::VisitTryCatchStatement(TryCatchStatement* node) {
Visit(node->try_block());
Visit(node->catch_var());
Visit(node->catch_block());
}
-void AstOptimizer::VisitTryFinally(TryFinally* node) {
+void AstOptimizer::VisitTryFinallyStatement(TryFinallyStatement* node) {
Visit(node->try_block());
Visit(node->finally_block());
}
@@ -553,6 +565,8 @@ class Processor: public AstVisitor {
virtual void Visit##type(type* node);
AST_NODE_LIST(DEF_VISIT)
#undef DEF_VISIT
+
+ void VisitIterationStatement(IterationStatement* stmt);
};
@@ -596,25 +610,35 @@ void Processor::VisitIfStatement(IfStatement* node) {
}
-
-
-void Processor::VisitLoopStatement(LoopStatement* node) {
- // Rewrite loop body statement.
+void Processor::VisitIterationStatement(IterationStatement* node) {
+ // Rewrite the body.
bool set_after_loop = is_set_;
Visit(node->body());
is_set_ = is_set_ && set_after_loop;
}
+void Processor::VisitDoWhileStatement(DoWhileStatement* node) {
+ VisitIterationStatement(node);
+}
+
+
+void Processor::VisitWhileStatement(WhileStatement* node) {
+ VisitIterationStatement(node);
+}
+
+
+void Processor::VisitForStatement(ForStatement* node) {
+ VisitIterationStatement(node);
+}
+
+
void Processor::VisitForInStatement(ForInStatement* node) {
- // Rewrite for-in body statement.
- bool set_after_for = is_set_;
- Visit(node->body());
- is_set_ = is_set_ && set_after_for;
+ VisitIterationStatement(node);
}
-void Processor::VisitTryCatch(TryCatch* node) {
+void Processor::VisitTryCatchStatement(TryCatchStatement* node) {
// Rewrite both try and catch blocks (reversed order).
bool set_after_catch = is_set_;
Visit(node->catch_block());
@@ -626,7 +650,7 @@ void Processor::VisitTryCatch(TryCatch* node) {
}
-void Processor::VisitTryFinally(TryFinally* node) {
+void Processor::VisitTryFinallyStatement(TryFinallyStatement* node) {
// Rewrite both try and finally block (reversed order).
Visit(node->finally_block());
bool save = in_try_;
diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc
index 4e1940d81..9eeffd10c 100644
--- a/deps/v8/src/runtime.cc
+++ b/deps/v8/src/runtime.cc
@@ -34,18 +34,17 @@
#include "arguments.h"
#include "compiler.h"
#include "cpu.h"
-#include "dateparser.h"
#include "dateparser-inl.h"
#include "debug.h"
#include "execution.h"
#include "jsregexp.h"
+#include "parser.h"
#include "platform.h"
#include "runtime.h"
#include "scopeinfo.h"
-#include "v8threads.h"
#include "smart-pointer.h"
-#include "parser.h"
#include "stub-cache.h"
+#include "v8threads.h"
namespace v8 {
namespace internal {
@@ -522,7 +521,7 @@ static Object* Runtime_GetTemplateField(Arguments args) {
RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
type == OBJECT_TEMPLATE_INFO_TYPE);
RUNTIME_ASSERT(offset > 0);
- if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
+ if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
} else {
RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
@@ -3252,8 +3251,8 @@ static Object* Runtime_URIEscape(Arguments args) {
} else {
escaped_length += 3;
}
- // We don't allow strings that are longer than Smi range.
- if (!Smi::IsValid(escaped_length)) {
+ // We don't allow strings that are longer than a maximal length.
+ if (escaped_length > String::kMaxLength) {
Top::context()->mark_out_of_memory();
return Failure::OutOfMemoryException();
}
@@ -3584,6 +3583,36 @@ static Object* Runtime_StringToUpperCase(Arguments args) {
return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
}
+static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
+ return unibrow::WhiteSpace::Is(c) || c == 0x200b;
+}
+
+static Object* Runtime_StringTrim(Arguments args) {
+ NoHandleAllocation ha;
+ ASSERT(args.length() == 3);
+
+ CONVERT_CHECKED(String, s, args[0]);
+ CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
+ CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
+
+ s->TryFlattenIfNotFlat();
+ int length = s->length();
+
+ int left = 0;
+ if (trimLeft) {
+ while (left < length && IsTrimWhiteSpace(s->Get(left))) {
+ left++;
+ }
+ }
+
+ int right = length;
+ if (trimRight) {
+ while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
+ right--;
+ }
+ }
+ return s->Slice(left, right);
+}
bool Runtime::IsUpperCaseChar(uint16_t ch) {
unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
@@ -3804,10 +3833,6 @@ static Object* Runtime_StringBuilderConcat(Arguments args) {
} else if (elt->IsString()) {
String* element = String::cast(elt);
int element_length = element->length();
- if (!Smi::IsValid(element_length + position)) {
- Top::context()->mark_out_of_memory();
- return Failure::OutOfMemoryException();
- }
position += element_length;
if (ascii && !element->IsAsciiRepresentation()) {
ascii = false;
@@ -3815,6 +3840,10 @@ static Object* Runtime_StringBuilderConcat(Arguments args) {
} else {
return Top::Throw(Heap::illegal_argument_symbol());
}
+ if (position > String::kMaxLength) {
+ Top::context()->mark_out_of_memory();
+ return Failure::OutOfMemoryException();
+ }
}
int length = position;
diff --git a/deps/v8/src/runtime.h b/deps/v8/src/runtime.h
index afa278b13..279181d07 100644
--- a/deps/v8/src/runtime.h
+++ b/deps/v8/src/runtime.h
@@ -152,6 +152,7 @@ namespace internal {
F(StringSlice, 3, 1) \
F(StringReplaceRegExpWithString, 4, 1) \
F(StringMatch, 3, 1) \
+ F(StringTrim, 3, 1) \
\
/* Numbers */ \
F(NumberToRadixString, 2, 1) \
diff --git a/deps/v8/src/serialize.cc b/deps/v8/src/serialize.cc
index 94cd02aac..6ff1d7f5b 100644
--- a/deps/v8/src/serialize.cc
+++ b/deps/v8/src/serialize.cc
@@ -38,6 +38,7 @@
#include "serialize.h"
#include "stub-cache.h"
#include "v8threads.h"
+#include "top.h"
namespace v8 {
namespace internal {
@@ -612,12 +613,23 @@ void ExternalReferenceTable::PopulateTable() {
}
// Top addresses
- const char* top_address_format = "Top::get_address_from_id(%i)";
- size_t top_format_length = strlen(top_address_format);
+ const char* top_address_format = "Top::%s";
+
+ const char* AddressNames[] = {
+#define C(name) #name,
+ TOP_ADDRESS_LIST(C)
+ TOP_ADDRESS_LIST_PROF(C)
+ NULL
+#undef C
+ };
+
+ size_t top_format_length = strlen(top_address_format) - 2;
for (uint16_t i = 0; i < Top::k_top_address_count; ++i) {
- Vector<char> name = Vector<char>::New(top_format_length + 1);
+ const char* address_name = AddressNames[i];
+ Vector<char> name =
+ Vector<char>::New(top_format_length + strlen(address_name) + 1);
const char* chars = name.start();
- OS::SNPrintF(name, top_address_format, i);
+ OS::SNPrintF(name, top_address_format, address_name);
Add(Top::get_address_from_id((Top::AddressId)i), TOP_ADDRESS, i, chars);
}
diff --git a/deps/v8/src/string.js b/deps/v8/src/string.js
index fbdc30793..d2d6e969d 100644
--- a/deps/v8/src/string.js
+++ b/deps/v8/src/string.js
@@ -680,6 +680,18 @@ function StringToLocaleUpperCase() {
return %StringToUpperCase(ToString(this));
}
+// ES5, 15.5.4.20
+function StringTrim() {
+ return %StringTrim(ToString(this), true, true);
+}
+
+function StringTrimLeft() {
+ return %StringTrim(ToString(this), true, false);
+}
+
+function StringTrimRight() {
+ return %StringTrim(ToString(this), false, true);
+}
// ECMA-262, section 15.5.3.2
function StringFromCharCode(code) {
@@ -855,6 +867,9 @@ function SetupString() {
"toLocaleLowerCase", StringToLocaleLowerCase,
"toUpperCase", StringToUpperCase,
"toLocaleUpperCase", StringToLocaleUpperCase,
+ "trim", StringTrim,
+ "trimLeft", StringTrimLeft,
+ "trimRight", StringTrimRight,
"link", StringLink,
"anchor", StringAnchor,
"fontcolor", StringFontcolor,
diff --git a/deps/v8/src/top.cc b/deps/v8/src/top.cc
index aa7788e3b..bb2dea4d2 100644
--- a/deps/v8/src/top.cc
+++ b/deps/v8/src/top.cc
@@ -54,6 +54,7 @@ Address Top::get_address_from_id(Top::AddressId id) {
return top_addresses[id];
}
+
char* Top::Iterate(ObjectVisitor* v, char* thread_storage) {
ThreadLocalTop* thread = reinterpret_cast<ThreadLocalTop*>(thread_storage);
Iterate(v, thread);
@@ -493,11 +494,17 @@ static MayAccessDecision MayAccessPreCheck(JSObject* receiver,
bool Top::MayNamedAccess(JSObject* receiver, Object* key, v8::AccessType type) {
ASSERT(receiver->IsAccessCheckNeeded());
+
+ // The callers of this method are not expecting a GC.
+ AssertNoAllocation no_gc;
+
+ // Skip checks for hidden properties access. Note, we do not
+ // require existence of a context in this case.
+ if (key == Heap::hidden_symbol()) return true;
+
// Check for compatibility between the security tokens in the
// current lexical context and the accessed object.
ASSERT(Top::context());
- // The callers of this method are not expecting a GC.
- AssertNoAllocation no_gc;
MayAccessDecision decision = MayAccessPreCheck(receiver, type);
if (decision != UNKNOWN) return decision == YES;
diff --git a/deps/v8/src/usage-analyzer.cc b/deps/v8/src/usage-analyzer.cc
index 23a4d9fcb..74cf9828a 100644
--- a/deps/v8/src/usage-analyzer.cc
+++ b/deps/v8/src/usage-analyzer.cc
@@ -159,14 +159,25 @@ void UsageComputer::VisitSwitchStatement(SwitchStatement* node) {
}
-void UsageComputer::VisitLoopStatement(LoopStatement* node) {
- if (node->init() != NULL)
- Visit(node->init());
+void UsageComputer::VisitDoWhileStatement(DoWhileStatement* node) {
+ WeightScaler ws(this, 10.0);
+ Read(node->cond());
+ Visit(node->body());
+}
+
+
+void UsageComputer::VisitWhileStatement(WhileStatement* node) {
+ WeightScaler ws(this, 10.0);
+ Read(node->cond());
+ Visit(node->body());
+}
+
+
+void UsageComputer::VisitForStatement(ForStatement* node) {
+ if (node->init() != NULL) Visit(node->init());
{ WeightScaler ws(this, 10.0); // executed in each iteration
- if (node->cond() != NULL)
- Read(node->cond());
- if (node->next() != NULL)
- Visit(node->next());
+ if (node->cond() != NULL) Read(node->cond());
+ if (node->next() != NULL) Visit(node->next());
Visit(node->body());
}
}
@@ -180,7 +191,7 @@ void UsageComputer::VisitForInStatement(ForInStatement* node) {
}
-void UsageComputer::VisitTryCatch(TryCatch* node) {
+void UsageComputer::VisitTryCatchStatement(TryCatchStatement* node) {
Visit(node->try_block());
{ WeightScaler ws(this, 0.25);
Write(node->catch_var());
@@ -189,7 +200,7 @@ void UsageComputer::VisitTryCatch(TryCatch* node) {
}
-void UsageComputer::VisitTryFinally(TryFinally* node) {
+void UsageComputer::VisitTryFinallyStatement(TryFinallyStatement* node) {
Visit(node->try_block());
Visit(node->finally_block());
}
diff --git a/deps/v8/src/utils.h b/deps/v8/src/utils.h
index 275dbb5cb..f4a0598c2 100644
--- a/deps/v8/src/utils.h
+++ b/deps/v8/src/utils.h
@@ -36,7 +36,8 @@ namespace internal {
// ----------------------------------------------------------------------------
// General helper functions
-// Returns true iff x is a power of 2. Does not work for zero.
+// Returns true iff x is a power of 2 (or zero). Cannot be used with the
+// maximally negative value of the type T (the -1 overflows).
template <typename T>
static inline bool IsPowerOf2(T x) {
return (x & (x - 1)) == 0;
diff --git a/deps/v8/src/v8-counters.h b/deps/v8/src/v8-counters.h
index e360b5576..84f9ee415 100644
--- a/deps/v8/src/v8-counters.h
+++ b/deps/v8/src/v8-counters.h
@@ -52,8 +52,8 @@ namespace internal {
HT(variable_allocation, V8.VariableAllocation) \
HT(ast_optimization, V8.ASTOptimization) \
HT(code_generation, V8.CodeGeneration) \
- HT(deferred_code_generation, V8.DeferredCodeGeneration) \
- HT(code_creation, V8.CodeCreation)
+ HT(deferred_code_generation, V8.DeferredCodeGeneration)
+
// WARNING: STATS_COUNTER_LIST_* is a very large macro that is causing MSVC
// Intellisense to crash. It was broken into two macros (each of length 40
@@ -150,7 +150,9 @@ namespace internal {
SC(reloc_info_count, V8.RelocInfoCount) \
SC(reloc_info_size, V8.RelocInfoSize) \
SC(zone_segment_bytes, V8.ZoneSegmentBytes) \
- SC(compute_entry_frame, V8.ComputeEntryFrame)
+ SC(compute_entry_frame, V8.ComputeEntryFrame) \
+ SC(generic_binary_stub_calls, V8.GenericBinaryStubCalls) \
+ SC(generic_binary_stub_calls_regs, V8.GenericBinaryStubCallsRegs)
// This file contains all the v8 counters that are in use.
diff --git a/deps/v8/src/v8.cc b/deps/v8/src/v8.cc
index 3f8e6cdea..3c70ee96b 100644
--- a/deps/v8/src/v8.cc
+++ b/deps/v8/src/v8.cc
@@ -178,11 +178,14 @@ bool V8::IdleNotification() {
return Heap::IdleNotification();
}
+static const uint32_t kRandomPositiveSmiMax = 0x3fffffff;
Smi* V8::RandomPositiveSmi() {
uint32_t random = Random();
- ASSERT(IsPowerOf2(Smi::kMaxValue + 1));
- return Smi::FromInt(random & Smi::kMaxValue);
+ ASSERT(static_cast<uint32_t>(Smi::kMaxValue) >= kRandomPositiveSmiMax);
+ // kRandomPositiveSmiMax must match the value being divided
+ // by in math.js.
+ return Smi::FromInt(random & kRandomPositiveSmiMax);
}
} } // namespace v8::internal
diff --git a/deps/v8/src/version.cc b/deps/v8/src/version.cc
index fdbcb58b5..8e2cef673 100644
--- a/deps/v8/src/version.cc
+++ b/deps/v8/src/version.cc
@@ -34,7 +34,7 @@
// cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 1
#define MINOR_VERSION 3
-#define BUILD_NUMBER 15
+#define BUILD_NUMBER 16
#define PATCH_LEVEL 0
#define CANDIDATE_VERSION false
diff --git a/deps/v8/src/x64/assembler-x64-inl.h b/deps/v8/src/x64/assembler-x64-inl.h
index 899a17cd4..8f078ff23 100644
--- a/deps/v8/src/x64/assembler-x64-inl.h
+++ b/deps/v8/src/x64/assembler-x64-inl.h
@@ -38,11 +38,6 @@ Condition NegateCondition(Condition cc) {
return static_cast<Condition>(cc ^ 1);
}
-// -----------------------------------------------------------------------------
-
-Immediate::Immediate(Smi* value) {
- value_ = static_cast<int32_t>(reinterpret_cast<intptr_t>(value));
-}
// -----------------------------------------------------------------------------
// Implementation of Assembler
@@ -199,7 +194,7 @@ void RelocInfo::apply(intptr_t delta) {
Memory::Address_at(pc_) += delta;
} else if (IsCodeTarget(rmode_)) {
Memory::int32_at(pc_) -= delta;
- } else if (rmode_ == JS_RETURN && IsCallInstruction()) {
+ } else if (rmode_ == JS_RETURN && IsPatchedReturnSequence()) {
// Special handling of js_return when a break point is set (call
// instruction has been inserted).
Memory::int32_at(pc_ + 1) -= delta; // relocate entry
@@ -267,45 +262,49 @@ void RelocInfo::set_target_object(Object* target) {
}
-bool RelocInfo::IsCallInstruction() {
+bool RelocInfo::IsPatchedReturnSequence() {
// The recognized call sequence is:
// movq(kScratchRegister, immediate64); call(kScratchRegister);
// It only needs to be distinguished from a return sequence
// movq(rsp, rbp); pop(rbp); ret(n); int3 *6
// The 11th byte is int3 (0xCC) in the return sequence and
// REX.WB (0x48+register bit) for the call sequence.
+#ifdef ENABLE_DEBUGGER_SUPPORT
return pc_[10] != 0xCC;
+#else
+ return false;
+#endif
}
Address RelocInfo::call_address() {
- ASSERT(IsCallInstruction());
+ ASSERT(IsPatchedReturnSequence());
return Memory::Address_at(
pc_ + Assembler::kRealPatchReturnSequenceAddressOffset);
}
void RelocInfo::set_call_address(Address target) {
- ASSERT(IsCallInstruction());
+ ASSERT(IsPatchedReturnSequence());
Memory::Address_at(pc_ + Assembler::kRealPatchReturnSequenceAddressOffset) =
target;
}
Object* RelocInfo::call_object() {
- ASSERT(IsCallInstruction());
+ ASSERT(IsPatchedReturnSequence());
return *call_object_address();
}
void RelocInfo::set_call_object(Object* target) {
- ASSERT(IsCallInstruction());
+ ASSERT(IsPatchedReturnSequence());
*call_object_address() = target;
}
Object** RelocInfo::call_object_address() {
- ASSERT(IsCallInstruction());
+ ASSERT(IsPatchedReturnSequence());
return reinterpret_cast<Object**>(
pc_ + Assembler::kPatchReturnSequenceAddressOffset);
}
diff --git a/deps/v8/src/x64/assembler-x64.cc b/deps/v8/src/x64/assembler-x64.cc
index cf79a435b..3f3d34e77 100644
--- a/deps/v8/src/x64/assembler-x64.cc
+++ b/deps/v8/src/x64/assembler-x64.cc
@@ -708,7 +708,7 @@ void Assembler::shift_32(Register dst, int subcode) {
void Assembler::shift_32(Register dst, Immediate shift_amount, int subcode) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
- ASSERT(is_uint6(shift_amount.value_)); // illegal shift count
+ ASSERT(is_uint5(shift_amount.value_)); // illegal shift count
if (shift_amount.value_ == 1) {
emit_optional_rex_32(dst);
emit(0xD1);
@@ -794,6 +794,12 @@ void Assembler::call(const Operand& op) {
}
+void Assembler::clc() {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ emit(0xF8);
+}
+
void Assembler::cdq() {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@@ -802,6 +808,11 @@ void Assembler::cdq() {
void Assembler::cmovq(Condition cc, Register dst, Register src) {
+ if (cc == always) {
+ movq(dst, src);
+ } else if (cc == never) {
+ return;
+ }
// No need to check CpuInfo for CMOV support, it's a required part of the
// 64-bit architecture.
ASSERT(cc >= 0); // Use mov for unconditional moves.
@@ -816,6 +827,11 @@ void Assembler::cmovq(Condition cc, Register dst, Register src) {
void Assembler::cmovq(Condition cc, Register dst, const Operand& src) {
+ if (cc == always) {
+ movq(dst, src);
+ } else if (cc == never) {
+ return;
+ }
ASSERT(cc >= 0);
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@@ -828,6 +844,11 @@ void Assembler::cmovq(Condition cc, Register dst, const Operand& src) {
void Assembler::cmovl(Condition cc, Register dst, Register src) {
+ if (cc == always) {
+ movl(dst, src);
+ } else if (cc == never) {
+ return;
+ }
ASSERT(cc >= 0);
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@@ -840,6 +861,11 @@ void Assembler::cmovl(Condition cc, Register dst, Register src) {
void Assembler::cmovl(Condition cc, Register dst, const Operand& src) {
+ if (cc == always) {
+ movl(dst, src);
+ } else if (cc == never) {
+ return;
+ }
ASSERT(cc >= 0);
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@@ -913,6 +939,27 @@ void Assembler::decl(const Operand& dst) {
}
+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);
+ }
+ emit(0xFE);
+ emit_modrm(0x1, dst);
+}
+
+
+void Assembler::decb(const Operand& dst) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ emit_optional_rex_32(dst);
+ emit(0xFE);
+ emit_operand(1, dst);
+}
+
+
void Assembler::enter(Immediate size) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@@ -1037,6 +1084,12 @@ void Assembler::int3() {
void Assembler::j(Condition cc, Label* L) {
+ if (cc == always) {
+ jmp(L);
+ return;
+ } else if (cc == never) {
+ return;
+ }
EnsureSpace ensure_space(this);
last_pc_ = pc_;
ASSERT(is_uint4(cc));
@@ -1373,10 +1426,7 @@ void Assembler::movq(Register dst, Handle<Object> value, RelocInfo::Mode mode) {
// There is no possible reason to store a heap pointer without relocation
// info, so it must be a smi.
ASSERT(value->IsSmi());
- // Smis never have more than 32 significant bits, but they might
- // have garbage in the high bits.
- movq(dst,
- Immediate(static_cast<int32_t>(reinterpret_cast<intptr_t>(*value))));
+ movq(dst, reinterpret_cast<int64_t>(*value), RelocInfo::NONE);
} else {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@@ -1650,22 +1700,6 @@ void Assembler::pushfq() {
}
-void Assembler::rcl(Register dst, uint8_t imm8) {
- EnsureSpace ensure_space(this);
- last_pc_ = pc_;
- ASSERT(is_uint6(imm8)); // illegal shift count
- if (imm8 == 1) {
- emit_rex_64(dst);
- emit(0xD1);
- emit_modrm(0x2, dst);
- } else {
- emit_rex_64(dst);
- emit(0xC1);
- emit_modrm(0x2, dst);
- emit(imm8);
- }
-}
-
void Assembler::rdtsc() {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@@ -1689,6 +1723,10 @@ void Assembler::ret(int imm16) {
void Assembler::setcc(Condition cc, Register reg) {
+ if (cc > last_condition) {
+ movb(reg, Immediate(cc == always ? 1 : 0));
+ return;
+ }
EnsureSpace ensure_space(this);
last_pc_ = pc_;
ASSERT(is_uint4(cc));
@@ -1750,6 +1788,18 @@ void Assembler::store_rax(ExternalReference ref) {
}
+void Assembler::testb(Register dst, Register src) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ if (dst.code() > 3 || src.code() > 3) {
+ // Register is not one of al, bl, cl, dl. Its encoding needs REX.
+ emit_rex_32(dst, src);
+ }
+ emit(0x84);
+ emit_modrm(dst, src);
+}
+
+
void Assembler::testb(Register reg, Immediate mask) {
ASSERT(is_int8(mask.value_) || is_uint8(mask.value_));
EnsureSpace ensure_space(this);
diff --git a/deps/v8/src/x64/assembler-x64.h b/deps/v8/src/x64/assembler-x64.h
index e17a55d82..7e09a416b 100644
--- a/deps/v8/src/x64/assembler-x64.h
+++ b/deps/v8/src/x64/assembler-x64.h
@@ -222,13 +222,18 @@ enum Condition {
less_equal = 14,
greater = 15,
+ // Fake conditions that are handled by the
+ // opcodes using them.
+ always = 16,
+ never = 17,
// aliases
carry = below,
not_carry = above_equal,
zero = equal,
not_zero = not_equal,
sign = negative,
- not_sign = positive
+ not_sign = positive,
+ last_condition = greater
};
@@ -284,7 +289,6 @@ inline Hint NegateHint(Hint hint) {
class Immediate BASE_EMBEDDED {
public:
explicit Immediate(int32_t value) : value_(value) {}
- inline explicit Immediate(Smi* value);
private:
int32_t value_;
@@ -372,6 +376,11 @@ class CpuFeatures : public AllStatic {
static void Probe();
// Check whether a feature is supported by the target CPU.
static bool IsSupported(Feature f) {
+ if (f == SSE2 && !FLAG_enable_sse2) return false;
+ if (f == SSE3 && !FLAG_enable_sse3) return false;
+ if (f == CMOV && !FLAG_enable_cmov) return false;
+ if (f == RDTSC && !FLAG_enable_rdtsc) return false;
+ if (f == SAHF && !FLAG_enable_sahf) return false;
return (supported_ & (V8_UINT64_C(1) << f)) != 0;
}
// Check whether a feature is currently enabled.
@@ -699,10 +708,17 @@ class Assembler : public Malloced {
immediate_arithmetic_op_32(0x4, dst, src);
}
+ void andl(Register dst, Register src) {
+ arithmetic_op_32(0x23, dst, src);
+ }
+
+
void decq(Register dst);
void decq(const Operand& dst);
void decl(Register dst);
void decl(const Operand& dst);
+ void decb(Register dst);
+ void decb(const Operand& dst);
// Sign-extends rax into rdx:rax.
void cqo();
@@ -758,12 +774,34 @@ class Assembler : public Malloced {
immediate_arithmetic_op(0x1, dst, src);
}
+ void orl(Register dst, Immediate src) {
+ immediate_arithmetic_op_32(0x1, dst, src);
+ }
+
void or_(const Operand& dst, Immediate src) {
immediate_arithmetic_op(0x1, dst, src);
}
+ void orl(const Operand& dst, Immediate src) {
+ immediate_arithmetic_op_32(0x1, dst, src);
+ }
+
- void rcl(Register dst, uint8_t imm8);
+ void rcl(Register dst, Immediate imm8) {
+ shift(dst, imm8, 0x2);
+ }
+
+ void rol(Register dst, Immediate imm8) {
+ shift(dst, imm8, 0x0);
+ }
+
+ void rcr(Register dst, Immediate imm8) {
+ shift(dst, imm8, 0x3);
+ }
+
+ void ror(Register dst, Immediate imm8) {
+ shift(dst, imm8, 0x1);
+ }
// Shifts dst:src left by cl bits, affecting only dst.
void shld(Register dst, Register src);
@@ -864,6 +902,7 @@ class Assembler : public Malloced {
immediate_arithmetic_op_8(0x5, dst, src);
}
+ void testb(Register dst, Register src);
void testb(Register reg, Immediate mask);
void testb(const Operand& op, Immediate mask);
void testl(Register dst, Register src);
@@ -902,6 +941,7 @@ class Assembler : public Malloced {
void bts(const Operand& dst, Register src);
// Miscellaneous
+ void clc();
void cpuid();
void hlt();
void int3();
diff --git a/deps/v8/src/x64/builtins-x64.cc b/deps/v8/src/x64/builtins-x64.cc
index 35eddc45e..01992ce4f 100644
--- a/deps/v8/src/x64/builtins-x64.cc
+++ b/deps/v8/src/x64/builtins-x64.cc
@@ -53,7 +53,7 @@ static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
__ movq(rbp, rsp);
// Store the arguments adaptor context sentinel.
- __ push(Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ Push(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
// Push the function on the stack.
__ push(rdi);
@@ -75,14 +75,9 @@ static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) {
__ pop(rbp);
// Remove caller arguments from the stack.
- // rbx holds a Smi, so we convery to dword offset by multiplying by 4.
- // TODO(smi): Find a way to abstract indexing by a smi.
- ASSERT_EQ(kSmiTagSize, 1 && kSmiTag == 0);
- ASSERT_EQ(kPointerSize, (1 << kSmiTagSize) * 4);
- // TODO(smi): Find way to abstract indexing by a smi.
__ pop(rcx);
- // 1 * kPointerSize is offset of receiver.
- __ lea(rsp, Operand(rsp, rbx, times_half_pointer_size, 1 * kPointerSize));
+ SmiIndex index = masm->SmiToIndex(rbx, rbx, kPointerSizeLog2);
+ __ lea(rsp, Operand(rsp, index.reg, index.scale, 1 * kPointerSize));
__ push(rcx);
}
@@ -342,7 +337,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
// Because runtime functions always remove the receiver from the stack, we
// have to fake one to avoid underflowing the stack.
__ push(rax);
- __ push(Immediate(Smi::FromInt(0)));
+ __ Push(Smi::FromInt(0));
// Do call to runtime routine.
__ CallRuntime(Runtime::kStackGuard, 1);
@@ -434,7 +429,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
// Update the index on the stack and in register rax.
__ movq(rax, Operand(rbp, kIndexOffset));
- __ addq(rax, Immediate(Smi::FromInt(1)));
+ __ SmiAddConstant(rax, rax, Smi::FromInt(1));
__ movq(Operand(rbp, kIndexOffset), rax);
__ bind(&entry);
@@ -507,7 +502,7 @@ static void AllocateEmptyJSArray(MacroAssembler* masm,
__ Move(FieldOperand(result, JSArray::kPropertiesOffset),
Factory::empty_fixed_array());
// Field JSArray::kElementsOffset is initialized later.
- __ movq(FieldOperand(result, JSArray::kLengthOffset), Immediate(0));
+ __ Move(FieldOperand(result, JSArray::kLengthOffset), Smi::FromInt(0));
// If no storage is requested for the elements array just set the empty
// fixed array.
@@ -718,14 +713,12 @@ static void ArrayNativeCode(MacroAssembler* masm,
__ cmpq(rax, Immediate(1));
__ j(not_equal, &argc_two_or_more);
__ movq(rdx, Operand(rsp, kPointerSize)); // Get the argument from the stack.
- Condition not_positive_smi = __ CheckNotPositiveSmi(rdx);
- __ j(not_positive_smi, call_generic_code);
+ __ JumpIfNotPositiveSmi(rdx, call_generic_code);
// Handle construction of an empty array of a certain size. Bail out if size
// is to large to actually allocate an elements array.
- __ JumpIfSmiGreaterEqualsConstant(rdx,
- JSObject::kInitialMaxFastElementArray,
- call_generic_code);
+ __ SmiCompare(rdx, Smi::FromInt(JSObject::kInitialMaxFastElementArray));
+ __ j(greater_equal, call_generic_code);
// rax: argc
// rdx: array_size (smi)
@@ -825,10 +818,10 @@ void Builtins::Generate_ArrayCode(MacroAssembler* masm) {
__ movq(rbx, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset));
// Will both indicate a NULL and a Smi.
ASSERT(kSmiTag == 0);
- Condition not_smi = __ CheckNotSmi(rbx);
- __ Assert(not_smi, "Unexpected initial map for Array function");
+ Condition not_smi = NegateCondition(masm->CheckSmi(rbx));
+ __ Check(not_smi, "Unexpected initial map for Array function");
__ CmpObjectType(rbx, MAP_TYPE, rcx);
- __ Assert(equal, "Unexpected initial map for Array function");
+ __ Check(equal, "Unexpected initial map for Array function");
}
// Run the native code for the Array function called as a normal function.
@@ -857,15 +850,15 @@ void Builtins::Generate_ArrayConstructCode(MacroAssembler* masm) {
// does always have a map.
GenerateLoadArrayFunction(masm, rbx);
__ cmpq(rdi, rbx);
- __ Assert(equal, "Unexpected Array function");
+ __ Check(equal, "Unexpected Array function");
// Initial map for the builtin Array function should be a map.
__ movq(rbx, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset));
// Will both indicate a NULL and a Smi.
ASSERT(kSmiTag == 0);
- Condition not_smi = __ CheckNotSmi(rbx);
- __ Assert(not_smi, "Unexpected initial map for Array function");
+ Condition not_smi = NegateCondition(masm->CheckSmi(rbx));
+ __ Check(not_smi, "Unexpected initial map for Array function");
__ CmpObjectType(rbx, MAP_TYPE, rcx);
- __ Assert(equal, "Unexpected initial map for Array function");
+ __ Check(equal, "Unexpected initial map for Array function");
}
// Run the native code for the Array function called as constructor.
@@ -902,7 +895,6 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
// edi: called object
// eax: number of arguments
__ bind(&non_function_call);
-
// Set expected number of arguments to zero (not changing eax).
__ movq(rbx, Immediate(0));
__ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
@@ -1143,11 +1135,9 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
__ LeaveConstructFrame();
// Remove caller arguments from the stack and return.
- ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
- // TODO(smi): Find a way to abstract indexing by a smi.
__ pop(rcx);
- // 1 * kPointerSize is offset of receiver.
- __ lea(rsp, Operand(rsp, rbx, times_half_pointer_size, 1 * kPointerSize));
+ SmiIndex index = masm->SmiToIndex(rbx, rbx, kPointerSizeLog2);
+ __ lea(rsp, Operand(rsp, index.reg, index.scale, 1 * kPointerSize));
__ push(rcx);
__ IncrementCounter(&Counters::constructed_objects, 1);
__ ret(0);
diff --git a/deps/v8/src/x64/codegen-x64.cc b/deps/v8/src/x64/codegen-x64.cc
index 8e6dbef2d..d72257e96 100644
--- a/deps/v8/src/x64/codegen-x64.cc
+++ b/deps/v8/src/x64/codegen-x64.cc
@@ -54,6 +54,7 @@ void DeferredCode::SaveRegisters() {
}
}
+
void DeferredCode::RestoreRegisters() {
// Restore registers in reverse order due to the stack.
for (int i = RegisterAllocator::kNumRegisters - 1; i >= 0; i--) {
@@ -237,8 +238,8 @@ class FloatingPointHelper : public AllStatic {
// Test if operands are smi or number objects (fp). Requirements:
// operand_1 in rax, operand_2 in rdx; falls through on float or smi
// operands, jumps to the non_float label otherwise.
- static void CheckFloatOperands(MacroAssembler* masm,
- Label* non_float);
+ static void CheckNumberOperands(MacroAssembler* masm,
+ Label* non_float);
// Allocate a heap number in new space with undefined value.
// Returns tagged pointer in result, or jumps to need_gc if new space is full.
@@ -278,7 +279,7 @@ void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
__ movq(kScratchRegister, pairs, RelocInfo::EMBEDDED_OBJECT);
frame_->EmitPush(kScratchRegister);
frame_->EmitPush(rsi); // The context is the second argument.
- frame_->EmitPush(Immediate(Smi::FromInt(is_eval() ? 1 : 0)));
+ frame_->EmitPush(Smi::FromInt(is_eval() ? 1 : 0));
Result ignored = frame_->CallRuntime(Runtime::kDeclareGlobals, 3);
// Return value is ignored.
}
@@ -767,8 +768,8 @@ void CodeGenerator::CallApplyLazy(Property* apply,
// adaptor frame below it.
Label invoke, adapted;
__ movq(rdx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
- __ movq(rcx, Operand(rdx, StandardFrameConstants::kContextOffset));
- __ cmpq(rcx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ SmiCompare(Operand(rdx, StandardFrameConstants::kContextOffset),
+ Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
__ j(equal, &adapted);
// No arguments adaptor frame. Copy fixed number of arguments.
@@ -793,12 +794,12 @@ void CodeGenerator::CallApplyLazy(Property* apply,
// have to worry about getting rid of the elements from the virtual
// frame.
Label loop;
- __ bind(&loop);
__ testl(rcx, rcx);
__ j(zero, &invoke);
+ __ bind(&loop);
__ push(Operand(rdx, rcx, times_pointer_size, 1 * kPointerSize));
__ decl(rcx);
- __ jmp(&loop);
+ __ j(not_zero, &loop);
// Invoke the function. The virtual frame knows about the receiver
// so make sure to forget that explicitly.
@@ -933,7 +934,7 @@ void CodeGenerator::VisitDeclaration(Declaration* node) {
// 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)));
+ 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
@@ -943,7 +944,7 @@ void CodeGenerator::VisitDeclaration(Declaration* node) {
} else if (node->fun() != NULL) {
Load(node->fun());
} else {
- frame_->EmitPush(Immediate(Smi::FromInt(0))); // no initial value!
+ frame_->EmitPush(Smi::FromInt(0)); // no initial value!
}
Result ignored = frame_->CallRuntime(Runtime::kDeclareContextSlot, 4);
// Ignore the return value (declarations are statements).
@@ -1291,288 +1292,335 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
}
-void CodeGenerator::VisitLoopStatement(LoopStatement* node) {
+void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) {
ASSERT(!in_spilled_code());
- Comment cmnt(masm_, "[ LoopStatement");
+ 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;
+ }
- // Simple condition analysis. ALWAYS_TRUE and ALWAYS_FALSE represent a
- // known result for the test expression, with no side effects.
- enum { ALWAYS_TRUE, ALWAYS_FALSE, DONT_KNOW } info = DONT_KNOW;
- if (node->cond() == NULL) {
- ASSERT(node->type() == LoopStatement::FOR_LOOP);
- info = ALWAYS_TRUE;
- } else {
- Literal* lit = node->cond()->AsLiteral();
- if (lit != NULL) {
- if (lit->IsTrue()) {
- info = ALWAYS_TRUE;
- } else if (lit->IsFalse()) {
- info = ALWAYS_FALSE;
+ 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()) {
+ ControlDestination dest(&body, node->break_target(), false);
+ LoadCondition(node->cond(), NOT_INSIDE_TYPEOF, &dest, true);
+ }
+ if (node->break_target()->is_linked()) {
+ node->break_target()->Bind();
+ }
+ break;
}
- switch (node->type()) {
- case LoopStatement::DO_LOOP: {
- JumpTarget body(JumpTarget::BIDIRECTIONAL);
- IncrementLoopNesting();
+ DecrementLoopNesting();
+ node->continue_target()->Unuse();
+ node->break_target()->Unuse();
+}
- // Label the top of the loop for the backward jump if necessary.
- if (info == ALWAYS_TRUE) {
- // Use the continue target.
- node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL);
- node->continue_target()->Bind();
- } else if (info == ALWAYS_FALSE) {
- // No need to label it.
+
+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 {
- // Continue is the test, so use the backward body target.
- ASSERT(info == DONT_KNOW);
- node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
- body.Bind();
+ // 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();
}
-
- CheckStack(); // TODO(1222600): ignore if body contains calls.
- Visit(node->body());
-
- // Compile the test.
- if (info == 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();
+ // 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(), NOT_INSIDE_TYPEOF, &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;
}
- } else if (info == 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();
- }
+ // 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;
+ }
- } else {
- ASSERT(info == 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.
+ 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(), NOT_INSIDE_TYPEOF, &dest, true);
}
- if (node->break_target()->is_linked()) {
- node->break_target()->Bind();
+ } 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;
+ }
- case LoopStatement::WHILE_LOOP: {
- // 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();
+ // 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();
+}
- IncrementLoopNesting();
- // If the condition is always false and has no side effects, we
- // do not need to compile anything.
- if (info == ALWAYS_FALSE) break;
+void CodeGenerator::VisitForStatement(ForStatement* node) {
+ ASSERT(!in_spilled_code());
+ Comment cmnt(masm_, "[ ForStatement");
+ CodeForStatementPosition(node);
- JumpTarget body;
- if (test_at_bottom) {
- body.set_direction(JumpTarget::BIDIRECTIONAL);
- }
+ // 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;
- // Based on the condition analysis, compile the test as necessary.
- if (info == ALWAYS_TRUE) {
- // We will not compile the test expression. Label the top of
- // the loop with the continue target.
+ // 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 {
- ASSERT(info == DONT_KNOW); // ALWAYS_FALSE cannot reach here.
- 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(), NOT_INSIDE_TYPEOF, &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()) break;
-
- // 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();
- }
+ // Otherwise use the backward loop target.
+ node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
+ loop.Bind();
}
-
- CheckStack(); // TODO(1222600): ignore if body contains calls.
- Visit(node->body());
-
- // Based on the condition analysis, compile the backward jump as
- // necessary.
- if (info == 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) {
+ // 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 {
- ASSERT(info == DONT_KNOW); // ALWAYS_FALSE cannot reach here.
- 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(), NOT_INSIDE_TYPEOF, &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();
- }
- }
+ // 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();
}
- // 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();
+ // 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(), NOT_INSIDE_TYPEOF, &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;
+ }
- case LoopStatement::FOR_LOOP: {
- // 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();
-
- // Compile the init expression if present.
- if (node->init() != NULL) {
- Visit(node->init());
- }
-
- IncrementLoopNesting();
-
- // If the condition is always false and has no side effects, we
- // do not need to compile anything else.
- if (info == ALWAYS_FALSE) break;
+ CheckStack(); // TODO(1222600): ignore if body contains calls.
+ Visit(node->body());
- // Target for backward edge if no test at the bottom, otherwise
- // unused.
- JumpTarget loop(JumpTarget::BIDIRECTIONAL);
+ // If there is an update expression, compile it if necessary.
+ if (node->next() != NULL) {
+ if (node->continue_target()->is_linked()) {
+ node->continue_target()->Bind();
+ }
- // 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);
- }
+ // 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 test as necessary.
- if (info == ALWAYS_TRUE) {
- // We will not compile the test expression. Label the top of
- // the loop.
+ // Based on the condition analysis, compile the backward jump as
+ // necessary.
+ switch (info) {
+ case ALWAYS_TRUE:
+ if (has_valid_frame()) {
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();
- }
- } else {
- ASSERT(info == 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();
+ node->continue_target()->Jump();
} 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(), NOT_INSIDE_TYPEOF, &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()) break;
-
- // 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();
+ loop.Jump();
}
}
-
- 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) {
+ 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 update by falling out of the body or
- // by a continue.
+ // 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()) {
- // 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());
+ // The break target is the fall-through (body is a backward
+ // jump from here).
+ ControlDestination dest(&body, node->break_target(), false);
+ LoadCondition(node->cond(), NOT_INSIDE_TYPEOF, &dest, true);
}
- }
-
- // Based on the condition analysis, compile the backward jump as
- // necessary.
- if (info == ALWAYS_TRUE) {
+ } else {
+ // Otherwise, jump back to the test at the top.
if (has_valid_frame()) {
if (node->next() == NULL) {
node->continue_target()->Jump();
@@ -1580,47 +1628,19 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) {
loop.Jump();
}
}
- } else {
- ASSERT(info == DONT_KNOW); // ALWAYS_FALSE cannot reach here.
- 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(), NOT_INSIDE_TYPEOF, &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();
- }
- }
- }
- }
-
- // 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();
}
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();
- node->continue_target()->Unuse();
- node->break_target()->Unuse();
}
@@ -1700,19 +1720,19 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
__ movl(rax, FieldOperand(rdx, FixedArray::kLengthOffset));
__ Integer32ToSmi(rax, rax);
frame_->EmitPush(rax); // <- slot 1
- frame_->EmitPush(Immediate(Smi::FromInt(0))); // <- slot 0
+ frame_->EmitPush(Smi::FromInt(0)); // <- slot 0
entry.Jump();
fixed_array.Bind();
// rax: fixed array (result from call to Runtime::kGetPropertyNamesFast)
- frame_->EmitPush(Immediate(Smi::FromInt(0))); // <- slot 3
+ 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.
__ movl(rax, FieldOperand(rax, FixedArray::kLengthOffset));
__ Integer32ToSmi(rax, rax);
frame_->EmitPush(rax); // <- slot 1
- frame_->EmitPush(Immediate(Smi::FromInt(0))); // <- slot 0
+ frame_->EmitPush(Smi::FromInt(0)); // <- slot 0
// Condition.
entry.Bind();
@@ -1722,8 +1742,8 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
__ movq(rax, frame_->ElementAt(0)); // load the current count
- __ cmpl(rax, frame_->ElementAt(1)); // compare to the array length
- node->break_target()->Branch(above_equal);
+ __ 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));
@@ -1796,7 +1816,7 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
node->continue_target()->Bind();
frame_->SpillAll();
frame_->EmitPop(rax);
- __ addq(rax, Immediate(Smi::FromInt(1)));
+ __ SmiAddConstant(rax, rax, Smi::FromInt(1));
frame_->EmitPush(rax);
entry.Jump();
@@ -1812,10 +1832,10 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
node->break_target()->Unuse();
}
-void CodeGenerator::VisitTryCatch(TryCatch* node) {
+void CodeGenerator::VisitTryCatchStatement(TryCatchStatement* node) {
ASSERT(!in_spilled_code());
VirtualFrame::SpilledScope spilled_scope;
- Comment cmnt(masm_, "[ TryCatch");
+ Comment cmnt(masm_, "[ TryCatchStatement");
CodeForStatementPosition(node);
JumpTarget try_block;
@@ -1951,10 +1971,10 @@ void CodeGenerator::VisitTryCatch(TryCatch* node) {
}
-void CodeGenerator::VisitTryFinally(TryFinally* node) {
+void CodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* node) {
ASSERT(!in_spilled_code());
VirtualFrame::SpilledScope spilled_scope;
- Comment cmnt(masm_, "[ TryFinally");
+ Comment cmnt(masm_, "[ TryFinallyStatement");
CodeForStatementPosition(node);
// State: Used to keep track of reason for entering the finally
@@ -1969,7 +1989,7 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) {
frame_->EmitPush(rax);
// In case of thrown exceptions, this is where we continue.
- __ movq(rcx, Immediate(Smi::FromInt(THROWING)));
+ __ Move(rcx, Smi::FromInt(THROWING));
finally_block.Jump();
// --- Try block ---
@@ -2028,7 +2048,7 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) {
// 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);
- __ movq(rcx, Immediate(Smi::FromInt(FALLING)));
+ __ Move(rcx, Smi::FromInt(FALLING));
if (nof_unlinks > 0) {
finally_block.Jump();
}
@@ -2074,7 +2094,7 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) {
// Fake TOS for targets that shadowed breaks and continues.
frame_->EmitPush(Heap::kUndefinedValueRootIndex);
}
- __ movq(rcx, Immediate(Smi::FromInt(JUMPING + i)));
+ __ 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();
@@ -2105,7 +2125,7 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) {
for (int i = 0; i < shadows.length(); i++) {
if (has_valid_frame() && shadows[i]->is_bound()) {
BreakTarget* original = shadows[i]->other_target();
- __ cmpq(rcx, Immediate(Smi::FromInt(JUMPING + i)));
+ __ SmiCompare(rcx, Smi::FromInt(JUMPING + i));
if (i == kReturnShadowIndex) {
// The return value is (already) in rax.
Result return_value = allocator_->Allocate(rax);
@@ -2130,7 +2150,7 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) {
if (has_valid_frame()) {
// Check if we need to rethrow the exception.
JumpTarget exit;
- __ cmpq(rcx, Immediate(Smi::FromInt(THROWING)));
+ __ SmiCompare(rcx, Smi::FromInt(THROWING));
exit.Branch(not_equal);
// Rethrow exception.
@@ -2278,7 +2298,7 @@ void DeferredRegExpLiteral::Generate() {
// Literal array (0).
__ push(literals_);
// Literal index (1).
- __ push(Immediate(Smi::FromInt(node_->literal_index())));
+ __ Push(Smi::FromInt(node_->literal_index()));
// RegExp pattern (2).
__ Push(node_->pattern());
// RegExp flags (3).
@@ -2351,7 +2371,7 @@ void DeferredObjectLiteral::Generate() {
// Literal array (0).
__ push(literals_);
// Literal index (1).
- __ push(Immediate(Smi::FromInt(node_->literal_index())));
+ __ Push(Smi::FromInt(node_->literal_index()));
// Constant properties (2).
__ Push(node_->constant_properties());
__ CallRuntime(Runtime::kCreateObjectLiteralBoilerplate, 3);
@@ -2484,7 +2504,7 @@ void DeferredArrayLiteral::Generate() {
// Literal array (0).
__ push(literals_);
// Literal index (1).
- __ push(Immediate(Smi::FromInt(node_->literal_index())));
+ __ Push(Smi::FromInt(node_->literal_index()));
// Constant properties (2).
__ Push(node_->literals());
__ CallRuntime(Runtime::kCreateArrayLiteralBoilerplate, 3);
@@ -3072,8 +3092,8 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
case Token::SUB: {
bool overwrite =
- (node->AsBinaryOperation() != NULL &&
- node->AsBinaryOperation()->ResultOverwriteAllowed());
+ (node->expression()->AsBinaryOperation() != NULL &&
+ node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
UnarySubStub stub(overwrite);
// TODO(1222589): remove dependency of TOS being cached inside stub
Result operand = frame_->Pop();
@@ -3151,7 +3171,7 @@ void DeferredPrefixCountOperation::Generate() {
__ push(dst_);
__ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
__ push(rax);
- __ push(Immediate(Smi::FromInt(1)));
+ __ Push(Smi::FromInt(1));
if (is_increment_) {
__ CallRuntime(Runtime::kNumberAdd, 2);
} else {
@@ -3191,7 +3211,7 @@ void DeferredPostfixCountOperation::Generate() {
// Call the runtime for the addition or subtraction.
__ push(rax);
- __ push(Immediate(Smi::FromInt(1)));
+ __ Push(Smi::FromInt(1));
if (is_increment_) {
__ CallRuntime(Runtime::kNumberAdd, 2);
} else {
@@ -3249,15 +3269,18 @@ void CodeGenerator::VisitCountOperation(CountOperation* node) {
is_increment);
}
- __ movq(kScratchRegister, new_value.reg());
+ __ JumpIfNotSmi(new_value.reg(), deferred->entry_label());
if (is_increment) {
- __ addl(kScratchRegister, Immediate(Smi::FromInt(1)));
+ __ SmiAddConstant(kScratchRegister,
+ new_value.reg(),
+ Smi::FromInt(1),
+ deferred->entry_label());
} else {
- __ subl(kScratchRegister, Immediate(Smi::FromInt(1)));
+ __ SmiSubConstant(kScratchRegister,
+ new_value.reg(),
+ Smi::FromInt(1),
+ deferred->entry_label());
}
- // Smi test.
- deferred->Branch(overflow);
- __ JumpIfNotSmi(kScratchRegister, deferred->entry_label());
__ movq(new_value.reg(), kScratchRegister);
deferred->BindExit();
@@ -3634,15 +3657,15 @@ void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) {
// Skip the arguments adaptor frame if it exists.
Label check_frame_marker;
- __ cmpq(Operand(fp.reg(), StandardFrameConstants::kContextOffset),
- Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ SmiCompare(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);
- __ cmpq(Operand(fp.reg(), StandardFrameConstants::kMarkerOffset),
- Immediate(Smi::FromInt(StackFrame::CONSTRUCT)));
+ __ SmiCompare(Operand(fp.reg(), StandardFrameConstants::kMarkerOffset),
+ Smi::FromInt(StackFrame::CONSTRUCT));
fp.Unuse();
destination()->Split(equal);
}
@@ -3878,7 +3901,7 @@ void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
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).
+ // being padded as a smi, so it should not be treated as a smi.).
ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
Result rbp_as_smi = allocator_->Allocate();
ASSERT(rbp_as_smi.is_valid());
@@ -4262,8 +4285,8 @@ void CodeGenerator::ToBoolean(ControlDestination* dest) {
dest->false_target()->Branch(equal);
// Smi => false iff zero.
- Condition equals = masm_->CheckSmiEqualsConstant(value.reg(), 0);
- dest->false_target()->Branch(equals);
+ __ SmiCompare(value.reg(), Smi::FromInt(0));
+ dest->false_target()->Branch(equal);
Condition is_smi = masm_->CheckSmi(value.reg());
dest->true_target()->Branch(is_smi);
@@ -4945,7 +4968,7 @@ void CodeGenerator::Comparison(Condition cc,
right_side = Result(right_val);
// Test smi equality and comparison by signed int comparison.
// Both sides are smis, so we can use an Immediate.
- __ cmpl(left_side.reg(), Immediate(Smi::cast(*right_side.handle())));
+ __ SmiCompare(left_side.reg(), Smi::cast(*right_side.handle()));
left_side.Unuse();
right_side.Unuse();
dest->Split(cc);
@@ -4978,7 +5001,7 @@ void CodeGenerator::Comparison(Condition cc,
Result temp = allocator()->Allocate();
ASSERT(temp.is_valid());
__ movq(temp.reg(),
- FieldOperand(operand.reg(), HeapObject::kMapOffset));
+ FieldOperand(operand.reg(), HeapObject::kMapOffset));
__ testb(FieldOperand(temp.reg(), Map::kBitFieldOffset),
Immediate(1 << Map::kIsUndetectable));
temp.Unuse();
@@ -4998,7 +5021,7 @@ void CodeGenerator::Comparison(Condition cc,
CompareStub stub(cc, strict);
Result answer = frame_->CallStub(&stub, &left_side, &right_side);
// The result is a Smi, which is negative, zero, or positive.
- __ testl(answer.reg(), answer.reg()); // Both zero and sign flag right.
+ __ SmiTest(answer.reg()); // Sets both zero and sign flag.
answer.Unuse();
dest->Split(cc);
} else {
@@ -5016,7 +5039,7 @@ void CodeGenerator::Comparison(Condition cc,
// When non-smi, call out to the compare stub.
CompareStub stub(cc, strict);
Result answer = frame_->CallStub(&stub, &left_side, &right_side);
- __ testl(answer.reg(), answer.reg()); // Sets both zero and sign flags.
+ __ SmiTest(answer.reg()); // Sets both zero and sign flags.
answer.Unuse();
dest->true_target()->Branch(cc);
dest->false_target()->Jump();
@@ -5024,7 +5047,7 @@ void CodeGenerator::Comparison(Condition cc,
is_smi.Bind();
left_side = Result(left_reg);
right_side = Result(right_reg);
- __ cmpl(left_side.reg(), right_side.reg());
+ __ SmiCompare(left_side.reg(), right_side.reg());
right_side.Unuse();
left_side.Unuse();
dest->Split(cc);
@@ -5221,7 +5244,7 @@ void DeferredReferenceGetNamedValue::Generate() {
void DeferredInlineSmiAdd::Generate() {
__ push(dst_);
- __ push(Immediate(value_));
+ __ Push(value_);
GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, SMI_CODE_INLINED);
__ CallStub(&igostub);
if (!dst_.is(rax)) __ movq(dst_, rax);
@@ -5229,7 +5252,7 @@ void DeferredInlineSmiAdd::Generate() {
void DeferredInlineSmiAddReversed::Generate() {
- __ push(Immediate(value_)); // Note: sign extended.
+ __ Push(value_);
__ push(dst_);
GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, SMI_CODE_INLINED);
__ CallStub(&igostub);
@@ -5239,7 +5262,7 @@ void DeferredInlineSmiAddReversed::Generate() {
void DeferredInlineSmiSub::Generate() {
__ push(dst_);
- __ push(Immediate(value_)); // Note: sign extended.
+ __ Push(value_);
GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, SMI_CODE_INLINED);
__ CallStub(&igostub);
if (!dst_.is(rax)) __ movq(dst_, rax);
@@ -5248,7 +5271,7 @@ void DeferredInlineSmiSub::Generate() {
void DeferredInlineSmiOperation::Generate() {
__ push(src_);
- __ push(Immediate(value_)); // Note: sign extended.
+ __ Push(value_);
// For mod we don't generate all the Smi code inline.
GenericBinaryOpStub stub(
op_,
@@ -5306,7 +5329,7 @@ void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op,
__ JumpIfNotSmi(operand->reg(), deferred->entry_label());
__ SmiAddConstant(operand->reg(),
operand->reg(),
- int_value,
+ smi_value,
deferred->entry_label());
deferred->BindExit();
frame_->Push(operand);
@@ -5328,7 +5351,7 @@ void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op,
// A smi currently fits in a 32-bit Immediate.
__ SmiSubConstant(operand->reg(),
operand->reg(),
- int_value,
+ smi_value,
deferred->entry_label());
deferred->BindExit();
frame_->Push(operand);
@@ -5382,9 +5405,9 @@ void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op,
overwrite_mode);
__ JumpIfNotSmi(operand->reg(), deferred->entry_label());
__ SmiShiftLogicalRightConstant(answer.reg(),
- operand->reg(),
- shift_value,
- deferred->entry_label());
+ operand->reg(),
+ shift_value,
+ deferred->entry_label());
deferred->BindExit();
operand->Unuse();
frame_->Push(&answer);
@@ -5453,15 +5476,15 @@ void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op,
overwrite_mode);
__ JumpIfNotSmi(operand->reg(), deferred->entry_label());
if (op == Token::BIT_AND) {
- __ SmiAndConstant(operand->reg(), operand->reg(), int_value);
+ __ SmiAndConstant(operand->reg(), operand->reg(), smi_value);
} else if (op == Token::BIT_XOR) {
if (int_value != 0) {
- __ SmiXorConstant(operand->reg(), operand->reg(), int_value);
+ __ SmiXorConstant(operand->reg(), operand->reg(), smi_value);
}
} else {
ASSERT(op == Token::BIT_OR);
if (int_value != 0) {
- __ SmiOrConstant(operand->reg(), operand->reg(), int_value);
+ __ SmiOrConstant(operand->reg(), operand->reg(), smi_value);
}
}
deferred->BindExit();
@@ -5476,18 +5499,21 @@ void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op,
(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);
+ DeferredCode* deferred =
+ new DeferredInlineSmiOperation(op,
+ operand->reg(),
+ operand->reg(),
+ smi_value,
+ overwrite_mode);
// Check for negative or non-Smi left hand side.
__ JumpIfNotPositiveSmi(operand->reg(), deferred->entry_label());
if (int_value < 0) int_value = -int_value;
if (int_value == 1) {
- __ movl(operand->reg(), Immediate(Smi::FromInt(0)));
+ __ Move(operand->reg(), Smi::FromInt(0));
} else {
- __ SmiAndConstant(operand->reg(), operand->reg(), int_value - 1);
+ __ SmiAndConstant(operand->reg(),
+ operand->reg(),
+ Smi::FromInt(int_value - 1));
}
deferred->BindExit();
frame_->Push(operand);
@@ -6085,8 +6111,6 @@ void Reference::SetValue(InitState init_state) {
// Check that the key is a non-negative smi.
__ JumpIfNotPositiveSmi(key.reg(), deferred->entry_label());
- // Ensure that the smi is zero-extended. This is not guaranteed.
- __ movl(key.reg(), key.reg());
// Check that the receiver is not a smi.
__ JumpIfSmi(receiver.reg(), deferred->entry_label());
@@ -6096,10 +6120,10 @@ void Reference::SetValue(InitState init_state) {
deferred->Branch(not_equal);
// Check that the key is within bounds. Both the key and the
- // length of the JSArray are smis, so compare only low 32 bits.
- __ cmpl(key.reg(),
- FieldOperand(receiver.reg(), JSArray::kLengthOffset));
- deferred->Branch(greater_equal);
+ // length of the JSArray are smis.
+ __ SmiCompare(FieldOperand(receiver.reg(), JSArray::kLengthOffset),
+ key.reg());
+ deferred->Branch(less_equal);
// Get the elements array from the receiver and check that it
// is a flat array (not a dictionary).
@@ -6303,22 +6327,17 @@ void UnarySubStub::Generate(MacroAssembler* masm) {
Label slow;
Label done;
Label try_float;
- Label special;
// Check whether the value is a smi.
__ JumpIfNotSmi(rax, &try_float);
// Enter runtime system if the value of the smi is zero
// to make sure that we switch between 0 and -0.
- // Also enter it if the value of the smi is Smi::kMinValue
- __ testl(rax, Immediate(0x7FFFFFFE));
- __ j(zero, &special);
- __ negl(rax);
- __ jmp(&done);
+ // Also enter it if the value of the smi is Smi::kMinValue.
+ __ SmiNeg(rax, rax, &done);
- __ bind(&special);
- // Either zero or -0x4000000, neither of which become a smi when negated.
- __ testl(rax, rax);
- __ j(not_zero, &slow);
+ // Either zero or Smi::kMinValue, neither of which become a smi when negated.
+ __ SmiCompare(rax, Smi::FromInt(0));
+ __ j(not_equal, &slow);
__ Move(rax, Factory::minus_zero_value());
__ jmp(&done);
@@ -6470,7 +6489,7 @@ void CompareStub::Generate(MacroAssembler* masm) {
// Call builtin if operands are not floating point or smi.
Label check_for_symbols;
// Push arguments on stack, for helper functions.
- FloatingPointHelper::CheckFloatOperands(masm, &check_for_symbols);
+ FloatingPointHelper::CheckNumberOperands(masm, &check_for_symbols);
FloatingPointHelper::LoadFloatOperands(masm, rax, rdx);
__ FCmp();
@@ -6527,7 +6546,7 @@ void CompareStub::Generate(MacroAssembler* masm) {
ASSERT(cc_ == greater || cc_ == greater_equal); // remaining cases
ncr = LESS;
}
- __ push(Immediate(Smi::FromInt(ncr)));
+ __ Push(Smi::FromInt(ncr));
}
// Restore return address on the stack.
@@ -6626,7 +6645,7 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
__ ret(2 * kPointerSize);
__ bind(&is_not_instance);
- __ movq(rax, Immediate(Smi::FromInt(1)));
+ __ Move(rax, Smi::FromInt(1));
__ ret(2 * kPointerSize);
// Slow-case: Go through the JavaScript implementation.
@@ -6644,8 +6663,8 @@ void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
// Check if the calling frame is an arguments adaptor frame.
Label runtime;
__ movq(rdx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
- __ movq(rcx, Operand(rdx, StandardFrameConstants::kContextOffset));
- __ cmpq(rcx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ SmiCompare(Operand(rdx, StandardFrameConstants::kContextOffset),
+ Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
__ j(not_equal, &runtime);
// Value in rcx is Smi encoded.
@@ -6678,8 +6697,8 @@ void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
// Check if the calling frame is an arguments adaptor frame.
Label adaptor;
__ movq(rbx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
- __ movq(rcx, Operand(rbx, StandardFrameConstants::kContextOffset));
- __ cmpq(rcx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ SmiCompare(Operand(rbx, StandardFrameConstants::kContextOffset),
+ Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
__ j(equal, &adaptor);
// Check index against formal parameters count limit passed in
@@ -6726,8 +6745,8 @@ void ArgumentsAccessStub::GenerateReadLength(MacroAssembler* masm) {
// Check if the calling frame is an arguments adaptor frame.
Label adaptor;
__ movq(rdx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
- __ movq(rcx, Operand(rdx, StandardFrameConstants::kContextOffset));
- __ cmpq(rcx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ SmiCompare(Operand(rdx, StandardFrameConstants::kContextOffset),
+ Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
__ j(equal, &adaptor);
// Nothing to do: The formal number of parameters has already been
@@ -7069,8 +7088,8 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
// Push the stack frame type marker twice.
int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
- __ push(Immediate(Smi::FromInt(marker))); // context slot
- __ push(Immediate(Smi::FromInt(marker))); // function slot
+ __ Push(Smi::FromInt(marker)); // context slot
+ __ Push(Smi::FromInt(marker)); // function slot
// Save callee-saved registers (X64 calling conventions).
__ push(r12);
__ push(r13);
@@ -7182,7 +7201,7 @@ void StackCheckStub::Generate(MacroAssembler* masm) {
// must be inserted below the return address on the stack so we
// temporarily store that in a register.
__ pop(rax);
- __ push(Immediate(Smi::FromInt(0)));
+ __ Push(Smi::FromInt(0));
__ push(rax);
// Do tail-call to runtime routine.
@@ -7321,8 +7340,8 @@ void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm,
}
-void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm,
- Label* non_float) {
+void FloatingPointHelper::CheckNumberOperands(MacroAssembler* masm,
+ Label* non_float) {
Label test_other, done;
// Test if both operands are numbers (heap_numbers or smis).
// If not, jump to label non_float.
@@ -7403,17 +7422,17 @@ void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) {
case Token::SHR:
case Token::SAR:
// Move the second operand into register ecx.
- __ movl(rcx, rbx);
+ __ movq(rcx, rbx);
// Perform the operation.
switch (op_) {
case Token::SAR:
- __ SmiShiftArithmeticRight(rax, rax, rbx);
+ __ SmiShiftArithmeticRight(rax, rax, rcx);
break;
case Token::SHR:
- __ SmiShiftLogicalRight(rax, rax, rbx, slow);
+ __ SmiShiftLogicalRight(rax, rax, rcx, slow);
break;
case Token::SHL:
- __ SmiShiftLeft(rax, rax, rbx, slow);
+ __ SmiShiftLeft(rax, rax, rcx, slow);
break;
default:
UNREACHABLE();
@@ -7454,7 +7473,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
case Token::DIV: {
// rax: y
// rdx: x
- FloatingPointHelper::CheckFloatOperands(masm, &call_runtime);
+ FloatingPointHelper::CheckNumberOperands(masm, &call_runtime);
// Fast-case: Both operands are numbers.
// Allocate a heap number, if needed.
Label skip_allocation;
@@ -7499,7 +7518,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
case Token::SAR:
case Token::SHL:
case Token::SHR: {
- FloatingPointHelper::CheckFloatOperands(masm, &call_runtime);
+ FloatingPointHelper::CheckNumberOperands(masm, &call_runtime);
// TODO(X64): Don't convert a Smi to float and then back to int32
// afterwards.
FloatingPointHelper::LoadFloatOperands(masm);
@@ -7553,29 +7572,27 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
__ pop(rcx);
__ pop(rax);
switch (op_) {
- case Token::BIT_OR: __ or_(rax, rcx); break;
- case Token::BIT_AND: __ and_(rax, rcx); break;
- case Token::BIT_XOR: __ xor_(rax, rcx); break;
+ 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(rax); break;
case Token::SHL: __ shll(rax); break;
case Token::SHR: __ shrl(rax); break;
default: UNREACHABLE();
}
if (op_ == Token::SHR) {
- // Check if result is non-negative and fits in a smi.
- __ testl(rax, Immediate(0xc0000000));
- __ j(not_zero, &non_smi_result);
- } else {
- // Check if result fits in a smi.
- __ cmpl(rax, Immediate(0xc0000000));
+ // Check if result is non-negative. This can only happen for a shift
+ // by zero, which also doesn't update the sign flag.
+ __ testl(rax, rax);
__ j(negative, &non_smi_result);
}
- // Tag smi result and return.
+ __ JumpIfNotValidSmiValue(rax, &non_smi_result);
+ // Tag smi result, if possible, and return.
__ Integer32ToSmi(rax, rax);
__ ret(2 * kPointerSize);
// All ops except SHR return a signed int32 that we load in a HeapNumber.
- if (op_ != Token::SHR) {
+ if (op_ != Token::SHR && non_smi_result.is_linked()) {
__ bind(&non_smi_result);
// Allocate a heap number if needed.
__ movsxlq(rbx, rax); // rbx: sign extended 32-bit result
diff --git a/deps/v8/src/x64/codegen-x64.h b/deps/v8/src/x64/codegen-x64.h
index 87db3a9be..5fa6583a3 100644
--- a/deps/v8/src/x64/codegen-x64.h
+++ b/deps/v8/src/x64/codegen-x64.h
@@ -294,6 +294,15 @@ class CodeGenerator: public AstVisitor {
Handle<Script> script,
bool is_eval);
+ // Printing of AST, etc. as requested by flags.
+ static void MakeCodePrologue(FunctionLiteral* fun);
+
+ // Allocate and install the code.
+ static Handle<Code> MakeCodeEpilogue(FunctionLiteral* fun,
+ MacroAssembler* masm,
+ Code::Flags flags,
+ Handle<Script> script);
+
#ifdef ENABLE_LOGGING_AND_PROFILING
static bool ShouldGenerateLog(Expression* type);
#endif
@@ -303,6 +312,8 @@ class CodeGenerator: public AstVisitor {
bool is_toplevel,
Handle<Script> script);
+ static void RecordPositions(MacroAssembler* masm, int pos);
+
// Accessors
MacroAssembler* masm() { return masm_; }
@@ -548,6 +559,14 @@ class CodeGenerator: public AstVisitor {
inline void GenerateMathSin(ZoneList<Expression*>* args);
inline void GenerateMathCos(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.
diff --git a/deps/v8/src/x64/debug-x64.cc b/deps/v8/src/x64/debug-x64.cc
index 10092c55c..49240b407 100644
--- a/deps/v8/src/x64/debug-x64.cc
+++ b/deps/v8/src/x64/debug-x64.cc
@@ -39,10 +39,7 @@ namespace internal {
bool Debug::IsDebugBreakAtReturn(v8::internal::RelocInfo* rinfo) {
ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()));
- // 11th byte of patch is 0x49 (REX.WB byte of computed jump/call to r10),
- // 11th byte of JS return is 0xCC (int3).
- ASSERT(*(rinfo->pc() + 10) == 0x49 || *(rinfo->pc() + 10) == 0xCC);
- return (*(rinfo->pc() + 10) != 0xCC);
+ return rinfo->IsPatchedReturnSequence();
}
#define __ ACCESS_MASM(masm)
diff --git a/deps/v8/src/x64/fast-codegen-x64.cc b/deps/v8/src/x64/fast-codegen-x64.cc
new file mode 100644
index 000000000..c43383631
--- /dev/null
+++ b/deps/v8/src/x64/fast-codegen-x64.cc
@@ -0,0 +1,181 @@
+// 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 "debug.h"
+#include "fast-codegen.h"
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm_)
+
+// Generate code for a JS function. On entry to the function the receiver
+// and arguments have been pushed on the stack left to right, with the
+// return address on top of them. The actual argument count matches the
+// formal parameter count expected by the function.
+//
+// The live registers are:
+// o rdi: the JS function object being called (ie, ourselves)
+// o rsi: our context
+// o rbp: our caller's frame pointer
+// o rsp: stack pointer (pointing to return address)
+//
+// The function builds a JS frame. Please see JavaScriptFrameConstants in
+// frames-x64.h for its layout.
+void FastCodeGenerator::Generate(FunctionLiteral* fun) {
+ function_ = fun;
+ SetFunctionPosition(fun);
+
+ __ push(rbp); // Caller's frame pointer.
+ __ movq(rbp, rsp);
+ __ push(rsi); // Callee's context.
+ __ push(rdi); // Callee's JS Function.
+
+ { Comment cmnt(masm_, "[ Allocate locals");
+ int locals_count = fun->scope()->num_stack_slots();
+ for (int i = 0; i < locals_count; i++) {
+ __ PushRoot(Heap::kUndefinedValueRootIndex);
+ }
+ }
+
+ { Comment cmnt(masm_, "[ Stack check");
+ Label ok;
+ __ CompareRoot(rsp, Heap::kStackLimitRootIndex);
+ __ j(above_equal, &ok);
+ StackCheckStub stub;
+ __ CallStub(&stub);
+ __ bind(&ok);
+ }
+
+ { Comment cmnt(masm_, "[ Body");
+ VisitStatements(fun->body());
+ }
+
+ { Comment cmnt(masm_, "[ return <undefined>;");
+ // Emit a 'return undefined' in case control fell off the end of the
+ // body.
+ __ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
+ SetReturnPosition(fun);
+ __ RecordJSReturn();
+ // Do not use the leave instruction here because it is too short to
+ // patch with the code required by the debugger.
+ __ movq(rsp, rbp);
+ __ pop(rbp);
+ __ ret((fun->scope()->num_parameters() + 1) * kPointerSize);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // Add padding that will be overwritten by a debugger breakpoint. We
+ // have just generated "movq rsp, rbp; pop rbp; ret k" with length 7
+ // (3 + 1 + 3).
+ const int kPadding = Debug::kX64JSReturnSequenceLength - 7;
+ for (int i = 0; i < kPadding; ++i) {
+ masm_->int3();
+ }
+#endif
+ }
+}
+
+
+void FastCodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) {
+ Comment cmnt(masm_, "[ ExpressionStatement");
+ SetStatementPosition(stmt);
+ Visit(stmt->expression());
+}
+
+
+void FastCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
+ Comment cmnt(masm_, "[ ReturnStatement");
+ SetStatementPosition(stmt);
+ Visit(stmt->expression());
+ __ pop(rax);
+ __ RecordJSReturn();
+ // Do not use the leave instruction here because it is too short to
+ // patch with the code required by the debugger.
+ __ movq(rsp, rbp);
+ __ pop(rbp);
+ __ ret((function_->scope()->num_parameters() + 1) * kPointerSize);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // Add padding that will be overwritten by a debugger breakpoint. We
+ // have just generated "movq rsp, rbp; pop rbp; ret k" with length 7
+ // (3 + 1 + 3).
+ const int kPadding = Debug::kX64JSReturnSequenceLength - 7;
+ for (int i = 0; i < kPadding; ++i) {
+ masm_->int3();
+ }
+#endif
+}
+
+
+void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
+ Comment cmnt(masm_, "[ VariableProxy");
+ Expression* rewrite = expr->var()->rewrite();
+ ASSERT(rewrite != NULL);
+
+ Slot* slot = rewrite->AsSlot();
+ ASSERT(slot != NULL);
+ { Comment cmnt(masm_, "[ Slot");
+ if (expr->location().is_temporary()) {
+ __ push(Operand(rbp, SlotOffset(slot)));
+ } else {
+ ASSERT(expr->location().is_nowhere());
+ }
+ }
+}
+
+
+void FastCodeGenerator::VisitLiteral(Literal* expr) {
+ Comment cmnt(masm_, "[ Literal");
+ if (expr->location().is_temporary()) {
+ __ Push(expr->handle());
+ } else {
+ ASSERT(expr->location().is_nowhere());
+ }
+}
+
+
+void FastCodeGenerator::VisitAssignment(Assignment* expr) {
+ Comment cmnt(masm_, "[ Assignment");
+ ASSERT(expr->op() == Token::ASSIGN || expr->op() == Token::INIT_VAR);
+
+ Visit(expr->value());
+
+ Variable* var = expr->target()->AsVariableProxy()->AsVariable();
+ ASSERT(var != NULL && var->slot() != NULL);
+
+ if (expr->location().is_temporary()) {
+ __ movq(rax, Operand(rsp, 0));
+ __ movq(Operand(rbp, SlotOffset(var->slot())), rax);
+ } else {
+ ASSERT(expr->location().is_nowhere());
+ __ pop(Operand(rbp, SlotOffset(var->slot())));
+ }
+}
+
+
+} } // namespace v8::internal
diff --git a/deps/v8/src/x64/frames-x64.h b/deps/v8/src/x64/frames-x64.h
index 5442be960..eefaa0aeb 100644
--- a/deps/v8/src/x64/frames-x64.h
+++ b/deps/v8/src/x64/frames-x64.h
@@ -31,9 +31,6 @@
namespace v8 {
namespace internal {
-// TODO(x64): This is a stub, mostly just a copy of the ia32 bit version.
-// This might all need to change to be correct for x64.
-
static const int kNumRegs = 8;
static const RegList kJSCallerSaved =
1 << 0 | // rax
diff --git a/deps/v8/src/x64/ic-x64.cc b/deps/v8/src/x64/ic-x64.cc
index 820909122..7108025a7 100644
--- a/deps/v8/src/x64/ic-x64.cc
+++ b/deps/v8/src/x64/ic-x64.cc
@@ -131,8 +131,8 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss_label,
// Check that the value is a normal property.
__ bind(&done);
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
- __ testl(Operand(r0, r1, times_pointer_size, kDetailsOffset - kHeapObjectTag),
- Immediate(Smi::FromInt(PropertyDetails::TypeField::mask())));
+ __ Test(Operand(r0, r1, times_pointer_size, kDetailsOffset - kHeapObjectTag),
+ Smi::FromInt(PropertyDetails::TypeField::mask()));
__ j(not_zero, miss_label);
// Get the value at the masked, scaled index.
@@ -336,13 +336,13 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
__ testb(FieldOperand(rdx, Map::kInstanceTypeOffset),
Immediate(kIsSymbolMask));
__ j(zero, &slow);
- // Probe the dictionary leaving result in ecx.
+ // Probe the dictionary leaving result in rcx.
GenerateDictionaryLoad(masm, &slow, rbx, rcx, rdx, rax);
GenerateCheckNonObjectOrLoaded(masm, &slow, rcx);
__ movq(rax, rcx);
__ IncrementCounter(&Counters::keyed_load_generic_symbol, 1);
__ ret(0);
- // Array index string: If short enough use cache in length/hash field (ebx).
+ // Array index string: If short enough use cache in length/hash field (rbx).
// We assert that there are enough bits in an int32_t after the hash shift
// bits have been subtracted to allow space for the length and the cached
// array index.
@@ -434,9 +434,6 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
__ movq(rbx, Operand(rsp, 1 * kPointerSize)); // 1 ~ return address
// Check that the key is a smi.
__ JumpIfNotSmi(rbx, &slow);
- // If it is a smi, make sure it is zero-extended, so it can be
- // used as an index in a memory operand.
- __ movl(rbx, rbx); // Clear the high bits of rbx.
__ CmpInstanceType(rcx, JS_ARRAY_TYPE);
__ j(equal, &array);
@@ -447,7 +444,7 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
// Object case: Check key against length in the elements array.
// rax: value
// rdx: JSObject
- // rbx: index (as a smi), zero-extended.
+ // rbx: index (as a smi)
__ movq(rcx, FieldOperand(rdx, JSObject::kElementsOffset));
// Check that the object is in fast mode (not dictionary).
__ CompareRoot(FieldOperand(rcx, HeapObject::kMapOffset),
@@ -488,14 +485,11 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
__ movq(rdx, rax); // Save the value.
__ SmiToInteger32(rax, rax);
{ // Clamp the value to [0..255].
- Label done, is_negative;
+ Label done;
__ testl(rax, Immediate(0xFFFFFF00));
__ j(zero, &done);
- __ j(negative, &is_negative);
- __ movl(rax, Immediate(255));
- __ jmp(&done);
- __ bind(&is_negative);
- __ xorl(rax, rax); // Clear rax.
+ __ setcc(negative, rax); // 1 if negative, 0 if positive.
+ __ decb(rax); // 0 if negative, 255 if positive.
__ bind(&done);
}
__ movq(rcx, FieldOperand(rcx, PixelArray::kExternalPointerOffset));
@@ -511,15 +505,15 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
// rdx: JSArray
// rcx: FixedArray
// rbx: index (as a smi)
- // flags: compare (rbx, rdx.length())
+ // flags: smicompare (rdx.length(), rbx)
__ j(not_equal, &slow); // do not leave holes in the array
__ SmiToInteger64(rbx, rbx);
__ cmpl(rbx, FieldOperand(rcx, FixedArray::kLengthOffset));
__ j(above_equal, &slow);
// Increment and restore smi-tag.
- __ Integer64AddToSmi(rbx, rbx, 1);
+ __ Integer64PlusConstantToSmi(rbx, rbx, 1);
__ movq(FieldOperand(rdx, JSArray::kLengthOffset), rbx);
- __ SmiSubConstant(rbx, rbx, 1, NULL);
+ __ SmiSubConstant(rbx, rbx, Smi::FromInt(1));
__ jmp(&fast);
// Array case: Get the length and the elements array from the JS
@@ -530,25 +524,36 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
// rdx: JSArray
// rbx: index (as a smi)
__ movq(rcx, FieldOperand(rdx, JSObject::kElementsOffset));
- __ Cmp(FieldOperand(rcx, HeapObject::kMapOffset), Factory::fixed_array_map());
+ __ CompareRoot(FieldOperand(rcx, HeapObject::kMapOffset),
+ Heap::kFixedArrayMapRootIndex);
__ j(not_equal, &slow);
// Check the key against the length in the array, compute the
// address to store into and fall through to fast case.
- __ cmpl(rbx, FieldOperand(rdx, JSArray::kLengthOffset));
- __ j(above_equal, &extra);
+ __ SmiCompare(FieldOperand(rdx, JSArray::kLengthOffset), rbx);
+ __ j(below_equal, &extra);
// Fast case: Do the store.
__ bind(&fast);
// rax: value
// rcx: FixedArray
// rbx: index (as a smi)
- __ movq(Operand(rcx, rbx, times_half_pointer_size,
+ Label non_smi_value;
+ __ JumpIfNotSmi(rax, &non_smi_value);
+ SmiIndex index = masm->SmiToIndex(rbx, rbx, kPointerSizeLog2);
+ __ movq(Operand(rcx, index.reg, index.scale,
FixedArray::kHeaderSize - kHeapObjectTag),
- rax);
+ rax);
+ __ ret(0);
+ __ bind(&non_smi_value);
+ // Slow case that needs to retain rbx for use by RecordWrite.
// Update write barrier for the elements array address.
+ SmiIndex index2 = masm->SmiToIndex(kScratchRegister, rbx, kPointerSizeLog2);
+ __ movq(Operand(rcx, index2.reg, index2.scale,
+ FixedArray::kHeaderSize - kHeapObjectTag),
+ rax);
__ movq(rdx, rax);
- __ RecordWrite(rcx, 0, rdx, rbx);
+ __ RecordWriteNonSmi(rcx, 0, rdx, rbx);
__ ret(0);
}
diff --git a/deps/v8/src/x64/macro-assembler-x64.cc b/deps/v8/src/x64/macro-assembler-x64.cc
index 2689e388b..614f67b0a 100644
--- a/deps/v8/src/x64/macro-assembler-x64.cc
+++ b/deps/v8/src/x64/macro-assembler-x64.cc
@@ -38,16 +38,15 @@ namespace v8 {
namespace internal {
MacroAssembler::MacroAssembler(void* buffer, int size)
- : Assembler(buffer, size),
- unresolved_(0),
- generating_stub_(false),
- allow_stub_calls_(true),
- code_object_(Heap::undefined_value()) {
+ : Assembler(buffer, size),
+ unresolved_(0),
+ generating_stub_(false),
+ allow_stub_calls_(true),
+ code_object_(Heap::undefined_value()) {
}
-void MacroAssembler::LoadRoot(Register destination,
- Heap::RootListIndex index) {
+void MacroAssembler::LoadRoot(Register destination, Heap::RootListIndex index) {
movq(destination, Operand(r13, index << kPointerSizeLog2));
}
@@ -57,14 +56,12 @@ void MacroAssembler::PushRoot(Heap::RootListIndex index) {
}
-void MacroAssembler::CompareRoot(Register with,
- Heap::RootListIndex index) {
+void MacroAssembler::CompareRoot(Register with, Heap::RootListIndex index) {
cmpq(with, Operand(r13, index << kPointerSizeLog2));
}
-void MacroAssembler::CompareRoot(Operand with,
- Heap::RootListIndex index) {
+void MacroAssembler::CompareRoot(Operand with, Heap::RootListIndex index) {
LoadRoot(kScratchRegister, index);
cmpq(with, kScratchRegister);
}
@@ -144,9 +141,9 @@ class RecordWriteStub : public CodeStub {
// Minor key encoding in 12 bits of three registers (object, address and
// scratch) OOOOAAAASSSS.
- class ScratchBits: public BitField<uint32_t, 0, 4> {};
- class AddressBits: public BitField<uint32_t, 4, 4> {};
- class ObjectBits: public BitField<uint32_t, 8, 4> {};
+ class ScratchBits : public BitField<uint32_t, 0, 4> {};
+ class AddressBits : public BitField<uint32_t, 4, 4> {};
+ class ObjectBits : public BitField<uint32_t, 8, 4> {};
Major MajorKey() { return RecordWrite; }
@@ -167,33 +164,45 @@ void RecordWriteStub::Generate(MacroAssembler* masm) {
// Set the remembered set bit for [object+offset].
// object is the object being stored into, value is the object being stored.
-// If offset is zero, then the scratch register contains the array index into
-// the elements array represented as a Smi.
+// If offset is zero, then the smi_index register contains the array index into
+// the elements array represented as a smi. Otherwise it can be used as a
+// scratch register.
// All registers are clobbered by the operation.
void MacroAssembler::RecordWrite(Register object,
int offset,
Register value,
- Register scratch) {
+ Register smi_index) {
// First, check if a remembered set write is even needed. The tests below
// catch stores of Smis and stores into young gen (which does not have space
// for the remembered set bits.
Label done;
+ JumpIfSmi(value, &done);
+ RecordWriteNonSmi(object, offset, value, smi_index);
+ bind(&done);
+}
+
+
+void MacroAssembler::RecordWriteNonSmi(Register object,
+ int offset,
+ Register scratch,
+ Register smi_index) {
+ Label done;
// Test that the object address is not in the new space. We cannot
// set remembered set bits in the new space.
- movq(value, object);
+ movq(scratch, object);
ASSERT(is_int32(static_cast<int64_t>(Heap::NewSpaceMask())));
- and_(value, Immediate(static_cast<int32_t>(Heap::NewSpaceMask())));
+ and_(scratch, Immediate(static_cast<int32_t>(Heap::NewSpaceMask())));
movq(kScratchRegister, ExternalReference::new_space_start());
- cmpq(value, kScratchRegister);
+ cmpq(scratch, kScratchRegister);
j(equal, &done);
if ((offset > 0) && (offset < Page::kMaxHeapObjectSize)) {
// Compute the bit offset in the remembered set, leave it in 'value'.
- lea(value, Operand(object, offset));
+ lea(scratch, Operand(object, offset));
ASSERT(is_int32(Page::kPageAlignmentMask));
- and_(value, Immediate(static_cast<int32_t>(Page::kPageAlignmentMask)));
- shr(value, Immediate(kObjectAlignmentBits));
+ and_(scratch, Immediate(static_cast<int32_t>(Page::kPageAlignmentMask)));
+ shr(scratch, Immediate(kObjectAlignmentBits));
// Compute the page address from the heap object pointer, leave it in
// 'object' (immediate value is sign extended).
@@ -203,24 +212,26 @@ void MacroAssembler::RecordWrite(Register object,
// to limit code size. We should probably evaluate this decision by
// measuring the performance of an equivalent implementation using
// "simpler" instructions
- bts(Operand(object, Page::kRSetOffset), value);
+ bts(Operand(object, Page::kRSetOffset), scratch);
} else {
- Register dst = scratch;
+ Register dst = smi_index;
if (offset != 0) {
lea(dst, Operand(object, offset));
} else {
// array access: calculate the destination address in the same manner as
- // KeyedStoreIC::GenerateGeneric. Multiply a smi by 4 to get an offset
- // into an array of pointers.
- lea(dst, Operand(object, dst, times_half_pointer_size,
+ // KeyedStoreIC::GenerateGeneric.
+ SmiIndex index = SmiToIndex(smi_index, smi_index, kPointerSizeLog2);
+ lea(dst, Operand(object,
+ index.reg,
+ index.scale,
FixedArray::kHeaderSize - kHeapObjectTag));
}
// If we are already generating a shared stub, not inlining the
// record write code isn't going to save us any memory.
if (generating_stub()) {
- RecordWriteHelper(this, object, dst, value);
+ RecordWriteHelper(this, object, dst, scratch);
} else {
- RecordWriteStub stub(object, dst, value);
+ RecordWriteStub stub(object, dst, scratch);
CallStub(&stub);
}
}
@@ -370,7 +381,6 @@ void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) {
addq(target, Immediate(Code::kHeaderSize - kHeapObjectTag));
}
-
Handle<Code> MacroAssembler::ResolveBuiltin(Builtins::JavaScript id,
bool* resolved) {
// Move the builtin function into the temporary function slot by
@@ -384,7 +394,6 @@ Handle<Code> MacroAssembler::ResolveBuiltin(Builtins::JavaScript id,
JSBuiltinsObject::kJSBuiltinsOffset + (id * kPointerSize);
movq(rdi, FieldOperand(rdx, builtins_offset));
-
return Builtins::GetCode(id, resolved);
}
@@ -416,244 +425,177 @@ void MacroAssembler::Set(const Operand& dst, int64_t x) {
}
}
-
// ----------------------------------------------------------------------------
// Smi tagging, untagging and tag detection.
+static int kSmiShift = kSmiTagSize + kSmiShiftSize;
void MacroAssembler::Integer32ToSmi(Register dst, Register src) {
- ASSERT_EQ(1, kSmiTagSize);
ASSERT_EQ(0, kSmiTag);
-#ifdef DEBUG
- cmpq(src, Immediate(0xC0000000u));
- Check(positive, "Smi conversion overflow");
-#endif
- if (dst.is(src)) {
- addl(dst, src);
- } else {
- lea(dst, Operand(src, src, times_1, 0));
+ if (!dst.is(src)) {
+ movl(dst, src);
}
+ shl(dst, Immediate(kSmiShift));
}
void MacroAssembler::Integer32ToSmi(Register dst,
Register src,
Label* on_overflow) {
- ASSERT_EQ(1, kSmiTagSize);
ASSERT_EQ(0, kSmiTag);
+ // 32-bit integer always fits in a long smi.
if (!dst.is(src)) {
movl(dst, src);
}
- addl(dst, src);
- j(overflow, on_overflow);
+ shl(dst, Immediate(kSmiShift));
}
-void MacroAssembler::Integer64AddToSmi(Register dst,
- Register src,
- int constant) {
-#ifdef DEBUG
- movl(kScratchRegister, src);
- addl(kScratchRegister, Immediate(constant));
- Check(no_overflow, "Add-and-smi-convert overflow");
- Condition valid = CheckInteger32ValidSmiValue(kScratchRegister);
- Check(valid, "Add-and-smi-convert overflow");
-#endif
- lea(dst, Operand(src, src, times_1, constant << kSmiTagSize));
+void MacroAssembler::Integer64PlusConstantToSmi(Register dst,
+ Register src,
+ int constant) {
+ if (dst.is(src)) {
+ addq(dst, Immediate(constant));
+ } else {
+ lea(dst, Operand(src, constant));
+ }
+ shl(dst, Immediate(kSmiShift));
}
void MacroAssembler::SmiToInteger32(Register dst, Register src) {
- ASSERT_EQ(1, kSmiTagSize);
ASSERT_EQ(0, kSmiTag);
if (!dst.is(src)) {
- movl(dst, src);
+ movq(dst, src);
}
- sarl(dst, Immediate(kSmiTagSize));
+ shr(dst, Immediate(kSmiShift));
}
void MacroAssembler::SmiToInteger64(Register dst, Register src) {
- ASSERT_EQ(1, kSmiTagSize);
ASSERT_EQ(0, kSmiTag);
- movsxlq(dst, src);
- sar(dst, Immediate(kSmiTagSize));
-}
-
-
-void MacroAssembler::PositiveSmiTimesPowerOfTwoToInteger64(Register dst,
- Register src,
- int power) {
- ASSERT(power >= 0);
- ASSERT(power < 64);
- if (power == 0) {
- SmiToInteger64(dst, src);
- return;
+ if (!dst.is(src)) {
+ movq(dst, src);
}
- movsxlq(dst, src);
- shl(dst, Immediate(power - 1));
+ sar(dst, Immediate(kSmiShift));
}
-void MacroAssembler::JumpIfSmi(Register src, Label* on_smi) {
- ASSERT_EQ(0, kSmiTag);
- testl(src, Immediate(kSmiTagMask));
- j(zero, on_smi);
-}
-
-void MacroAssembler::JumpIfNotSmi(Register src, Label* on_not_smi) {
- Condition not_smi = CheckNotSmi(src);
- j(not_smi, on_not_smi);
+void MacroAssembler::SmiTest(Register src) {
+ testq(src, src);
}
-void MacroAssembler::JumpIfNotPositiveSmi(Register src,
- Label* on_not_positive_smi) {
- Condition not_positive_smi = CheckNotPositiveSmi(src);
- j(not_positive_smi, on_not_positive_smi);
+void MacroAssembler::SmiCompare(Register dst, Register src) {
+ cmpq(dst, src);
}
-void MacroAssembler::JumpIfSmiEqualsConstant(Register src,
- int constant,
- Label* on_equals) {
- if (Smi::IsValid(constant)) {
- Condition are_equal = CheckSmiEqualsConstant(src, constant);
- j(are_equal, on_equals);
+void MacroAssembler::SmiCompare(Register dst, Smi* src) {
+ ASSERT(!dst.is(kScratchRegister));
+ if (src->value() == 0) {
+ testq(dst, dst);
+ } else {
+ Move(kScratchRegister, src);
+ cmpq(dst, kScratchRegister);
}
}
-void MacroAssembler::JumpIfSmiGreaterEqualsConstant(Register src,
- int constant,
- Label* on_greater_equals) {
- if (Smi::IsValid(constant)) {
- Condition are_greater_equal = CheckSmiGreaterEqualsConstant(src, constant);
- j(are_greater_equal, on_greater_equals);
- } else if (constant < Smi::kMinValue) {
- jmp(on_greater_equals);
- }
+void MacroAssembler::SmiCompare(const Operand& dst, Register src) {
+ cmpq(dst, src);
}
-void MacroAssembler::JumpIfNotValidSmiValue(Register src, Label* on_invalid) {
- Condition is_valid = CheckInteger32ValidSmiValue(src);
- j(ReverseCondition(is_valid), on_invalid);
+void MacroAssembler::SmiCompare(const Operand& dst, Smi* src) {
+ if (src->value() == 0) {
+ // Only tagged long smi to have 32-bit representation.
+ cmpq(dst, Immediate(0));
+ } else {
+ Move(kScratchRegister, src);
+ cmpq(dst, kScratchRegister);
+ }
}
-
-void MacroAssembler::JumpIfNotBothSmi(Register src1,
- Register src2,
- Label* on_not_both_smi) {
- Condition not_both_smi = CheckNotBothSmi(src1, src2);
- j(not_both_smi, on_not_both_smi);
-}
-
-Condition MacroAssembler::CheckSmi(Register src) {
- testb(src, Immediate(kSmiTagMask));
- return zero;
+void MacroAssembler::PositiveSmiTimesPowerOfTwoToInteger64(Register dst,
+ Register src,
+ int power) {
+ ASSERT(power >= 0);
+ ASSERT(power < 64);
+ if (power == 0) {
+ SmiToInteger64(dst, src);
+ return;
+ }
+ if (!dst.is(src)) {
+ movq(dst, src);
+ }
+ if (power < kSmiShift) {
+ sar(dst, Immediate(kSmiShift - power));
+ } else if (power > kSmiShift) {
+ shl(dst, Immediate(power - kSmiShift));
+ }
}
-Condition MacroAssembler::CheckNotSmi(Register src) {
+Condition MacroAssembler::CheckSmi(Register src) {
ASSERT_EQ(0, kSmiTag);
testb(src, Immediate(kSmiTagMask));
- return not_zero;
+ return zero;
}
Condition MacroAssembler::CheckPositiveSmi(Register src) {
ASSERT_EQ(0, kSmiTag);
- testl(src, Immediate(static_cast<uint32_t>(0x80000000u | kSmiTagMask)));
+ movq(kScratchRegister, src);
+ rol(kScratchRegister, Immediate(1));
+ testl(kScratchRegister, Immediate(0x03));
return zero;
}
-Condition MacroAssembler::CheckNotPositiveSmi(Register src) {
- ASSERT_EQ(0, kSmiTag);
- testl(src, Immediate(static_cast<uint32_t>(0x80000000u | kSmiTagMask)));
- return not_zero;
-}
-
-
Condition MacroAssembler::CheckBothSmi(Register first, Register second) {
if (first.is(second)) {
return CheckSmi(first);
}
movl(kScratchRegister, first);
orl(kScratchRegister, second);
- return CheckSmi(kScratchRegister);
-}
-
-
-Condition MacroAssembler::CheckNotBothSmi(Register first, Register second) {
- ASSERT_EQ(0, kSmiTag);
- if (first.is(second)) {
- return CheckNotSmi(first);
- }
- movl(kScratchRegister, first);
- or_(kScratchRegister, second);
- return CheckNotSmi(kScratchRegister);
+ testb(kScratchRegister, Immediate(kSmiTagMask));
+ return zero;
}
Condition MacroAssembler::CheckIsMinSmi(Register src) {
ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
- cmpl(src, Immediate(0x40000000));
+ movq(kScratchRegister, src);
+ rol(kScratchRegister, Immediate(1));
+ cmpq(kScratchRegister, Immediate(1));
return equal;
}
-Condition MacroAssembler::CheckSmiEqualsConstant(Register src, int constant) {
- if (constant == 0) {
- testl(src, src);
- return zero;
- }
- if (Smi::IsValid(constant)) {
- cmpl(src, Immediate(Smi::FromInt(constant)));
- return zero;
- }
- // Can't be equal.
- UNREACHABLE();
- return no_condition;
-}
-
-
-Condition MacroAssembler::CheckSmiGreaterEqualsConstant(Register src,
- int constant) {
- if (constant == 0) {
- testl(src, Immediate(static_cast<uint32_t>(0x80000000u)));
- return positive;
- }
- if (Smi::IsValid(constant)) {
- cmpl(src, Immediate(Smi::FromInt(constant)));
- return greater_equal;
- }
- // Can't be equal.
- UNREACHABLE();
- return no_condition;
-}
-
Condition MacroAssembler::CheckInteger32ValidSmiValue(Register src) {
- // A 32-bit integer value can be converted to a smi if it is in the
- // range [-2^30 .. 2^30-1]. That is equivalent to having its 32-bit
- // representation have bits 30 and 31 be equal.
- cmpl(src, Immediate(0xC0000000u));
- return positive;
+ // A 32-bit integer value can always be converted to a smi.
+ return always;
}
-void MacroAssembler::SmiNeg(Register dst,
- Register src,
- Label* on_not_smi_result) {
- if (!dst.is(src)) {
- movl(dst, src);
+void MacroAssembler::SmiNeg(Register dst, Register src, Label* on_smi_result) {
+ if (dst.is(src)) {
+ ASSERT(!dst.is(kScratchRegister));
+ movq(kScratchRegister, src);
+ neg(dst); // Low 32 bits are retained as zero by negation.
+ // Test if result is zero or Smi::kMinValue.
+ cmpq(dst, kScratchRegister);
+ j(not_equal, on_smi_result);
+ movq(src, kScratchRegister);
+ } else {
+ movq(dst, src);
+ neg(dst);
+ cmpq(dst, src);
+ // If the result is zero or Smi::kMinValue, negation failed to create a smi.
+ j(not_equal, on_smi_result);
}
- negl(dst);
- testl(dst, Immediate(0x7fffffff));
- // If the result is zero or 0x80000000, negation failed to create a smi.
- j(equal, on_not_smi_result);
}
@@ -662,42 +604,39 @@ void MacroAssembler::SmiAdd(Register dst,
Register src2,
Label* on_not_smi_result) {
ASSERT(!dst.is(src2));
- if (!dst.is(src1)) {
- movl(dst, src1);
- }
- addl(dst, src2);
- if (!dst.is(src1)) {
- j(overflow, on_not_smi_result);
- } else {
+ if (dst.is(src1)) {
+ addq(dst, src2);
Label smi_result;
j(no_overflow, &smi_result);
// Restore src1.
- subl(src1, src2);
+ subq(src1, src2);
jmp(on_not_smi_result);
bind(&smi_result);
+ } else {
+ movq(dst, src1);
+ addq(dst, src2);
+ j(overflow, on_not_smi_result);
}
}
-
void MacroAssembler::SmiSub(Register dst,
Register src1,
Register src2,
Label* on_not_smi_result) {
ASSERT(!dst.is(src2));
- if (!dst.is(src1)) {
- movl(dst, src1);
- }
- subl(dst, src2);
- if (!dst.is(src1)) {
- j(overflow, on_not_smi_result);
- } else {
+ if (dst.is(src1)) {
+ subq(dst, src2);
Label smi_result;
j(no_overflow, &smi_result);
// Restore src1.
- addl(src1, src2);
+ addq(src1, src2);
jmp(on_not_smi_result);
bind(&smi_result);
+ } else {
+ movq(dst, src1);
+ subq(dst, src2);
+ j(overflow, on_not_smi_result);
}
}
@@ -707,80 +646,137 @@ void MacroAssembler::SmiMul(Register dst,
Register src2,
Label* on_not_smi_result) {
ASSERT(!dst.is(src2));
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src1.is(kScratchRegister));
+ ASSERT(!src2.is(kScratchRegister));
if (dst.is(src1)) {
- movq(kScratchRegister, src1);
- }
- SmiToInteger32(dst, src1);
+ Label failure, zero_correct_result;
+ movq(kScratchRegister, src1); // Create backup for later testing.
+ SmiToInteger64(dst, src1);
+ imul(dst, src2);
+ j(overflow, &failure);
+
+ // Check for negative zero result. If product is zero, and one
+ // argument is negative, go to slow case.
+ Label correct_result;
+ testq(dst, dst);
+ j(not_zero, &correct_result);
- imull(dst, src2);
- j(overflow, on_not_smi_result);
+ movq(dst, kScratchRegister);
+ xor_(dst, src2);
+ j(positive, &zero_correct_result); // Result was positive zero.
- // 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.
- Label non_zero_result;
- testl(dst, dst);
- j(not_zero, &non_zero_result);
+ bind(&failure); // Reused failure exit, restores src1.
+ movq(src1, kScratchRegister);
+ jmp(on_not_smi_result);
- // Test whether either operand is negative (the other must be zero).
- orl(kScratchRegister, src2);
- j(negative, on_not_smi_result);
- bind(&non_zero_result);
+ bind(&zero_correct_result);
+ xor_(dst, dst);
+
+ bind(&correct_result);
+ } else {
+ SmiToInteger64(dst, src1);
+ imul(dst, src2);
+ j(overflow, on_not_smi_result);
+ // Check for negative zero result. If product is zero, and one
+ // argument is negative, go to slow case.
+ Label correct_result;
+ testq(dst, dst);
+ j(not_zero, &correct_result);
+ // One of src1 and src2 is zero, the check whether the other is
+ // negative.
+ movq(kScratchRegister, src1);
+ xor_(kScratchRegister, src2);
+ j(negative, on_not_smi_result);
+ bind(&correct_result);
+ }
}
void MacroAssembler::SmiTryAddConstant(Register dst,
Register src,
- int32_t constant,
+ Smi* constant,
Label* on_not_smi_result) {
// Does not assume that src is a smi.
- ASSERT_EQ(1, kSmiTagMask);
+ ASSERT_EQ(static_cast<intptr_t>(1), kSmiTagMask);
ASSERT_EQ(0, kSmiTag);
- ASSERT(Smi::IsValid(constant));
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src.is(kScratchRegister));
- Register tmp = (src.is(dst) ? kScratchRegister : dst);
- movl(tmp, src);
- addl(tmp, Immediate(Smi::FromInt(constant)));
- if (tmp.is(kScratchRegister)) {
- j(overflow, on_not_smi_result);
- testl(tmp, Immediate(kSmiTagMask));
- j(not_zero, on_not_smi_result);
- movl(dst, tmp);
+ JumpIfNotSmi(src, on_not_smi_result);
+ Register tmp = (dst.is(src) ? kScratchRegister : dst);
+ Move(tmp, constant);
+ addq(tmp, src);
+ j(overflow, on_not_smi_result);
+ if (dst.is(src)) {
+ movq(dst, tmp);
+ }
+}
+
+
+void MacroAssembler::SmiAddConstant(Register dst, Register src, Smi* constant) {
+ if (constant->value() == 0) {
+ if (!dst.is(src)) {
+ movq(dst, src);
+ }
+ } else if (dst.is(src)) {
+ ASSERT(!dst.is(kScratchRegister));
+
+ Move(kScratchRegister, constant);
+ addq(dst, kScratchRegister);
} else {
- movl(kScratchRegister, Immediate(kSmiTagMask));
- cmovl(overflow, dst, kScratchRegister);
- testl(dst, kScratchRegister);
- j(not_zero, on_not_smi_result);
+ Move(dst, constant);
+ addq(dst, src);
}
}
void MacroAssembler::SmiAddConstant(Register dst,
Register src,
- int32_t constant,
+ Smi* constant,
Label* on_not_smi_result) {
- ASSERT(Smi::IsValid(constant));
- if (on_not_smi_result == NULL) {
- if (dst.is(src)) {
- movl(dst, src);
- } else {
- lea(dst, Operand(src, constant << kSmiTagSize));
+ if (constant->value() == 0) {
+ if (!dst.is(src)) {
+ movq(dst, src);
}
+ } else if (dst.is(src)) {
+ ASSERT(!dst.is(kScratchRegister));
+
+ Move(kScratchRegister, constant);
+ addq(dst, kScratchRegister);
+ Label result_ok;
+ j(no_overflow, &result_ok);
+ subq(dst, kScratchRegister);
+ jmp(on_not_smi_result);
+ bind(&result_ok);
} else {
+ Move(dst, constant);
+ addq(dst, src);
+ j(overflow, on_not_smi_result);
+ }
+}
+
+
+void MacroAssembler::SmiSubConstant(Register dst, Register src, Smi* constant) {
+ if (constant->value() == 0) {
if (!dst.is(src)) {
- movl(dst, src);
+ movq(dst, src);
}
- addl(dst, Immediate(Smi::FromInt(constant)));
- if (!dst.is(src)) {
- j(overflow, on_not_smi_result);
+ } else if (dst.is(src)) {
+ ASSERT(!dst.is(kScratchRegister));
+
+ Move(kScratchRegister, constant);
+ subq(dst, kScratchRegister);
+ } else {
+ // Subtract by adding the negative, to do it in two operations.
+ if (constant->value() == Smi::kMinValue) {
+ Move(kScratchRegister, constant);
+ movq(dst, src);
+ subq(dst, kScratchRegister);
} else {
- Label result_ok;
- j(no_overflow, &result_ok);
- subl(dst, Immediate(Smi::FromInt(constant)));
- jmp(on_not_smi_result);
- bind(&result_ok);
+ Move(dst, Smi::FromInt(-constant->value()));
+ addq(dst, src);
}
}
}
@@ -788,24 +784,33 @@ void MacroAssembler::SmiAddConstant(Register dst,
void MacroAssembler::SmiSubConstant(Register dst,
Register src,
- int32_t constant,
+ Smi* constant,
Label* on_not_smi_result) {
- ASSERT(Smi::IsValid(constant));
- Smi* smi_value = Smi::FromInt(constant);
- if (dst.is(src)) {
- // Optimistic subtract - may change value of dst register,
- // if it has garbage bits in the higher half, but will not change
- // the value as a tagged smi.
- subl(dst, Immediate(smi_value));
- if (on_not_smi_result != NULL) {
- Label add_success;
- j(no_overflow, &add_success);
- addl(dst, Immediate(smi_value));
- jmp(on_not_smi_result);
- bind(&add_success);
+ if (constant->value() == 0) {
+ if (!dst.is(src)) {
+ movq(dst, src);
}
+ } else if (dst.is(src)) {
+ ASSERT(!dst.is(kScratchRegister));
+
+ Move(kScratchRegister, constant);
+ subq(dst, kScratchRegister);
+ Label sub_success;
+ j(no_overflow, &sub_success);
+ addq(src, kScratchRegister);
+ jmp(on_not_smi_result);
+ bind(&sub_success);
} else {
- UNIMPLEMENTED(); // Not used yet.
+ if (constant->value() == Smi::kMinValue) {
+ Move(kScratchRegister, constant);
+ movq(dst, src);
+ subq(dst, kScratchRegister);
+ j(overflow, on_not_smi_result);
+ } else {
+ Move(dst, Smi::FromInt(-(constant->value())));
+ addq(dst, src);
+ j(overflow, on_not_smi_result);
+ }
}
}
@@ -814,38 +819,61 @@ void MacroAssembler::SmiDiv(Register dst,
Register src1,
Register src2,
Label* on_not_smi_result) {
+ ASSERT(!src1.is(kScratchRegister));
+ ASSERT(!src2.is(kScratchRegister));
+ ASSERT(!dst.is(kScratchRegister));
ASSERT(!src2.is(rax));
ASSERT(!src2.is(rdx));
ASSERT(!src1.is(rdx));
// Check for 0 divisor (result is +/-Infinity).
Label positive_divisor;
- testl(src2, src2);
- j(zero, on_not_smi_result);
- j(positive, &positive_divisor);
- // Check for negative zero result. If the dividend is zero, and the
- // divisor is negative, return a floating point negative zero.
- testl(src1, src1);
+ testq(src2, src2);
j(zero, on_not_smi_result);
- bind(&positive_divisor);
- // Sign extend src1 into edx:eax.
- if (!src1.is(rax)) {
- movl(rax, src1);
+ if (src1.is(rax)) {
+ movq(kScratchRegister, src1);
}
- cdq();
+ SmiToInteger32(rax, src1);
+ // We need to rule out dividing Smi::kMinValue by -1, since that would
+ // overflow in idiv and raise an exception.
+ // We combine this with negative zero test (negative zero only happens
+ // when dividing zero by a negative number).
+
+ // We overshoot a little and go to slow case if we divide min-value
+ // by any negative value, not just -1.
+ Label safe_div;
+ testl(rax, Immediate(0x7fffffff));
+ j(not_zero, &safe_div);
+ testq(src2, src2);
+ if (src1.is(rax)) {
+ j(positive, &safe_div);
+ movq(src1, kScratchRegister);
+ jmp(on_not_smi_result);
+ } else {
+ j(negative, on_not_smi_result);
+ }
+ bind(&safe_div);
+ SmiToInteger32(src2, src2);
+ // Sign extend src1 into edx:eax.
+ cdq();
idivl(src2);
- // 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.
- ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
- cmpl(rax, Immediate(0x40000000));
- j(equal, on_not_smi_result);
+ Integer32ToSmi(src2, src2);
// Check that the remainder is zero.
testl(rdx, rdx);
- j(not_zero, on_not_smi_result);
- // Tag the result and store it in the destination register.
+ if (src1.is(rax)) {
+ Label smi_result;
+ j(zero, &smi_result);
+ movq(src1, kScratchRegister);
+ jmp(on_not_smi_result);
+ bind(&smi_result);
+ } else {
+ j(not_zero, on_not_smi_result);
+ }
+ if (!dst.is(src1) && src1.is(rax)) {
+ movq(src1, kScratchRegister);
+ }
Integer32ToSmi(dst, rax);
}
@@ -860,109 +888,136 @@ void MacroAssembler::SmiMod(Register dst,
ASSERT(!src2.is(rax));
ASSERT(!src2.is(rdx));
ASSERT(!src1.is(rdx));
+ ASSERT(!src1.is(src2));
- testl(src2, src2);
+ testq(src2, src2);
j(zero, on_not_smi_result);
if (src1.is(rax)) {
- // Mist remember the value to see if a zero result should
- // be a negative zero.
- movl(kScratchRegister, rax);
- } else {
- movl(rax, src1);
+ movq(kScratchRegister, src1);
}
+ SmiToInteger32(rax, src1);
+ SmiToInteger32(src2, src2);
+
+ // Test for the edge case of dividing Smi::kMinValue by -1 (will overflow).
+ Label safe_div;
+ cmpl(rax, Immediate(Smi::kMinValue));
+ j(not_equal, &safe_div);
+ cmpl(src2, Immediate(-1));
+ j(not_equal, &safe_div);
+ // Retag inputs and go slow case.
+ Integer32ToSmi(src2, src2);
+ if (src1.is(rax)) {
+ movq(src1, kScratchRegister);
+ }
+ jmp(on_not_smi_result);
+ bind(&safe_div);
+
// Sign extend eax into edx:eax.
cdq();
idivl(src2);
- // Check for a negative zero result. If the result is zero, and the
- // dividend is negative, return a floating point negative zero.
- Label non_zero_result;
- testl(rdx, rdx);
- j(not_zero, &non_zero_result);
+ // Restore smi tags on inputs.
+ Integer32ToSmi(src2, src2);
if (src1.is(rax)) {
- testl(kScratchRegister, kScratchRegister);
- } else {
- testl(src1, src1);
+ movq(src1, kScratchRegister);
}
+ // Check for a negative zero result. If the result is zero, and the
+ // dividend is negative, go slow to return a floating point negative zero.
+ Label smi_result;
+ testl(rdx, rdx);
+ j(not_zero, &smi_result);
+ testq(src1, src1);
j(negative, on_not_smi_result);
- bind(&non_zero_result);
- if (!dst.is(rdx)) {
- movl(dst, rdx);
- }
+ bind(&smi_result);
+ Integer32ToSmi(dst, rdx);
}
void MacroAssembler::SmiNot(Register dst, Register src) {
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src.is(kScratchRegister));
+ // Set tag and padding bits before negating, so that they are zero afterwards.
+ movl(kScratchRegister, Immediate(~0));
if (dst.is(src)) {
- not_(dst);
- // Remove inverted smi-tag. The mask is sign-extended to 64 bits.
- xor_(src, Immediate(kSmiTagMask));
+ xor_(dst, kScratchRegister);
} else {
- ASSERT_EQ(0, kSmiTag);
- lea(dst, Operand(src, kSmiTagMask));
- not_(dst);
+ lea(dst, Operand(src, kScratchRegister, times_1, 0));
}
+ not_(dst);
}
void MacroAssembler::SmiAnd(Register dst, Register src1, Register src2) {
+ ASSERT(!dst.is(src2));
if (!dst.is(src1)) {
- movl(dst, src1);
+ movq(dst, src1);
}
and_(dst, src2);
}
-void MacroAssembler::SmiAndConstant(Register dst, Register src, int constant) {
- ASSERT(Smi::IsValid(constant));
- if (!dst.is(src)) {
- movl(dst, src);
+void MacroAssembler::SmiAndConstant(Register dst, Register src, Smi* constant) {
+ if (constant->value() == 0) {
+ xor_(dst, dst);
+ } else if (dst.is(src)) {
+ ASSERT(!dst.is(kScratchRegister));
+ Move(kScratchRegister, constant);
+ and_(dst, kScratchRegister);
+ } else {
+ Move(dst, constant);
+ and_(dst, src);
}
- and_(dst, Immediate(Smi::FromInt(constant)));
}
void MacroAssembler::SmiOr(Register dst, Register src1, Register src2) {
if (!dst.is(src1)) {
- movl(dst, src1);
+ movq(dst, src1);
}
or_(dst, src2);
}
-void MacroAssembler::SmiOrConstant(Register dst, Register src, int constant) {
- ASSERT(Smi::IsValid(constant));
- if (!dst.is(src)) {
- movl(dst, src);
+void MacroAssembler::SmiOrConstant(Register dst, Register src, Smi* constant) {
+ if (dst.is(src)) {
+ ASSERT(!dst.is(kScratchRegister));
+ Move(kScratchRegister, constant);
+ or_(dst, kScratchRegister);
+ } else {
+ Move(dst, constant);
+ or_(dst, src);
}
- or_(dst, Immediate(Smi::FromInt(constant)));
}
+
void MacroAssembler::SmiXor(Register dst, Register src1, Register src2) {
if (!dst.is(src1)) {
- movl(dst, src1);
+ movq(dst, src1);
}
xor_(dst, src2);
}
-void MacroAssembler::SmiXorConstant(Register dst, Register src, int constant) {
- ASSERT(Smi::IsValid(constant));
- if (!dst.is(src)) {
- movl(dst, src);
+void MacroAssembler::SmiXorConstant(Register dst, Register src, Smi* constant) {
+ if (dst.is(src)) {
+ ASSERT(!dst.is(kScratchRegister));
+ Move(kScratchRegister, constant);
+ xor_(dst, kScratchRegister);
+ } else {
+ Move(dst, constant);
+ xor_(dst, src);
}
- xor_(dst, Immediate(Smi::FromInt(constant)));
}
-
void MacroAssembler::SmiShiftArithmeticRightConstant(Register dst,
Register src,
int shift_value) {
+ ASSERT(is_uint5(shift_value));
if (shift_value > 0) {
if (dst.is(src)) {
- sarl(dst, Immediate(shift_value));
- and_(dst, Immediate(~kSmiTagMask));
+ sar(dst, Immediate(shift_value + kSmiShift));
+ shl(dst, Immediate(kSmiShift));
} else {
UNIMPLEMENTED(); // Not used.
}
@@ -978,20 +1033,13 @@ void MacroAssembler::SmiShiftLogicalRightConstant(Register dst,
if (dst.is(src)) {
UNIMPLEMENTED(); // Not used.
} else {
- movl(dst, src);
- // Untag the smi.
- sarl(dst, Immediate(kSmiTagSize));
- if (shift_value < 2) {
- // A negative Smi shifted right two is in the positive Smi range,
- // but if shifted only by zero or one, it never is.
+ movq(dst, src);
+ if (shift_value == 0) {
+ testq(dst, dst);
j(negative, on_not_smi_result);
}
- if (shift_value > 0) {
- // Do the right shift on the integer value.
- shrl(dst, Immediate(shift_value));
- }
- // Re-tag the result.
- addl(dst, dst);
+ shr(dst, Immediate(shift_value + kSmiShift));
+ shl(dst, Immediate(kSmiShift));
}
}
@@ -1000,20 +1048,11 @@ void MacroAssembler::SmiShiftLeftConstant(Register dst,
Register src,
int shift_value,
Label* on_not_smi_result) {
- if (dst.is(src)) {
- UNIMPLEMENTED(); // Not used.
- } else {
- movl(dst, src);
- if (shift_value > 0) {
- // Treat dst as an untagged integer value equal to two times the
- // smi value of src, i.e., already shifted left by one.
- if (shift_value > 1) {
- shll(dst, Immediate(shift_value - 1));
- }
- // Convert int result to Smi, checking that it is in smi range.
- ASSERT(kSmiTagSize == 1); // adjust code if not the case
- Integer32ToSmi(dst, dst, on_not_smi_result);
- }
+ if (!dst.is(src)) {
+ movq(dst, src);
+ }
+ if (shift_value > 0) {
+ shl(dst, Immediate(shift_value));
}
}
@@ -1024,23 +1063,14 @@ void MacroAssembler::SmiShiftLeft(Register dst,
Label* on_not_smi_result) {
ASSERT(!dst.is(rcx));
Label result_ok;
- // Untag both operands.
- SmiToInteger32(dst, src1);
- SmiToInteger32(rcx, src2);
- shll(dst);
- // Check that the *signed* result fits in a smi.
- Condition is_valid = CheckInteger32ValidSmiValue(dst);
- j(is_valid, &result_ok);
- // Restore the relevant bits of the source registers
- // and call the slow version.
- if (dst.is(src1)) {
- shrl(dst);
- Integer32ToSmi(dst, dst);
+ // Untag shift amount.
+ if (!dst.is(src1)) {
+ movq(dst, src1);
}
- Integer32ToSmi(rcx, rcx);
- jmp(on_not_smi_result);
- bind(&result_ok);
- Integer32ToSmi(dst, dst);
+ SmiToInteger32(rcx, src2);
+ // Shift amount specified by lower 5 bits, not six as the shl opcode.
+ and_(rcx, Immediate(0x1f));
+ shl(dst);
}
@@ -1048,48 +1078,62 @@ void MacroAssembler::SmiShiftLogicalRight(Register dst,
Register src1,
Register src2,
Label* on_not_smi_result) {
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src1.is(kScratchRegister));
+ ASSERT(!src2.is(kScratchRegister));
ASSERT(!dst.is(rcx));
Label result_ok;
- // Untag both operands.
- SmiToInteger32(dst, src1);
+ if (src1.is(rcx) || src2.is(rcx)) {
+ movq(kScratchRegister, rcx);
+ }
+ if (!dst.is(src1)) {
+ movq(dst, src1);
+ }
SmiToInteger32(rcx, src2);
-
- shrl(dst);
- // Check that the *unsigned* result fits in a smi.
- // I.e., that it is a valid positive smi value. The positive smi
- // values are 0..0x3fffffff, i.e., neither of the top-most two
- // bits can be set.
- //
- // 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.
- testl(dst, Immediate(0xc0000000));
- j(zero, &result_ok);
- if (dst.is(src1)) {
- shll(dst);
- Integer32ToSmi(dst, dst);
+ orl(rcx, Immediate(kSmiShift));
+ shr(dst); // Shift is rcx modulo 0x1f + 32.
+ shl(dst, Immediate(kSmiShift));
+ testq(dst, dst);
+ if (src1.is(rcx) || src2.is(rcx)) {
+ Label positive_result;
+ j(positive, &positive_result);
+ if (src1.is(rcx)) {
+ movq(src1, kScratchRegister);
+ } else {
+ movq(src2, kScratchRegister);
+ }
+ jmp(on_not_smi_result);
+ bind(&positive_result);
+ } else {
+ j(negative, on_not_smi_result); // src2 was zero and src1 negative.
}
- Integer32ToSmi(rcx, rcx);
- jmp(on_not_smi_result);
- bind(&result_ok);
- // Smi-tag the result in answer.
- Integer32ToSmi(dst, dst);
}
void MacroAssembler::SmiShiftArithmeticRight(Register dst,
Register src1,
Register src2) {
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src1.is(kScratchRegister));
+ ASSERT(!src2.is(kScratchRegister));
ASSERT(!dst.is(rcx));
- // Untag both operands.
- SmiToInteger32(dst, src1);
+ if (src1.is(rcx)) {
+ movq(kScratchRegister, src1);
+ } else if (src2.is(rcx)) {
+ movq(kScratchRegister, src2);
+ }
+ if (!dst.is(src1)) {
+ movq(dst, src1);
+ }
SmiToInteger32(rcx, src2);
- // Shift as integer.
- sarl(dst);
- // Retag result.
- Integer32ToSmi(dst, dst);
+ orl(rcx, Immediate(kSmiShift));
+ sar(dst); // Shift 32 + original rcx & 0x1f.
+ shl(dst, Immediate(kSmiShift));
+ if (src1.is(rcx)) {
+ movq(src1, kScratchRegister);
+ } else if (src2.is(rcx)) {
+ movq(src2, kScratchRegister);
+ }
}
@@ -1097,21 +1141,27 @@ void MacroAssembler::SelectNonSmi(Register dst,
Register src1,
Register src2,
Label* on_not_smis) {
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src1.is(kScratchRegister));
+ ASSERT(!src2.is(kScratchRegister));
ASSERT(!dst.is(src1));
ASSERT(!dst.is(src2));
// Both operands must not be smis.
#ifdef DEBUG
- Condition not_both_smis = CheckNotBothSmi(src1, src2);
- Check(not_both_smis, "Both registers were smis.");
+ if (allow_stub_calls()) { // Check contains a stub call.
+ Condition not_both_smis = NegateCondition(CheckBothSmi(src1, src2));
+ Check(not_both_smis, "Both registers were smis in SelectNonSmi.");
+ }
#endif
ASSERT_EQ(0, kSmiTag);
ASSERT_EQ(0, Smi::FromInt(0));
- movq(kScratchRegister, Immediate(kSmiTagMask));
+ movl(kScratchRegister, Immediate(kSmiTagMask));
and_(kScratchRegister, src1);
testl(kScratchRegister, src2);
+ // If non-zero then both are smis.
j(not_zero, on_not_smis);
- // One operand is a smi.
+ // Exactly one operand is a smi.
ASSERT_EQ(1, static_cast<int>(kSmiTagMask));
// kScratchRegister still holds src1 & kSmiTag, which is either zero or one.
subq(kScratchRegister, Immediate(1));
@@ -1121,71 +1171,117 @@ void MacroAssembler::SelectNonSmi(Register dst,
and_(dst, kScratchRegister);
// If src1 is a smi, dst holds src1 ^ src2, else it is zero.
xor_(dst, src1);
- // If src1 is a smi, dst is src2, else it is src1, i.e., a non-smi.
+ // If src1 is a smi, dst is src2, else it is src1, i.e., the non-smi.
}
-
-SmiIndex MacroAssembler::SmiToIndex(Register dst, Register src, int shift) {
+SmiIndex MacroAssembler::SmiToIndex(Register dst,
+ Register src,
+ int shift) {
ASSERT(is_uint6(shift));
- if (shift == 0) { // times_1.
- SmiToInteger32(dst, src);
- return SmiIndex(dst, times_1);
- }
- if (shift <= 4) { // 2 - 16 times multiplier is handled using ScaleFactor.
- // We expect that all smis are actually zero-padded. If this holds after
- // checking, this line can be omitted.
- movl(dst, src); // Ensure that the smi is zero-padded.
- return SmiIndex(dst, static_cast<ScaleFactor>(shift - kSmiTagSize));
- }
- // Shift by shift-kSmiTagSize.
- movl(dst, src); // Ensure that the smi is zero-padded.
- shl(dst, Immediate(shift - kSmiTagSize));
+ // There is a possible optimization if shift is in the range 60-63, but that
+ // will (and must) never happen.
+ if (!dst.is(src)) {
+ movq(dst, src);
+ }
+ if (shift < kSmiShift) {
+ sar(dst, Immediate(kSmiShift - shift));
+ } else {
+ shl(dst, Immediate(shift - kSmiShift));
+ }
return SmiIndex(dst, times_1);
}
-
SmiIndex MacroAssembler::SmiToNegativeIndex(Register dst,
Register src,
int shift) {
// Register src holds a positive smi.
ASSERT(is_uint6(shift));
- if (shift == 0) { // times_1.
- SmiToInteger32(dst, src);
- neg(dst);
- return SmiIndex(dst, times_1);
- }
- if (shift <= 4) { // 2 - 16 times multiplier is handled using ScaleFactor.
- movl(dst, src);
- neg(dst);
- return SmiIndex(dst, static_cast<ScaleFactor>(shift - kSmiTagSize));
+ if (!dst.is(src)) {
+ movq(dst, src);
}
- // Shift by shift-kSmiTagSize.
- movl(dst, src);
neg(dst);
- shl(dst, Immediate(shift - kSmiTagSize));
+ if (shift < kSmiShift) {
+ sar(dst, Immediate(kSmiShift - shift));
+ } else {
+ shl(dst, Immediate(shift - kSmiShift));
+ }
return SmiIndex(dst, times_1);
}
+void MacroAssembler::JumpIfSmi(Register src, Label* on_smi) {
+ ASSERT_EQ(0, kSmiTag);
+ Condition smi = CheckSmi(src);
+ j(smi, on_smi);
+}
+
+
+void MacroAssembler::JumpIfNotSmi(Register src, Label* on_not_smi) {
+ Condition smi = CheckSmi(src);
+ j(NegateCondition(smi), on_not_smi);
+}
+
+
+void MacroAssembler::JumpIfNotPositiveSmi(Register src,
+ Label* on_not_positive_smi) {
+ Condition positive_smi = CheckPositiveSmi(src);
+ j(NegateCondition(positive_smi), on_not_positive_smi);
+}
+
+
+void MacroAssembler::JumpIfSmiEqualsConstant(Register src,
+ Smi* constant,
+ Label* on_equals) {
+ SmiCompare(src, constant);
+ j(equal, on_equals);
+}
+
+
+void MacroAssembler::JumpIfNotValidSmiValue(Register src, Label* on_invalid) {
+ Condition is_valid = CheckInteger32ValidSmiValue(src);
+ j(NegateCondition(is_valid), on_invalid);
+}
+
+
+void MacroAssembler::JumpIfNotBothSmi(Register src1, Register src2,
+ Label* on_not_both_smi) {
+ Condition both_smi = CheckBothSmi(src1, src2);
+ j(NegateCondition(both_smi), on_not_both_smi);
+}
bool MacroAssembler::IsUnsafeSmi(Smi* value) {
return false;
}
+
void MacroAssembler::LoadUnsafeSmi(Register dst, Smi* source) {
UNIMPLEMENTED();
}
+void MacroAssembler::Move(Register dst, Smi* source) {
+ if (IsUnsafeSmi(source)) {
+ LoadUnsafeSmi(dst, source);
+ } else {
+ Set(dst, reinterpret_cast<int64_t>(source));
+ }
+}
+
+
+void MacroAssembler::Move(const Operand& dst, Smi* source) {
+ if (IsUnsafeSmi(source)) {
+ LoadUnsafeSmi(kScratchRegister, source);
+ movq(dst, kScratchRegister);
+ } else {
+ Set(dst, reinterpret_cast<int64_t>(source));
+ }
+}
+
+
void MacroAssembler::Move(Register dst, Handle<Object> source) {
ASSERT(!source->IsFailure());
if (source->IsSmi()) {
- if (IsUnsafeSmi(source)) {
- LoadUnsafeSmi(dst, source);
- } else {
- int32_t smi = static_cast<int32_t>(reinterpret_cast<intptr_t>(*source));
- movq(dst, Immediate(smi));
- }
+ Move(dst, Smi::cast(*source));
} else {
movq(dst, source, RelocInfo::EMBEDDED_OBJECT);
}
@@ -1193,9 +1289,9 @@ void MacroAssembler::Move(Register dst, Handle<Object> source) {
void MacroAssembler::Move(const Operand& dst, Handle<Object> source) {
+ ASSERT(!source->IsFailure());
if (source->IsSmi()) {
- int32_t smi = static_cast<int32_t>(reinterpret_cast<intptr_t>(*source));
- movq(dst, Immediate(smi));
+ Move(dst, Smi::cast(*source));
} else {
movq(kScratchRegister, source, RelocInfo::EMBEDDED_OBJECT);
movq(dst, kScratchRegister);
@@ -1204,21 +1300,18 @@ void MacroAssembler::Move(const Operand& dst, Handle<Object> source) {
void MacroAssembler::Cmp(Register dst, Handle<Object> source) {
- Move(kScratchRegister, source);
- cmpq(dst, kScratchRegister);
+ if (source->IsSmi()) {
+ SmiCompare(dst, Smi::cast(*source));
+ } else {
+ Move(kScratchRegister, source);
+ cmpq(dst, kScratchRegister);
+ }
}
void MacroAssembler::Cmp(const Operand& dst, Handle<Object> source) {
if (source->IsSmi()) {
- if (IsUnsafeSmi(source)) {
- LoadUnsafeSmi(kScratchRegister, source);
- cmpl(dst, kScratchRegister);
- } else {
- // For smi-comparison, it suffices to compare the low 32 bits.
- int32_t smi = static_cast<int32_t>(reinterpret_cast<intptr_t>(*source));
- cmpl(dst, Immediate(smi));
- }
+ SmiCompare(dst, Smi::cast(*source));
} else {
ASSERT(source->IsHeapObject());
movq(kScratchRegister, source, RelocInfo::EMBEDDED_OBJECT);
@@ -1229,13 +1322,7 @@ void MacroAssembler::Cmp(const Operand& dst, Handle<Object> source) {
void MacroAssembler::Push(Handle<Object> source) {
if (source->IsSmi()) {
- if (IsUnsafeSmi(source)) {
- LoadUnsafeSmi(kScratchRegister, source);
- push(kScratchRegister);
- } else {
- int32_t smi = static_cast<int32_t>(reinterpret_cast<intptr_t>(*source));
- push(Immediate(smi));
- }
+ Push(Smi::cast(*source));
} else {
ASSERT(source->IsHeapObject());
movq(kScratchRegister, source, RelocInfo::EMBEDDED_OBJECT);
@@ -1249,8 +1336,29 @@ void MacroAssembler::Push(Smi* source) {
LoadUnsafeSmi(kScratchRegister, source);
push(kScratchRegister);
} else {
- int32_t smi = static_cast<int32_t>(reinterpret_cast<intptr_t>(source));
- push(Immediate(smi));
+ intptr_t smi = reinterpret_cast<intptr_t>(source);
+ if (is_int32(smi)) {
+ push(Immediate(static_cast<int32_t>(smi)));
+ } else {
+ Set(kScratchRegister, smi);
+ push(kScratchRegister);
+ }
+ }
+}
+
+
+void MacroAssembler::Test(const Operand& src, Smi* source) {
+ if (IsUnsafeSmi(source)) {
+ LoadUnsafeSmi(kScratchRegister, source);
+ testq(src, kScratchRegister);
+ } else {
+ intptr_t smi = reinterpret_cast<intptr_t>(source);
+ if (is_int32(smi)) {
+ testl(src, Immediate(static_cast<int32_t>(smi)));
+ } else {
+ Move(kScratchRegister, source);
+ testq(src, kScratchRegister);
+ }
}
}
@@ -1446,7 +1554,6 @@ void MacroAssembler::DecrementCounter(StatsCounter* counter, int value) {
}
}
-
#ifdef ENABLE_DEBUGGER_SUPPORT
void MacroAssembler::PushRegistersFromMemory(RegList regs) {
@@ -1463,6 +1570,7 @@ void MacroAssembler::PushRegistersFromMemory(RegList regs) {
}
}
+
void MacroAssembler::SaveRegistersToMemory(RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Copy the content of registers to memory location.
@@ -1545,8 +1653,11 @@ void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) {
// arguments match the expected number of arguments. Fake a
// parameter count to avoid emitting code to do the check.
ParameterCount expected(0);
- InvokeCode(Handle<Code>(code), expected, expected,
- RelocInfo::CODE_TARGET, flag);
+ InvokeCode(Handle<Code>(code),
+ expected,
+ expected,
+ RelocInfo::CODE_TARGET,
+ flag);
const char* name = Builtins::GetName(id);
int argc = Builtins::GetArgumentsCount(id);
@@ -1578,7 +1689,7 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
} else {
movq(rax, Immediate(actual.immediate()));
if (expected.immediate() ==
- SharedFunctionInfo::kDontAdaptArgumentsSentinel) {
+ SharedFunctionInfo::kDontAdaptArgumentsSentinel) {
// Don't worry about adapting arguments for built-ins that
// don't want that done. Skip adaption code by making it look
// like we have a match between expected and actual number of
@@ -1684,7 +1795,7 @@ void MacroAssembler::EnterFrame(StackFrame::Type type) {
push(rbp);
movq(rbp, rsp);
push(rsi); // Context.
- push(Immediate(Smi::FromInt(type)));
+ Push(Smi::FromInt(type));
movq(kScratchRegister, CodeObject(), RelocInfo::EMBEDDED_OBJECT);
push(kScratchRegister);
if (FLAG_debug_code) {
@@ -1699,7 +1810,7 @@ void MacroAssembler::EnterFrame(StackFrame::Type type) {
void MacroAssembler::LeaveFrame(StackFrame::Type type) {
if (FLAG_debug_code) {
- movq(kScratchRegister, Immediate(Smi::FromInt(type)));
+ Move(kScratchRegister, Smi::FromInt(type));
cmpq(Operand(rbp, StandardFrameConstants::kMarkerOffset), kScratchRegister);
Check(equal, "stack frame types must match");
}
@@ -1708,7 +1819,6 @@ void MacroAssembler::LeaveFrame(StackFrame::Type type) {
}
-
void MacroAssembler::EnterExitFrame(StackFrame::Type type, int result_size) {
ASSERT(type == StackFrame::EXIT || type == StackFrame::EXIT_DEBUG);
@@ -1721,7 +1831,7 @@ void MacroAssembler::EnterExitFrame(StackFrame::Type type, int result_size) {
movq(rbp, rsp);
// Reserve room for entry stack pointer and push the debug marker.
- ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize);
+ ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize);
push(Immediate(0)); // saved entry sp, patched before call
push(Immediate(type == StackFrame::EXIT_DEBUG ? 1 : 0));
@@ -1834,8 +1944,10 @@ void MacroAssembler::LeaveExitFrame(StackFrame::Type type, int result_size) {
}
-Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
- JSObject* holder, Register holder_reg,
+Register MacroAssembler::CheckMaps(JSObject* object,
+ Register object_reg,
+ JSObject* holder,
+ Register holder_reg,
Register scratch,
Label* miss) {
// Make sure there's no overlap between scratch and the other
@@ -1901,8 +2013,7 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
}
// Check the holder map.
- Cmp(FieldOperand(reg, HeapObject::kMapOffset),
- Handle<Map>(holder->map()));
+ Cmp(FieldOperand(reg, HeapObject::kMapOffset), Handle<Map>(holder->map()));
j(not_equal, miss);
// Log the check depth.
@@ -1919,8 +2030,6 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
}
-
-
void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
Register scratch,
Label* miss) {
@@ -1974,8 +2083,8 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
movq(kScratchRegister,
FieldOperand(holder_reg, JSGlobalProxy::kContextOffset));
- int token_offset = Context::kHeaderSize +
- Context::SECURITY_TOKEN_INDEX * kPointerSize;
+ int token_offset =
+ Context::kHeaderSize + Context::SECURITY_TOKEN_INDEX * kPointerSize;
movq(scratch, FieldOperand(scratch, token_offset));
cmpq(scratch, FieldOperand(kScratchRegister, token_offset));
j(not_equal, miss);
@@ -2160,5 +2269,4 @@ CodePatcher::~CodePatcher() {
ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
}
-
} } // namespace v8::internal
diff --git a/deps/v8/src/x64/macro-assembler-x64.h b/deps/v8/src/x64/macro-assembler-x64.h
index adc136a7f..2aa4ce055 100644
--- a/deps/v8/src/x64/macro-assembler-x64.h
+++ b/deps/v8/src/x64/macro-assembler-x64.h
@@ -72,6 +72,18 @@ class MacroAssembler: public Assembler {
Register value,
Register scratch);
+ // Set the remembered set bit for [object+offset].
+ // The value is known to not be a smi.
+ // object is the object being stored into, value is the object being stored.
+ // If offset is zero, then the scratch register contains the array index into
+ // the elements array represented as a Smi.
+ // All registers are clobbered by the operation.
+ void RecordWriteNonSmi(Register object,
+ int offset,
+ Register value,
+ Register scratch);
+
+
#ifdef ENABLE_DEBUGGER_SUPPORT
// ---------------------------------------------------------------------------
// Debugger Support
@@ -146,11 +158,12 @@ class MacroAssembler: public Assembler {
// Tag an integer value if possible, or jump the integer value cannot be
// represented as a smi. Only uses the low 32 bit of the src registers.
+ // NOTICE: Destroys the dst register even if unsuccessful!
void Integer32ToSmi(Register dst, Register src, Label* on_overflow);
// Adds constant to src and tags the result as a smi.
// Result must be a valid smi.
- void Integer64AddToSmi(Register dst, Register src, int constant);
+ void Integer64PlusConstantToSmi(Register dst, Register src, int constant);
// Convert smi to 32-bit integer. I.e., not sign extended into
// high 32 bits of destination.
@@ -165,38 +178,31 @@ class MacroAssembler: public Assembler {
Register src,
int power);
+ // Simple comparison of smis.
+ void SmiCompare(Register dst, Register src);
+ void SmiCompare(Register dst, Smi* src);
+ void SmiCompare(const Operand& dst, Register src);
+ void SmiCompare(const Operand& dst, Smi* src);
+ // Sets sign and zero flags depending on value of smi in register.
+ void SmiTest(Register src);
+
// Functions performing a check on a known or potential smi. Returns
// a condition that is satisfied if the check is successful.
// Is the value a tagged smi.
Condition CheckSmi(Register src);
- // Is the value not a tagged smi.
- Condition CheckNotSmi(Register src);
-
// Is the value a positive tagged smi.
Condition CheckPositiveSmi(Register src);
- // Is the value not a positive tagged smi.
- Condition CheckNotPositiveSmi(Register src);
-
// Are both values are tagged smis.
Condition CheckBothSmi(Register first, Register second);
- // Is one of the values not a tagged smi.
- Condition CheckNotBothSmi(Register first, Register second);
-
// Is the value the minimum smi value (since we are using
// two's complement numbers, negating the value is known to yield
// a non-smi value).
Condition CheckIsMinSmi(Register src);
- // Check whether a tagged smi is equal to a constant.
- Condition CheckSmiEqualsConstant(Register src, int constant);
-
- // Check whether a tagged smi is greater than or equal to a constant.
- Condition CheckSmiGreaterEqualsConstant(Register src, int constant);
-
// Checks whether an 32-bit integer value is a valid for conversion
// to a smi.
Condition CheckInteger32ValidSmiValue(Register src);
@@ -216,15 +222,9 @@ class MacroAssembler: public Assembler {
// Jump to label if the value is not a positive tagged smi.
void JumpIfNotPositiveSmi(Register src, Label* on_not_smi);
- // Jump to label if the value is a tagged smi with value equal
+ // Jump to label if the value, which must be a tagged smi, has value equal
// to the constant.
- void JumpIfSmiEqualsConstant(Register src, int constant, Label* on_equals);
-
- // Jump to label if the value is a tagged smi with value greater than or equal
- // to the constant.
- void JumpIfSmiGreaterEqualsConstant(Register src,
- int constant,
- Label* on_equals);
+ void JumpIfSmiEqualsConstant(Register src, Smi* constant, Label* on_equals);
// Jump if either or both register are not smi values.
void JumpIfNotBothSmi(Register src1, Register src2, Label* on_not_both_smi);
@@ -239,29 +239,36 @@ class MacroAssembler: public Assembler {
// the label.
void SmiTryAddConstant(Register dst,
Register src,
- int32_t constant,
+ Smi* constant,
Label* on_not_smi_result);
+ // Add an integer constant to a tagged smi, giving a tagged smi as result.
+ // No overflow testing on the result is done.
+ void SmiAddConstant(Register dst, Register src, Smi* constant);
+
// Add an integer constant to a tagged smi, giving a tagged smi as result,
// or jumping to a label if the result cannot be represented by a smi.
- // If the label is NULL, no testing on the result is done.
void SmiAddConstant(Register dst,
Register src,
- int32_t constant,
+ Smi* constant,
Label* on_not_smi_result);
// Subtract an integer constant from a tagged smi, giving a tagged smi as
+ // result. No testing on the result is done.
+ void SmiSubConstant(Register dst, Register src, Smi* constant);
+
+ // Subtract an integer constant from a tagged smi, giving a tagged smi as
// result, or jumping to a label if the result cannot be represented by a smi.
- // If the label is NULL, no testing on the result is done.
void SmiSubConstant(Register dst,
Register src,
- int32_t constant,
+ Smi* constant,
Label* on_not_smi_result);
// Negating a smi can give a negative zero or too large positive value.
+ // NOTICE: This operation jumps on success, not failure!
void SmiNeg(Register dst,
Register src,
- Label* on_not_smi_result);
+ Label* on_smi_result);
// Adds smi values and return the result as a smi.
// If dst is src1, then src1 will be destroyed, even if
@@ -307,9 +314,9 @@ class MacroAssembler: public Assembler {
void SmiAnd(Register dst, Register src1, Register src2);
void SmiOr(Register dst, Register src1, Register src2);
void SmiXor(Register dst, Register src1, Register src2);
- void SmiAndConstant(Register dst, Register src1, int constant);
- void SmiOrConstant(Register dst, Register src1, int constant);
- void SmiXorConstant(Register dst, Register src1, int constant);
+ void SmiAndConstant(Register dst, Register src1, Smi* constant);
+ void SmiOrConstant(Register dst, Register src1, Smi* constant);
+ void SmiXorConstant(Register dst, Register src1, Smi* constant);
void SmiShiftLeftConstant(Register dst,
Register src,
@@ -367,20 +374,27 @@ class MacroAssembler: public Assembler {
// Converts a positive smi to a negative index.
SmiIndex SmiToNegativeIndex(Register dst, Register src, int shift);
+ bool IsUnsafeSmi(Smi* value);
+ void LoadUnsafeSmi(Register dst, Smi* source);
+
+ // Basic Smi operations.
+ void Move(Register dst, Smi* source);
+ void Move(const Operand& dst, Smi* source);
+ void Push(Smi* smi);
+ void Test(const Operand& dst, Smi* source);
+
// ---------------------------------------------------------------------------
// Macro instructions
- // Expression support
+ // Load a register with a long value as efficiently as possible.
void Set(Register dst, int64_t x);
void Set(const Operand& dst, int64_t x);
// Handle support
- bool IsUnsafeSmi(Smi* value);
bool IsUnsafeSmi(Handle<Object> value) {
return IsUnsafeSmi(Smi::cast(*value));
}
- void LoadUnsafeSmi(Register dst, Smi* source);
void LoadUnsafeSmi(Register dst, Handle<Object> source) {
LoadUnsafeSmi(dst, Smi::cast(*source));
}
@@ -390,7 +404,6 @@ class MacroAssembler: public Assembler {
void Cmp(Register dst, Handle<Object> source);
void Cmp(const Operand& dst, Handle<Object> source);
void Push(Handle<Object> source);
- void Push(Smi* smi);
// Control Flow
void Jump(Address destination, RelocInfo::Mode rmode);
diff --git a/deps/v8/src/x64/stub-cache-x64.cc b/deps/v8/src/x64/stub-cache-x64.cc
index 099423007..58a3e0f6a 100644
--- a/deps/v8/src/x64/stub-cache-x64.cc
+++ b/deps/v8/src/x64/stub-cache-x64.cc
@@ -47,19 +47,24 @@ static void ProbeTable(MacroAssembler* masm,
StubCache::Table table,
Register name,
Register offset) {
- // The offset register must hold a *positive* smi.
+ ASSERT_EQ(8, kPointerSize);
+ ASSERT_EQ(16, sizeof(StubCache::Entry));
+ // The offset register holds the entry offset times four (due to masking
+ // and shifting optimizations).
ExternalReference key_offset(SCTableReference::keyReference(table));
Label miss;
__ movq(kScratchRegister, key_offset);
- SmiIndex index = masm->SmiToIndex(offset, offset, kPointerSizeLog2);
// Check that the key in the entry matches the name.
- __ cmpl(name, Operand(kScratchRegister, index.reg, index.scale, 0));
+ // Multiply entry offset by 16 to get the entry address. Since the
+ // offset register already holds the entry offset times four, multiply
+ // by a further four.
+ __ cmpl(name, Operand(kScratchRegister, offset, times_4, 0));
__ j(not_equal, &miss);
// Get the code entry from the cache.
// Use key_offset + kPointerSize, rather than loading value_offset.
__ movq(kScratchRegister,
- Operand(kScratchRegister, index.reg, index.scale, kPointerSize));
+ Operand(kScratchRegister, offset, times_4, kPointerSize));
// Check that the flags match what we're looking for.
__ movl(offset, FieldOperand(kScratchRegister, Code::kFlagsOffset));
__ and_(offset, Immediate(~Code::kFlagsNotUsedInLookup));
diff --git a/deps/v8/src/x64/virtual-frame-x64.cc b/deps/v8/src/x64/virtual-frame-x64.cc
index 655f4c63b..781efd14b 100644
--- a/deps/v8/src/x64/virtual-frame-x64.cc
+++ b/deps/v8/src/x64/virtual-frame-x64.cc
@@ -63,14 +63,16 @@ void VirtualFrame::Enter() {
Comment cmnt(masm(), "[ Enter JS frame");
#ifdef DEBUG
- // Verify that rdi contains a JS function. The following code
- // relies on rax being available for use.
- Condition not_smi = masm()->CheckNotSmi(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).");
+ 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);
@@ -197,6 +199,14 @@ void VirtualFrame::EmitPush(Immediate immediate) {
}
+void VirtualFrame::EmitPush(Smi* smi_value) {
+ ASSERT(stack_pointer_ == element_count() - 1);
+ elements_.Add(FrameElement::MemoryElement());
+ stack_pointer_++;
+ __ Push(smi_value);
+}
+
+
void VirtualFrame::EmitPush(Handle<Object> value) {
ASSERT(stack_pointer_ == element_count() - 1);
elements_.Add(FrameElement::MemoryElement());
@@ -841,7 +851,7 @@ void VirtualFrame::SyncElementByPushing(int index) {
switch (element.type()) {
case FrameElement::INVALID:
- __ push(Immediate(Smi::FromInt(0)));
+ __ Push(Smi::FromInt(0));
break;
case FrameElement::MEMORY:
@@ -883,15 +893,16 @@ void VirtualFrame::SyncRange(int begin, int end) {
// on the stack.
int start = Min(begin, stack_pointer_ + 1);
- // If positive we have to adjust the stack pointer.
- int delta = end - stack_pointer_;
- if (delta > 0) {
- stack_pointer_ = end;
- __ subq(rsp, Immediate(delta * kPointerSize));
- }
-
+ // 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()) SyncElementBelowStackPointer(i);
+ if (!elements_[i].is_synced()) {
+ if (i <= stack_pointer_) {
+ SyncElementBelowStackPointer(i);
+ } else {
+ SyncElementByPushing(i);
+ }
+ }
}
}
@@ -1004,7 +1015,7 @@ Result VirtualFrame::CallConstructor(int arg_count) {
function.ToRegister(rdi);
// Constructors are called with the number of arguments in register
- // eax for now. Another option would be to have separate construct
+ // 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());
diff --git a/deps/v8/src/x64/virtual-frame-x64.h b/deps/v8/src/x64/virtual-frame-x64.h
index 006148d7d..e49230507 100644
--- a/deps/v8/src/x64/virtual-frame-x64.h
+++ b/deps/v8/src/x64/virtual-frame-x64.h
@@ -377,6 +377,7 @@ class VirtualFrame : public ZoneObject {
void EmitPush(const Operand& operand);
void EmitPush(Heap::RootListIndex index);
void EmitPush(Immediate immediate);
+ void EmitPush(Smi* value);
// Uses kScratchRegister, emits appropriate relocation info.
void EmitPush(Handle<Object> value);
diff --git a/deps/v8/test/cctest/SConscript b/deps/v8/test/cctest/SConscript
index 91034034a..f041041c1 100644
--- a/deps/v8/test/cctest/SConscript
+++ b/deps/v8/test/cctest/SConscript
@@ -67,7 +67,9 @@ SOURCES = {
'test-disasm-ia32.cc',
'test-log-stack-tracer.cc'
],
- 'arch:x64': ['test-assembler-x64.cc', 'test-log-stack-tracer.cc'],
+ 'arch:x64': ['test-assembler-x64.cc',
+ 'test-macro-assembler-x64.cc',
+ 'test-log-stack-tracer.cc'],
'os:linux': ['test-platform-linux.cc'],
'os:macos': ['test-platform-macos.cc'],
'os:nullos': ['test-platform-nullos.cc'],
diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc
index 2282c2d9b..c63ba3165 100644
--- a/deps/v8/test/cctest/test-api.cc
+++ b/deps/v8/test/cctest/test-api.cc
@@ -25,8 +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.
-#include <stdlib.h>
-
#include "v8.h"
#include "api.h"
@@ -574,6 +572,44 @@ THREADED_TEST(UsingExternalAsciiString) {
}
+THREADED_TEST(StringConcat) {
+ {
+ v8::HandleScope scope;
+ LocalContext env;
+ const char* one_byte_string_1 = "function a_times_t";
+ const char* two_byte_string_1 = "wo_plus_b(a, b) {return ";
+ const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + ";
+ const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + ";
+ const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
+ const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
+ const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);";
+ Local<String> left = v8_str(one_byte_string_1);
+ Local<String> right = String::New(AsciiToTwoByteString(two_byte_string_1));
+ Local<String> source = String::Concat(left, right);
+ right = String::NewExternal(
+ new TestAsciiResource(i::StrDup(one_byte_extern_1)));
+ source = String::Concat(source, right);
+ right = String::NewExternal(
+ new TestResource(AsciiToTwoByteString(two_byte_extern_1)));
+ source = String::Concat(source, right);
+ right = v8_str(one_byte_string_2);
+ source = String::Concat(source, right);
+ right = String::New(AsciiToTwoByteString(two_byte_string_2));
+ source = String::Concat(source, right);
+ right = String::NewExternal(
+ new TestResource(AsciiToTwoByteString(two_byte_extern_2)));
+ source = String::Concat(source, right);
+ Local<Script> script = Script::Compile(source);
+ Local<Value> value = script->Run();
+ CHECK(value->IsNumber());
+ CHECK_EQ(68, value->Int32Value());
+ }
+ v8::internal::CompilationCache::Clear();
+ i::Heap::CollectAllGarbage(false);
+ i::Heap::CollectAllGarbage(false);
+}
+
+
THREADED_TEST(GlobalProperties) {
v8::HandleScope scope;
LocalContext env;
@@ -702,6 +738,88 @@ THREADED_TEST(PropertyHandler) {
}
+THREADED_TEST(TinyInteger) {
+ v8::HandleScope scope;
+ LocalContext env;
+ int32_t value = 239;
+ Local<v8::Integer> value_obj = v8::Integer::New(value);
+ CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
+}
+
+
+THREADED_TEST(BigSmiInteger) {
+ v8::HandleScope scope;
+ LocalContext env;
+ int32_t value = i::Smi::kMaxValue;
+ // We cannot add one to a Smi::kMaxValue without wrapping.
+ if (i::kSmiValueSize < 32) {
+ CHECK(i::Smi::IsValid(value));
+ CHECK(!i::Smi::IsValid(value + 1));
+ Local<v8::Integer> value_obj = v8::Integer::New(value);
+ CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
+ }
+}
+
+
+THREADED_TEST(BigInteger) {
+ v8::HandleScope scope;
+ LocalContext env;
+ // We cannot add one to a Smi::kMaxValue without wrapping.
+ if (i::kSmiValueSize < 32) {
+ // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1.
+ // The code will not be run in that case, due to the "if" guard.
+ int32_t value =
+ static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1);
+ CHECK(value > i::Smi::kMaxValue);
+ CHECK(!i::Smi::IsValid(value));
+ Local<v8::Integer> value_obj = v8::Integer::New(value);
+ CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
+ }
+}
+
+
+THREADED_TEST(TinyUnsignedInteger) {
+ v8::HandleScope scope;
+ LocalContext env;
+ uint32_t value = 239;
+ Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
+ CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
+}
+
+
+THREADED_TEST(BigUnsignedSmiInteger) {
+ v8::HandleScope scope;
+ LocalContext env;
+ uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue);
+ CHECK(i::Smi::IsValid(value));
+ CHECK(!i::Smi::IsValid(value + 1));
+ Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
+ CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
+}
+
+
+THREADED_TEST(BigUnsignedInteger) {
+ v8::HandleScope scope;
+ LocalContext env;
+ uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1;
+ CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue));
+ CHECK(!i::Smi::IsValid(value));
+ Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
+ CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
+}
+
+
+THREADED_TEST(OutOfSignedRangeUnsignedInteger) {
+ v8::HandleScope scope;
+ LocalContext env;
+ uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1;
+ uint32_t value = INT32_MAX_AS_UINT + 1;
+ CHECK(value > INT32_MAX_AS_UINT); // No overflow.
+ Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
+ CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
+}
+
+
THREADED_TEST(Number) {
v8::HandleScope scope;
LocalContext env;
@@ -1346,6 +1464,44 @@ THREADED_TEST(InternalFieldsNativePointers) {
}
+THREADED_TEST(InternalFieldsNativePointersAndExternal) {
+ v8::HandleScope scope;
+ LocalContext env;
+
+ Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
+ Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
+ instance_templ->SetInternalFieldCount(1);
+ Local<v8::Object> obj = templ->GetFunction()->NewInstance();
+ CHECK_EQ(1, obj->InternalFieldCount());
+ CHECK(obj->GetPointerFromInternalField(0) == NULL);
+
+ char* data = new char[100];
+
+ void* aligned = data;
+ CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1);
+ void* unaligned = data + 1;
+ CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1);
+
+ obj->SetPointerInInternalField(0, aligned);
+ i::Heap::CollectAllGarbage(false);
+ CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0)));
+
+ obj->SetPointerInInternalField(0, unaligned);
+ i::Heap::CollectAllGarbage(false);
+ CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0)));
+
+ obj->SetInternalField(0, v8::External::Wrap(aligned));
+ i::Heap::CollectAllGarbage(false);
+ CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
+
+ obj->SetInternalField(0, v8::External::Wrap(unaligned));
+ i::Heap::CollectAllGarbage(false);
+ CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
+
+ delete[] data;
+}
+
+
THREADED_TEST(IdentityHash) {
v8::HandleScope scope;
LocalContext env;
@@ -1810,7 +1966,7 @@ TEST(HugeConsStringOutOfMemory) {
// Build huge string. This should fail with out of memory exception.
Local<Value> result = CompileRun(
"var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();"
- "for (var i = 0; i < 21; i++) { str = str + str; }");
+ "for (var i = 0; i < 22; i++) { str = str + str; }");
// Check for out of memory state.
CHECK(result.IsEmpty());
diff --git a/deps/v8/test/cctest/test-assembler-x64.cc b/deps/v8/test/cctest/test-assembler-x64.cc
index cd750c5df..81aa973db 100644
--- a/deps/v8/test/cctest/test-assembler-x64.cc
+++ b/deps/v8/test/cctest/test-assembler-x64.cc
@@ -44,6 +44,7 @@ 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;
@@ -53,20 +54,28 @@ using v8::internal::less_equal;
using v8::internal::not_equal;
using v8::internal::greater;
-
// Test the x64 assembler by compiling some simple functions into
// a buffer and executing them. These tests do not initialize the
// V8 library, create a context, or use any V8 objects.
-// The AMD64 calling convention is used, with the first five arguments
-// in RSI, RDI, RDX, RCX, R8, and R9, and floating point arguments in
+// The AMD64 calling convention is used, with the first six arguments
+// in RDI, RSI, RDX, RCX, R8, and R9, and floating point arguments in
// the XMM registers. The return value is in RAX.
// This calling convention is used on Linux, with GCC, and on Mac OS,
-// with GCC. A different convention is used on 64-bit windows.
+// with GCC. A different convention is used on 64-bit windows,
+// where the first four integer arguments are passed in RCX, RDX, R8 and R9.
typedef int (*F0)();
typedef int (*F1)(int64_t x);
typedef int (*F2)(int64_t x, int64_t y);
+#ifdef _WIN64
+static const v8::internal::Register arg1 = rcx;
+static const v8::internal::Register arg2 = rdx;
+#else
+static const v8::internal::Register arg1 = rdi;
+static const v8::internal::Register arg2 = rsi;
+#endif
+
#define __ assm.
@@ -80,7 +89,7 @@ TEST(AssemblerX64ReturnOperation) {
Assembler assm(buffer, actual_size);
// Assemble a simple function that copies argument 2 and returns it.
- __ movq(rax, rsi);
+ __ movq(rax, arg2);
__ nop();
__ ret(0);
@@ -105,9 +114,9 @@ TEST(AssemblerX64StackOperations) {
// incorrect stack frames when debugging this function (which has them).
__ push(rbp);
__ movq(rbp, rsp);
- __ push(rsi); // Value at (rbp - 8)
- __ push(rsi); // Value at (rbp - 16)
- __ push(rdi); // Value at (rbp - 24)
+ __ push(arg2); // Value at (rbp - 8)
+ __ push(arg2); // Value at (rbp - 16)
+ __ push(arg1); // Value at (rbp - 24)
__ pop(rax);
__ pop(rax);
__ pop(rax);
@@ -132,8 +141,8 @@ TEST(AssemblerX64ArithmeticOperations) {
Assembler assm(buffer, actual_size);
// Assemble a simple function that adds arguments returning the sum.
- __ movq(rax, rsi);
- __ addq(rax, rdi);
+ __ movq(rax, arg2);
+ __ addq(rax, arg1);
__ ret(0);
CodeDesc desc;
@@ -154,8 +163,8 @@ TEST(AssemblerX64ImulOperation) {
// Assemble a simple function that multiplies arguments returning the high
// word.
- __ movq(rax, rsi);
- __ imul(rdi);
+ __ movq(rax, arg2);
+ __ imul(arg1);
__ movq(rax, rdx);
__ ret(0);
@@ -182,14 +191,16 @@ TEST(AssemblerX64MemoryOperands) {
// Assemble a simple function that copies argument 2 and returns it.
__ push(rbp);
__ movq(rbp, rsp);
- __ push(rsi); // Value at (rbp - 8)
- __ push(rsi); // Value at (rbp - 16)
- __ push(rdi); // Value at (rbp - 24)
+
+ __ push(arg2); // Value at (rbp - 8)
+ __ push(arg2); // Value at (rbp - 16)
+ __ push(arg1); // Value at (rbp - 24)
+
const int kStackElementSize = 8;
__ movq(rax, Operand(rbp, -3 * kStackElementSize));
- __ pop(rsi);
- __ pop(rsi);
- __ pop(rsi);
+ __ pop(arg2);
+ __ pop(arg2);
+ __ pop(arg2);
__ pop(rbp);
__ nop();
__ ret(0);
@@ -210,13 +221,14 @@ TEST(AssemblerX64ControlFlow) {
CHECK(buffer);
Assembler assm(buffer, actual_size);
- // Assemble a simple function that copies argument 2 and returns it.
+ // Assemble a simple function that copies argument 1 and returns it.
__ push(rbp);
+
__ movq(rbp, rsp);
- __ movq(rax, rdi);
+ __ movq(rax, arg1);
Label target;
__ jmp(&target);
- __ movq(rax, rsi);
+ __ movq(rax, arg2);
__ bind(&target);
__ pop(rbp);
__ ret(0);
diff --git a/deps/v8/test/cctest/test-debug.cc b/deps/v8/test/cctest/test-debug.cc
index 1da363c2a..4ffcee3db 100644
--- a/deps/v8/test/cctest/test-debug.cc
+++ b/deps/v8/test/cctest/test-debug.cc
@@ -3539,6 +3539,52 @@ bool IsBreakEventMessage(char *message) {
}
+// We match parts of the message to decide if it is a exception message.
+bool IsExceptionEventMessage(char *message) {
+ const char* type_event = "\"type\":\"event\"";
+ const char* event_exception = "\"event\":\"exception\"";
+ // Does the message contain both type:event and event:exception?
+ return strstr(message, type_event) != NULL &&
+ strstr(message, event_exception) != NULL;
+}
+
+
+// We match the message wether it is an evaluate response message.
+bool IsEvaluateResponseMessage(char* message) {
+ const char* type_response = "\"type\":\"response\"";
+ const char* command_evaluate = "\"command\":\"evaluate\"";
+ // Does the message contain both type:response and command:evaluate?
+ return strstr(message, type_response) != NULL &&
+ strstr(message, command_evaluate) != NULL;
+}
+
+
+// We match parts of the message to get evaluate result int value.
+int GetEvaluateIntResult(char *message) {
+ const char* value = "\"value\":";
+ char* pos = strstr(message, value);
+ if (pos == NULL) {
+ return -1;
+ }
+ int res = -1;
+ res = atoi(pos + strlen(value));
+ return res;
+}
+
+
+// We match parts of the message to get hit breakpoint id.
+int GetBreakpointIdFromBreakEventMessage(char *message) {
+ const char* breakpoints = "\"breakpoints\":[";
+ char* pos = strstr(message, breakpoints);
+ if (pos == NULL) {
+ return -1;
+ }
+ int res = -1;
+ res = atoi(pos + strlen(breakpoints));
+ return res;
+}
+
+
/* Test MessageQueues */
/* Tests the message queues that hold debugger commands and
* response messages to the debugger. Fills queues and makes
@@ -3566,8 +3612,6 @@ static void MessageHandler(const uint16_t* message, int length,
// Allow message handler to block on a semaphore, to test queueing of
// messages while blocked.
message_queue_barriers.semaphore_1->Wait();
- printf("%s\n", print_buffer);
- fflush(stdout);
}
void MessageQueueDebuggerThread::Run() {
@@ -3822,8 +3866,6 @@ static void ThreadedMessageHandler(const v8::Debug::Message& message) {
if (IsBreakEventMessage(print_buffer)) {
threaded_debugging_barriers.barrier_2.Wait();
}
- printf("%s\n", print_buffer);
- fflush(stdout);
}
@@ -3911,16 +3953,20 @@ class BreakpointsDebuggerThread : public v8::internal::Thread {
Barriers* breakpoints_barriers;
+int break_event_breakpoint_id;
+int evaluate_int_result;
static void BreakpointsMessageHandler(const v8::Debug::Message& message) {
static char print_buffer[1000];
v8::String::Value json(message.GetJSON());
Utf16ToAscii(*json, json.length(), print_buffer);
- printf("%s\n", print_buffer);
- fflush(stdout);
- // Is break_template a prefix of the message?
if (IsBreakEventMessage(print_buffer)) {
+ break_event_breakpoint_id =
+ GetBreakpointIdFromBreakEventMessage(print_buffer);
+ breakpoints_barriers->semaphore_1->Signal();
+ } else if (IsEvaluateResponseMessage(print_buffer)) {
+ evaluate_int_result = GetEvaluateIntResult(print_buffer);
breakpoints_barriers->semaphore_1->Signal();
}
}
@@ -3930,9 +3976,9 @@ void BreakpointsV8Thread::Run() {
const char* source_1 = "var y_global = 3;\n"
"function cat( new_value ) {\n"
" var x = new_value;\n"
- " y_global = 4;\n"
+ " y_global = y_global + 4;\n"
" x = 3 * x + 1;\n"
- " y_global = 5;\n"
+ " y_global = y_global + 5;\n"
" return x;\n"
"}\n"
"\n"
@@ -3970,59 +4016,76 @@ void BreakpointsDebuggerThread::Run() {
"\"type\":\"request\","
"\"command\":\"setbreakpoint\","
"\"arguments\":{\"type\":\"function\",\"target\":\"dog\",\"line\":3}}";
- const char* command_3 = "{\"seq\":104,"
+ const char* command_3 = "{\"seq\":103,"
"\"type\":\"request\","
"\"command\":\"evaluate\","
"\"arguments\":{\"expression\":\"dog()\",\"disable_break\":false}}";
- const char* command_4 = "{\"seq\":105,"
+ const char* command_4 = "{\"seq\":104,"
"\"type\":\"request\","
"\"command\":\"evaluate\","
- "\"arguments\":{\"expression\":\"x\",\"disable_break\":true}}";
- const char* command_5 = "{\"seq\":106,"
+ "\"arguments\":{\"expression\":\"x + 1\",\"disable_break\":true}}";
+ const char* command_5 = "{\"seq\":105,"
"\"type\":\"request\","
"\"command\":\"continue\"}";
- const char* command_6 = "{\"seq\":107,"
+ const char* command_6 = "{\"seq\":106,"
"\"type\":\"request\","
"\"command\":\"continue\"}";
- const char* command_7 = "{\"seq\":108,"
+ const char* command_7 = "{\"seq\":107,"
"\"type\":\"request\","
"\"command\":\"evaluate\","
"\"arguments\":{\"expression\":\"dog()\",\"disable_break\":true}}";
- const char* command_8 = "{\"seq\":109,"
+ const char* command_8 = "{\"seq\":108,"
"\"type\":\"request\","
"\"command\":\"continue\"}";
// v8 thread initializes, runs source_1
breakpoints_barriers->barrier_1.Wait();
- // 1:Set breakpoint in cat().
+ // 1:Set breakpoint in cat() (will get id 1).
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_1, buffer));
- // 2:Set breakpoint in dog()
+ // 2:Set breakpoint in dog() (will get id 2).
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_2, buffer));
breakpoints_barriers->barrier_2.Wait();
- // v8 thread starts compiling source_2.
+ // V8 thread starts compiling source_2.
// Automatic break happens, to run queued commands
// breakpoints_barriers->semaphore_1->Wait();
// Commands 1 through 3 run, thread continues.
// v8 thread runs source_2 to breakpoint in cat().
// message callback receives break event.
breakpoints_barriers->semaphore_1->Wait();
+ // Must have hit breakpoint #1.
+ CHECK_EQ(1, break_event_breakpoint_id);
// 4:Evaluate dog() (which has a breakpoint).
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_3, buffer));
- // v8 thread hits breakpoint in dog()
+ // V8 thread hits breakpoint in dog().
breakpoints_barriers->semaphore_1->Wait(); // wait for break event
- // 5:Evaluate x
+ // Must have hit breakpoint #2.
+ CHECK_EQ(2, break_event_breakpoint_id);
+ // 5:Evaluate (x + 1).
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_4, buffer));
- // 6:Continue evaluation of dog()
+ // Evaluate (x + 1) finishes.
+ breakpoints_barriers->semaphore_1->Wait();
+ // Must have result 108.
+ CHECK_EQ(108, evaluate_int_result);
+ // 6:Continue evaluation of dog().
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_5, buffer));
- // dog() finishes.
+ // Evaluate dog() finishes.
+ breakpoints_barriers->semaphore_1->Wait();
+ // Must have result 107.
+ CHECK_EQ(107, evaluate_int_result);
// 7:Continue evaluation of source_2, finish cat(17), hit breakpoint
// in cat(19).
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_6, buffer));
- // message callback gets break event
+ // Message callback gets break event.
breakpoints_barriers->semaphore_1->Wait(); // wait for break event
- // 8: Evaluate dog() with breaks disabled
+ // Must have hit breakpoint #1.
+ CHECK_EQ(1, break_event_breakpoint_id);
+ // 8: Evaluate dog() with breaks disabled.
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_7, buffer));
+ // Evaluate dog() finishes.
+ breakpoints_barriers->semaphore_1->Wait();
+ // Must have result 116.
+ CHECK_EQ(116, evaluate_int_result);
// 9: Continue evaluation of source2, reach end.
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_8, buffer));
}
@@ -4325,7 +4388,13 @@ static int message_handler_hit_count = 0;
static void MessageHandlerHitCount(const v8::Debug::Message& message) {
message_handler_hit_count++;
- SendContinueCommand();
+ static char print_buffer[1000];
+ v8::String::Value json(message.GetJSON());
+ Utf16ToAscii(*json, json.length(), print_buffer);
+ if (IsExceptionEventMessage(print_buffer)) {
+ // Send a continue command for exception events.
+ SendContinueCommand();
+ }
}
@@ -4415,8 +4484,6 @@ static void HostDispatchMessageHandler(const v8::Debug::Message& message) {
static char print_buffer[1000];
v8::String::Value json(message.GetJSON());
Utf16ToAscii(*json, json.length(), print_buffer);
- printf("%s\n", print_buffer);
- fflush(stdout);
}
@@ -4776,8 +4843,12 @@ static void ContextCheckMessageHandler(const v8::Debug::Message& message) {
expected_context_data));
message_handler_hit_count++;
+ static char print_buffer[1000];
+ v8::String::Value json(message.GetJSON());
+ Utf16ToAscii(*json, json.length(), print_buffer);
+
// Send a continue command for break events.
- if (message.GetEvent() == v8::Break) {
+ if (IsBreakEventMessage(print_buffer)) {
SendContinueCommand();
}
}
@@ -5016,7 +5087,11 @@ static void DebugEvalContextCheckMessageHandler(
expected_context_data));
message_handler_hit_count++;
- if (message.IsEvent() && message.GetEvent() == v8::Break) {
+ static char print_buffer[1000];
+ v8::String::Value json(message.GetJSON());
+ Utf16ToAscii(*json, json.length(), print_buffer);
+
+ if (IsBreakEventMessage(print_buffer)) {
break_count++;
if (!sent_eval) {
sent_eval = true;
@@ -5038,7 +5113,8 @@ static void DebugEvalContextCheckMessageHandler(
SendContinueCommand();
continue_command_send_count++;
}
- } else if (message.IsResponse() && continue_command_send_count < 2) {
+ } else if (IsEvaluateResponseMessage(print_buffer) &&
+ continue_command_send_count < 2) {
// Response to the evaluation request. We're still on the breakpoint so
// send continue.
SendContinueCommand();
diff --git a/deps/v8/test/cctest/test-disasm-ia32.cc b/deps/v8/test/cctest/test-disasm-ia32.cc
index af9fb97e5..74db23463 100644
--- a/deps/v8/test/cctest/test-disasm-ia32.cc
+++ b/deps/v8/test/cctest/test-disasm-ia32.cc
@@ -363,7 +363,31 @@ TEST(DisasmIa320) {
__ divsd(xmm1, xmm0);
__ movdbl(xmm1, Operand(ebx, ecx, times_4, 10000));
__ movdbl(Operand(ebx, ecx, times_4, 10000), xmm1);
+ __ comisd(xmm0, xmm1);
}
+
+ // cmov.
+ {
+ CHECK(CpuFeatures::IsSupported(CpuFeatures::CMOV));
+ CpuFeatures::Scope use_cmov(CpuFeatures::CMOV);
+ __ cmov(overflow, eax, Operand(eax, 0));
+ __ cmov(no_overflow, eax, Operand(eax, 1));
+ __ cmov(below, eax, Operand(eax, 2));
+ __ cmov(above_equal, eax, Operand(eax, 3));
+ __ cmov(equal, eax, Operand(ebx, 0));
+ __ cmov(not_equal, eax, Operand(ebx, 1));
+ __ cmov(below_equal, eax, Operand(ebx, 2));
+ __ cmov(above, eax, Operand(ebx, 3));
+ __ cmov(sign, eax, Operand(ecx, 0));
+ __ cmov(not_sign, eax, Operand(ecx, 1));
+ __ cmov(parity_even, eax, Operand(ecx, 2));
+ __ cmov(parity_odd, eax, Operand(ecx, 3));
+ __ cmov(less, eax, Operand(edx, 0));
+ __ cmov(greater_equal, eax, Operand(edx, 1));
+ __ cmov(less_equal, eax, Operand(edx, 2));
+ __ cmov(greater, eax, Operand(edx, 3));
+ }
+
__ ret(0);
CodeDesc desc;
diff --git a/deps/v8/test/cctest/test-heap.cc b/deps/v8/test/cctest/test-heap.cc
index eb32b6539..9911ce42b 100644
--- a/deps/v8/test/cctest/test-heap.cc
+++ b/deps/v8/test/cctest/test-heap.cc
@@ -132,15 +132,19 @@ TEST(HeapObjects) {
CHECK(value->IsNumber());
CHECK_EQ(Smi::kMaxValue, Smi::cast(value)->value());
+#ifndef V8_TARGET_ARCH_X64
+ // TODO(lrn): We need a NumberFromIntptr function in order to test this.
value = Heap::NumberFromInt32(Smi::kMinValue - 1);
CHECK(value->IsHeapNumber());
CHECK(value->IsNumber());
CHECK_EQ(static_cast<double>(Smi::kMinValue - 1), value->Number());
+#endif
- value = Heap::NumberFromInt32(Smi::kMaxValue + 1);
+ value = Heap::NumberFromUint32(static_cast<uint32_t>(Smi::kMaxValue) + 1);
CHECK(value->IsHeapNumber());
CHECK(value->IsNumber());
- CHECK_EQ(static_cast<double>(Smi::kMaxValue + 1), value->Number());
+ CHECK_EQ(static_cast<double>(static_cast<uint32_t>(Smi::kMaxValue) + 1),
+ value->Number());
// nan oddball checks
CHECK(Heap::nan_value()->IsNumber());
@@ -640,8 +644,9 @@ TEST(JSArray) {
CHECK_EQ(Smi::FromInt(1), array->length());
CHECK_EQ(array->GetElement(0), name);
- // Set array length with larger than smi value.
- Object* length = Heap::NumberFromInt32(Smi::kMaxValue + 1);
+// Set array length with larger than smi value.
+ Object* length =
+ Heap::NumberFromUint32(static_cast<uint32_t>(Smi::kMaxValue) + 1);
array->SetElementsLength(length);
uint32_t int_length = 0;
diff --git a/deps/v8/test/cctest/test-macro-assembler-x64.cc b/deps/v8/test/cctest/test-macro-assembler-x64.cc
new file mode 100755
index 000000000..9c1197ffd
--- /dev/null
+++ b/deps/v8/test/cctest/test-macro-assembler-x64.cc
@@ -0,0 +1,2096 @@
+// 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 <stdlib.h>
+
+#include "v8.h"
+
+#include "macro-assembler.h"
+#include "factory.h"
+#include "platform.h"
+#include "serialize.h"
+#include "cctest.h"
+
+using v8::internal::byte;
+using v8::internal::OS;
+using v8::internal::Assembler;
+using v8::internal::Condition;
+using v8::internal::MacroAssembler;
+using v8::internal::HandleScope;
+using v8::internal::Operand;
+using v8::internal::Immediate;
+using v8::internal::SmiIndex;
+using v8::internal::Label;
+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::r11;
+using v8::internal::r12;
+using v8::internal::r13;
+using v8::internal::r14;
+using v8::internal::r15;
+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;
+
+// Test the x64 assembler by compiling some simple functions into
+// a buffer and executing them. These tests do not initialize the
+// V8 library, create a context, or use any V8 objects.
+// The AMD64 calling convention is used, with the first five arguments
+// in RSI, RDI, RDX, RCX, R8, and R9, and floating point arguments in
+// the XMM registers. The return value is in RAX.
+// This calling convention is used on Linux, with GCC, and on Mac OS,
+// with GCC. A different convention is used on 64-bit windows.
+
+typedef int (*F0)();
+
+#define __ masm->
+
+TEST(Smi) {
+ // Check that C++ Smi operations work as expected.
+ intptr_t test_numbers[] = {
+ 0, 1, -1, 127, 128, -128, -129, 255, 256, -256, -257,
+ Smi::kMaxValue, static_cast<intptr_t>(Smi::kMaxValue) + 1,
+ Smi::kMinValue, static_cast<intptr_t>(Smi::kMinValue) - 1
+ };
+ int test_number_count = 15;
+ for (int i = 0; i < test_number_count; i++) {
+ intptr_t number = test_numbers[i];
+ bool is_valid = Smi::IsValid(number);
+ bool is_in_range = number >= Smi::kMinValue && number <= Smi::kMaxValue;
+ CHECK_EQ(is_in_range, is_valid);
+ if (is_valid) {
+ Smi* smi_from_intptr = Smi::FromIntptr(number);
+ if (static_cast<int>(number) == number) { // Is a 32-bit int.
+ Smi* smi_from_int = Smi::FromInt(static_cast<int32_t>(number));
+ CHECK_EQ(smi_from_int, smi_from_intptr);
+ }
+ int smi_value = smi_from_intptr->value();
+ CHECK_EQ(number, static_cast<intptr_t>(smi_value));
+ }
+ }
+}
+
+
+static void TestMoveSmi(MacroAssembler* masm, Label* exit, int id, Smi* value) {
+ __ movl(rax, Immediate(id));
+ __ Move(rcx, Smi::FromInt(0));
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(0)));
+ __ cmpq(rcx, rdx);
+ __ j(not_equal, exit);
+}
+
+
+// Test that we can move a Smi value literally into a register.
+TEST(SmiMove) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+ MacroAssembler* masm = &assembler; // Create a pointer for the __ macro.
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestMoveSmi(masm, &exit, 1, Smi::FromInt(0));
+ TestMoveSmi(masm, &exit, 2, Smi::FromInt(127));
+ TestMoveSmi(masm, &exit, 3, Smi::FromInt(128));
+ TestMoveSmi(masm, &exit, 4, Smi::FromInt(255));
+ TestMoveSmi(masm, &exit, 5, Smi::FromInt(256));
+ TestMoveSmi(masm, &exit, 6, Smi::FromInt(Smi::kMaxValue));
+ TestMoveSmi(masm, &exit, 7, Smi::FromInt(-1));
+ TestMoveSmi(masm, &exit, 8, Smi::FromInt(-128));
+ TestMoveSmi(masm, &exit, 9, Smi::FromInt(-129));
+ TestMoveSmi(masm, &exit, 10, Smi::FromInt(-256));
+ TestMoveSmi(masm, &exit, 11, Smi::FromInt(-257));
+ TestMoveSmi(masm, &exit, 12, Smi::FromInt(Smi::kMinValue));
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiCompare(MacroAssembler* masm, Label* exit, int id, int x, int y) {
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r8, rcx);
+ __ Move(rdx, Smi::FromInt(y));
+ __ movq(r9, rdx);
+ __ SmiCompare(rcx, rdx);
+ if (x < y) {
+ __ movl(rax, Immediate(id + 1));
+ __ j(greater_equal, exit);
+ } else if (x > y) {
+ __ movl(rax, Immediate(id + 2));
+ __ j(less_equal, exit);
+ } else {
+ ASSERT_EQ(x, y);
+ __ movl(rax, Immediate(id + 3));
+ __ j(not_equal, exit);
+ }
+ __ movl(rax, Immediate(id + 4));
+ __ cmpq(rcx, r8);
+ __ j(not_equal, exit);
+ __ incq(rax);
+ __ cmpq(rdx, r9);
+ __ j(not_equal, exit);
+
+ if (x != y) {
+ __ SmiCompare(rdx, rcx);
+ if (y < x) {
+ __ movl(rax, Immediate(id + 9));
+ __ j(greater_equal, exit);
+ } else {
+ ASSERT(y > x);
+ __ movl(rax, Immediate(id + 10));
+ __ j(less_equal, exit);
+ }
+ } else {
+ __ SmiCompare(rcx, rcx);
+ __ movl(rax, Immediate(id + 11));
+ __ j(not_equal, exit);
+ __ incq(rax);
+ __ cmpq(rcx, r8);
+ __ j(not_equal, exit);
+ }
+}
+
+
+// Test that we can compare smis for equality (and more).
+TEST(SmiCompare) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiCompare(masm, &exit, 0x10, 0, 0);
+ TestSmiCompare(masm, &exit, 0x20, 0, 1);
+ TestSmiCompare(masm, &exit, 0x30, 1, 0);
+ TestSmiCompare(masm, &exit, 0x40, 1, 1);
+ TestSmiCompare(masm, &exit, 0x50, 0, -1);
+ TestSmiCompare(masm, &exit, 0x60, -1, 0);
+ TestSmiCompare(masm, &exit, 0x70, -1, -1);
+ TestSmiCompare(masm, &exit, 0x80, 0, Smi::kMinValue);
+ TestSmiCompare(masm, &exit, 0x90, Smi::kMinValue, 0);
+ TestSmiCompare(masm, &exit, 0xA0, 0, Smi::kMaxValue);
+ TestSmiCompare(masm, &exit, 0xB0, Smi::kMaxValue, 0);
+ TestSmiCompare(masm, &exit, 0xC0, -1, Smi::kMinValue);
+ TestSmiCompare(masm, &exit, 0xD0, Smi::kMinValue, -1);
+ TestSmiCompare(masm, &exit, 0xE0, -1, Smi::kMaxValue);
+ TestSmiCompare(masm, &exit, 0xF0, Smi::kMaxValue, -1);
+ TestSmiCompare(masm, &exit, 0x100, Smi::kMinValue, Smi::kMinValue);
+ TestSmiCompare(masm, &exit, 0x110, Smi::kMinValue, Smi::kMaxValue);
+ TestSmiCompare(masm, &exit, 0x120, Smi::kMaxValue, Smi::kMinValue);
+ TestSmiCompare(masm, &exit, 0x130, Smi::kMaxValue, Smi::kMaxValue);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+
+TEST(Integer32ToSmi) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ __ movq(rax, Immediate(1)); // Test number.
+ __ movl(rcx, Immediate(0));
+ __ Integer32ToSmi(rcx, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(0)));
+ __ SmiCompare(rcx, rdx);
+ __ j(not_equal, &exit);
+
+ __ movq(rax, Immediate(2)); // Test number.
+ __ movl(rcx, Immediate(1024));
+ __ Integer32ToSmi(rcx, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(1024)));
+ __ SmiCompare(rcx, rdx);
+ __ j(not_equal, &exit);
+
+ __ movq(rax, Immediate(3)); // Test number.
+ __ movl(rcx, Immediate(-1));
+ __ Integer32ToSmi(rcx, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(-1)));
+ __ SmiCompare(rcx, rdx);
+ __ j(not_equal, &exit);
+
+ __ movq(rax, Immediate(4)); // Test number.
+ __ movl(rcx, Immediate(Smi::kMaxValue));
+ __ Integer32ToSmi(rcx, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(Smi::kMaxValue)));
+ __ SmiCompare(rcx, rdx);
+ __ j(not_equal, &exit);
+
+ __ movq(rax, Immediate(5)); // Test number.
+ __ movl(rcx, Immediate(Smi::kMinValue));
+ __ Integer32ToSmi(rcx, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(Smi::kMinValue)));
+ __ SmiCompare(rcx, rdx);
+ __ j(not_equal, &exit);
+
+ // Different target register.
+
+ __ movq(rax, Immediate(6)); // Test number.
+ __ movl(rcx, Immediate(0));
+ __ Integer32ToSmi(r8, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(0)));
+ __ SmiCompare(r8, rdx);
+ __ j(not_equal, &exit);
+
+ __ movq(rax, Immediate(7)); // Test number.
+ __ movl(rcx, Immediate(1024));
+ __ Integer32ToSmi(r8, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(1024)));
+ __ SmiCompare(r8, rdx);
+ __ j(not_equal, &exit);
+
+ __ movq(rax, Immediate(8)); // Test number.
+ __ movl(rcx, Immediate(-1));
+ __ Integer32ToSmi(r8, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(-1)));
+ __ SmiCompare(r8, rdx);
+ __ j(not_equal, &exit);
+
+ __ movq(rax, Immediate(9)); // Test number.
+ __ movl(rcx, Immediate(Smi::kMaxValue));
+ __ Integer32ToSmi(r8, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(Smi::kMaxValue)));
+ __ SmiCompare(r8, rdx);
+ __ j(not_equal, &exit);
+
+ __ movq(rax, Immediate(10)); // Test number.
+ __ movl(rcx, Immediate(Smi::kMinValue));
+ __ Integer32ToSmi(r8, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(Smi::kMinValue)));
+ __ SmiCompare(r8, rdx);
+ __ j(not_equal, &exit);
+
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestI64PlusConstantToSmi(MacroAssembler* masm,
+ Label* exit,
+ int id,
+ int64_t x,
+ int y) {
+ int64_t result = x + y;
+ ASSERT(Smi::IsValid(result));
+ __ movl(rax, Immediate(id));
+ __ Move(r8, Smi::FromInt(result));
+ __ movq(rcx, x, RelocInfo::NONE);
+ __ movq(r11, rcx);
+ __ Integer64PlusConstantToSmi(rdx, rcx, y);
+ __ SmiCompare(rdx, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Integer64PlusConstantToSmi(rcx, rcx, y);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+}
+
+
+TEST(Integer64PlusConstantToSmi) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ int64_t twice_max = static_cast<int64_t>(Smi::kMaxValue) * 2;
+
+ TestI64PlusConstantToSmi(masm, &exit, 0x10, 0, 0);
+ TestI64PlusConstantToSmi(masm, &exit, 0x20, 0, 1);
+ TestI64PlusConstantToSmi(masm, &exit, 0x30, 1, 0);
+ TestI64PlusConstantToSmi(masm, &exit, 0x40, Smi::kMaxValue - 5, 5);
+ TestI64PlusConstantToSmi(masm, &exit, 0x50, Smi::kMinValue + 5, 5);
+ TestI64PlusConstantToSmi(masm, &exit, 0x60, twice_max, -Smi::kMaxValue);
+ TestI64PlusConstantToSmi(masm, &exit, 0x70, -twice_max, Smi::kMaxValue);
+ TestI64PlusConstantToSmi(masm, &exit, 0x80, 0, Smi::kMinValue);
+ TestI64PlusConstantToSmi(masm, &exit, 0x90, 0, Smi::kMaxValue);
+ TestI64PlusConstantToSmi(masm, &exit, 0xA0, Smi::kMinValue, 0);
+ TestI64PlusConstantToSmi(masm, &exit, 0xB0, Smi::kMaxValue, 0);
+ TestI64PlusConstantToSmi(masm, &exit, 0xC0, twice_max, Smi::kMinValue);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+TEST(SmiCheck) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+ Condition cond;
+
+ __ movl(rax, Immediate(1)); // Test number.
+
+ // CheckSmi
+
+ __ movl(rcx, Immediate(0));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckSmi(rcx);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ cond = masm->CheckSmi(rcx);
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ movl(rcx, Immediate(-1));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckSmi(rcx);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ cond = masm->CheckSmi(rcx);
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ movl(rcx, Immediate(Smi::kMaxValue));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckSmi(rcx);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ cond = masm->CheckSmi(rcx);
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ movl(rcx, Immediate(Smi::kMinValue));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckSmi(rcx);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ cond = masm->CheckSmi(rcx);
+ __ j(cond, &exit);
+
+ // CheckPositiveSmi
+
+ __ incq(rax);
+ __ movl(rcx, Immediate(0));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckPositiveSmi(rcx); // Zero counts as positive.
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ cond = masm->CheckPositiveSmi(rcx); // "zero" non-smi.
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(-1));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckPositiveSmi(rcx); // Negative smis are not positive.
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(Smi::kMinValue));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckPositiveSmi(rcx); // Most negative smi is not positive.
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ cond = masm->CheckPositiveSmi(rcx); // "Negative" non-smi.
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(Smi::kMaxValue));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckPositiveSmi(rcx); // Most positive smi is positive.
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ cond = masm->CheckPositiveSmi(rcx); // "Positive" non-smi.
+ __ j(cond, &exit);
+
+ // CheckIsMinSmi
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(Smi::kMaxValue));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckIsMinSmi(rcx);
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(0));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckIsMinSmi(rcx);
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(Smi::kMinValue));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckIsMinSmi(rcx);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(Smi::kMinValue + 1));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckIsMinSmi(rcx);
+ __ j(cond, &exit);
+
+ // CheckBothSmi
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(Smi::kMaxValue));
+ __ Integer32ToSmi(rcx, rcx);
+ __ movq(rdx, Immediate(Smi::kMinValue));
+ __ Integer32ToSmi(rdx, rdx);
+ cond = masm->CheckBothSmi(rcx, rdx);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ cond = masm->CheckBothSmi(rcx, rdx);
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ xor_(rdx, Immediate(kSmiTagMask));
+ cond = masm->CheckBothSmi(rcx, rdx);
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ cond = masm->CheckBothSmi(rcx, rdx);
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ cond = masm->CheckBothSmi(rcx, rcx);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ cond = masm->CheckBothSmi(rdx, rdx);
+ __ j(cond, &exit);
+
+ // CheckInteger32ValidSmiValue
+ __ incq(rax);
+ __ movq(rcx, Immediate(0));
+ cond = masm->CheckInteger32ValidSmiValue(rax);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(-1));
+ cond = masm->CheckInteger32ValidSmiValue(rax);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(Smi::kMaxValue));
+ cond = masm->CheckInteger32ValidSmiValue(rax);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(Smi::kMinValue));
+ cond = masm->CheckInteger32ValidSmiValue(rax);
+ __ j(NegateCondition(cond), &exit);
+
+ // Success
+ __ xor_(rax, rax);
+
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+
+void TestSmiNeg(MacroAssembler* masm, Label* exit, int id, int x) {
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+ if (x == Smi::kMinValue || x == 0) {
+ // Negation fails.
+ __ movl(rax, Immediate(id + 8));
+ __ SmiNeg(r9, rcx, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiNeg(rcx, rcx, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+ } else {
+ Label smi_ok, smi_ok2;
+ int result = -x;
+ __ movl(rax, Immediate(id));
+ __ Move(r8, Smi::FromInt(result));
+
+ __ SmiNeg(r9, rcx, &smi_ok);
+ __ jmp(exit);
+ __ bind(&smi_ok);
+ __ incq(rax);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiNeg(rcx, rcx, &smi_ok2);
+ __ jmp(exit);
+ __ bind(&smi_ok2);
+ __ incq(rax);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+ }
+}
+
+
+TEST(SmiNeg) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiNeg(masm, &exit, 0x10, 0);
+ TestSmiNeg(masm, &exit, 0x20, 1);
+ TestSmiNeg(masm, &exit, 0x30, -1);
+ TestSmiNeg(masm, &exit, 0x40, 127);
+ TestSmiNeg(masm, &exit, 0x50, 65535);
+ TestSmiNeg(masm, &exit, 0x60, Smi::kMinValue);
+ TestSmiNeg(masm, &exit, 0x70, Smi::kMaxValue);
+ TestSmiNeg(masm, &exit, 0x80, -Smi::kMaxValue);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+
+
+static void SmiAddTest(MacroAssembler* masm,
+ Label* exit,
+ int id,
+ int first,
+ int second) {
+ __ movl(rcx, Immediate(first));
+ __ Integer32ToSmi(rcx, rcx);
+ __ movl(rdx, Immediate(second));
+ __ Integer32ToSmi(rdx, rdx);
+ __ movl(r8, Immediate(first + second));
+ __ Integer32ToSmi(r8, r8);
+
+ __ movl(rax, Immediate(id)); // Test number.
+ __ SmiAdd(r9, rcx, rdx, exit);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiAdd(rcx, rcx, rdx, exit); \
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+
+ __ movl(rcx, Immediate(first));
+ __ Integer32ToSmi(rcx, rcx);
+
+ __ incq(rax);
+ __ SmiAddConstant(r9, rcx, Smi::FromInt(second));
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ SmiAddConstant(rcx, rcx, Smi::FromInt(second));
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+
+ __ movl(rcx, Immediate(first));
+ __ Integer32ToSmi(rcx, rcx);
+
+ __ incq(rax);
+ __ SmiAddConstant(r9, rcx, Smi::FromInt(second), exit);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiAddConstant(rcx, rcx, Smi::FromInt(second), exit);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+}
+
+TEST(SmiAdd) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ // No-overflow tests.
+ SmiAddTest(masm, &exit, 0x10, 1, 2);
+ SmiAddTest(masm, &exit, 0x20, 1, -2);
+ SmiAddTest(masm, &exit, 0x30, -1, 2);
+ SmiAddTest(masm, &exit, 0x40, -1, -2);
+ SmiAddTest(masm, &exit, 0x50, 0x1000, 0x2000);
+ SmiAddTest(masm, &exit, 0x60, Smi::kMinValue, 5);
+ SmiAddTest(masm, &exit, 0x70, Smi::kMaxValue, -5);
+ SmiAddTest(masm, &exit, 0x80, Smi::kMaxValue, Smi::kMinValue);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+static void SmiSubTest(MacroAssembler* masm,
+ Label* exit,
+ int id,
+ int first,
+ int second) {
+ __ Move(rcx, Smi::FromInt(first));
+ __ Move(rdx, Smi::FromInt(second));
+ __ Move(r8, Smi::FromInt(first - second));
+
+ __ movl(rax, Immediate(id)); // Test 0.
+ __ SmiSub(r9, rcx, rdx, exit);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax); // Test 1.
+ __ SmiSub(rcx, rcx, rdx, exit);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+
+ __ Move(rcx, Smi::FromInt(first));
+
+ __ incq(rax); // Test 2.
+ __ SmiSubConstant(r9, rcx, Smi::FromInt(second));
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax); // Test 3.
+ __ SmiSubConstant(rcx, rcx, Smi::FromInt(second));
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+
+ __ Move(rcx, Smi::FromInt(first));
+
+ __ incq(rax); // Test 4.
+ __ SmiSubConstant(r9, rcx, Smi::FromInt(second), exit);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax); // Test 5.
+ __ SmiSubConstant(rcx, rcx, Smi::FromInt(second), exit);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+}
+
+static void SmiSubOverflowTest(MacroAssembler* masm,
+ Label* exit,
+ int id,
+ int x) {
+ // Subtracts a Smi from x so that the subtraction overflows.
+ ASSERT(x != -1); // Can't overflow by subtracting a Smi.
+ int y_max = (x < 0) ? (Smi::kMaxValue + 0) : (Smi::kMinValue + 0);
+ int y_min = (x < 0) ? (Smi::kMaxValue + x + 2) : (Smi::kMinValue + x);
+
+ __ movl(rax, Immediate(id));
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx); // Store original Smi value of x in r11.
+ __ Move(rdx, Smi::FromInt(y_min));
+ {
+ Label overflow_ok;
+ __ SmiSub(r9, rcx, rdx, &overflow_ok);
+ __ jmp(exit);
+ __ bind(&overflow_ok);
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+
+ {
+ Label overflow_ok;
+ __ incq(rax);
+ __ SmiSub(rcx, rcx, rdx, &overflow_ok);
+ __ jmp(exit);
+ __ bind(&overflow_ok);
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+
+ __ movq(rcx, r11);
+ {
+ Label overflow_ok;
+ __ incq(rax);
+ __ SmiSubConstant(r9, rcx, Smi::FromInt(y_min), &overflow_ok);
+ __ jmp(exit);
+ __ bind(&overflow_ok);
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+
+ {
+ Label overflow_ok;
+ __ incq(rax);
+ __ SmiSubConstant(rcx, rcx, Smi::FromInt(y_min), &overflow_ok);
+ __ jmp(exit);
+ __ bind(&overflow_ok);
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+
+ __ Move(rdx, Smi::FromInt(y_max));
+
+ {
+ Label overflow_ok;
+ __ incq(rax);
+ __ SmiSub(r9, rcx, rdx, &overflow_ok);
+ __ jmp(exit);
+ __ bind(&overflow_ok);
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+
+ {
+ Label overflow_ok;
+ __ incq(rax);
+ __ SmiSub(rcx, rcx, rdx, &overflow_ok);
+ __ jmp(exit);
+ __ bind(&overflow_ok);
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+
+ __ movq(rcx, r11);
+ {
+ Label overflow_ok;
+ __ incq(rax);
+ __ SmiSubConstant(r9, rcx, Smi::FromInt(y_max), &overflow_ok);
+ __ jmp(exit);
+ __ bind(&overflow_ok);
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+
+ {
+ Label overflow_ok;
+ __ incq(rax);
+ __ SmiSubConstant(rcx, rcx, Smi::FromInt(y_max), &overflow_ok);
+ __ jmp(exit);
+ __ bind(&overflow_ok);
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+}
+
+
+TEST(SmiSub) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 2,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ SmiSubTest(masm, &exit, 0x10, 1, 2);
+ SmiSubTest(masm, &exit, 0x20, 1, -2);
+ SmiSubTest(masm, &exit, 0x30, -1, 2);
+ SmiSubTest(masm, &exit, 0x40, -1, -2);
+ SmiSubTest(masm, &exit, 0x50, 0x1000, 0x2000);
+ SmiSubTest(masm, &exit, 0x60, Smi::kMinValue, -5);
+ SmiSubTest(masm, &exit, 0x70, Smi::kMaxValue, 5);
+ SmiSubTest(masm, &exit, 0x80, -Smi::kMaxValue, Smi::kMinValue);
+ SmiSubTest(masm, &exit, 0x90, 0, Smi::kMaxValue);
+
+ SmiSubOverflowTest(masm, &exit, 0xA0, 1);
+ SmiSubOverflowTest(masm, &exit, 0xB0, 1024);
+ SmiSubOverflowTest(masm, &exit, 0xC0, Smi::kMaxValue);
+ SmiSubOverflowTest(masm, &exit, 0xD0, -2);
+ SmiSubOverflowTest(masm, &exit, 0xE0, -42000);
+ SmiSubOverflowTest(masm, &exit, 0xF0, Smi::kMinValue);
+ SmiSubOverflowTest(masm, &exit, 0x100, 0);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+
+void TestSmiMul(MacroAssembler* masm, Label* exit, int id, int x, int y) {
+ int64_t result = static_cast<int64_t>(x) * static_cast<int64_t>(y);
+ bool negative_zero = (result == 0) && (x < 0 || y < 0);
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+ __ Move(rdx, Smi::FromInt(y));
+ if (Smi::IsValid(result) && !negative_zero) {
+ __ movl(rax, Immediate(id));
+ __ Move(r8, Smi::FromIntptr(result));
+ __ SmiMul(r9, rcx, rdx, exit);
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+ __ incq(rax);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiMul(rcx, rcx, rdx, exit);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+ } else {
+ __ movl(rax, Immediate(id + 8));
+ Label overflow_ok, overflow_ok2;
+ __ SmiMul(r9, rcx, rdx, &overflow_ok);
+ __ jmp(exit);
+ __ bind(&overflow_ok);
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+ __ incq(rax);
+ __ SmiMul(rcx, rcx, rdx, &overflow_ok2);
+ __ jmp(exit);
+ __ bind(&overflow_ok2);
+ // 31-bit version doesn't preserve rcx on failure.
+ // __ incq(rax);
+ // __ SmiCompare(r11, rcx);
+ // __ j(not_equal, exit);
+ }
+}
+
+
+TEST(SmiMul) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiMul(masm, &exit, 0x10, 0, 0);
+ TestSmiMul(masm, &exit, 0x20, -1, 0);
+ TestSmiMul(masm, &exit, 0x30, 0, -1);
+ TestSmiMul(masm, &exit, 0x40, -1, -1);
+ TestSmiMul(masm, &exit, 0x50, 0x10000, 0x10000);
+ TestSmiMul(masm, &exit, 0x60, 0x10000, 0xffff);
+ TestSmiMul(masm, &exit, 0x70, 0x10000, 0xffff);
+ TestSmiMul(masm, &exit, 0x80, Smi::kMaxValue, -1);
+ TestSmiMul(masm, &exit, 0x90, Smi::kMaxValue, -2);
+ TestSmiMul(masm, &exit, 0xa0, Smi::kMaxValue, 2);
+ TestSmiMul(masm, &exit, 0xb0, (Smi::kMaxValue / 2), 2);
+ TestSmiMul(masm, &exit, 0xc0, (Smi::kMaxValue / 2) + 1, 2);
+ TestSmiMul(masm, &exit, 0xd0, (Smi::kMinValue / 2), 2);
+ TestSmiMul(masm, &exit, 0xe0, (Smi::kMinValue / 2) - 1, 2);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiDiv(MacroAssembler* masm, Label* exit, int id, int x, int y) {
+ bool division_by_zero = (y == 0);
+ bool negative_zero = (x == 0 && y < 0);
+#ifdef V8_TARGET_ARCH_X64
+ bool overflow = (x == Smi::kMinValue && y < 0); // Safe approx. used.
+#else
+ bool overflow = (x == Smi::kMinValue && y == -1);
+#endif
+ bool fraction = !division_by_zero && !overflow && (x % y != 0);
+ __ Move(r11, Smi::FromInt(x));
+ __ Move(r12, Smi::FromInt(y));
+ if (!fraction && !overflow && !negative_zero && !division_by_zero) {
+ // Division succeeds
+ __ movq(rcx, r11);
+ __ movq(r15, Immediate(id));
+ int result = x / y;
+ __ Move(r8, Smi::FromInt(result));
+ __ SmiDiv(r9, rcx, r12, exit);
+ // Might have destroyed rcx and r12.
+ __ incq(r15);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(r15);
+ __ movq(rcx, r11);
+ __ Move(r12, Smi::FromInt(y));
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+
+ __ incq(r15);
+ __ SmiDiv(rcx, rcx, r12, exit);
+
+ __ incq(r15);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+ } else {
+ // Division fails.
+ __ movq(r15, Immediate(id + 8));
+
+ Label fail_ok, fail_ok2;
+ __ movq(rcx, r11);
+ __ SmiDiv(r9, rcx, r12, &fail_ok);
+ __ jmp(exit);
+ __ bind(&fail_ok);
+
+ __ incq(r15);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+
+ __ incq(r15);
+ __ SmiDiv(rcx, rcx, r12, &fail_ok2);
+ __ jmp(exit);
+ __ bind(&fail_ok2);
+
+ __ incq(r15);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+}
+
+
+TEST(SmiDiv) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 2,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiDiv(masm, &exit, 0x10, 1, 1);
+ TestSmiDiv(masm, &exit, 0x20, 1, 0);
+ TestSmiDiv(masm, &exit, 0x30, -1, 0);
+ TestSmiDiv(masm, &exit, 0x40, 0, 1);
+ TestSmiDiv(masm, &exit, 0x50, 0, -1);
+ TestSmiDiv(masm, &exit, 0x60, 4, 2);
+ TestSmiDiv(masm, &exit, 0x70, -4, 2);
+ TestSmiDiv(masm, &exit, 0x80, 4, -2);
+ TestSmiDiv(masm, &exit, 0x90, -4, -2);
+ TestSmiDiv(masm, &exit, 0xa0, 3, 2);
+ TestSmiDiv(masm, &exit, 0xb0, 3, 4);
+ TestSmiDiv(masm, &exit, 0xc0, 1, Smi::kMaxValue);
+ TestSmiDiv(masm, &exit, 0xd0, -1, Smi::kMaxValue);
+ TestSmiDiv(masm, &exit, 0xe0, Smi::kMaxValue, 1);
+ TestSmiDiv(masm, &exit, 0xf0, Smi::kMaxValue, Smi::kMaxValue);
+ TestSmiDiv(masm, &exit, 0x100, Smi::kMaxValue, -Smi::kMaxValue);
+ TestSmiDiv(masm, &exit, 0x110, Smi::kMaxValue, -1);
+ TestSmiDiv(masm, &exit, 0x120, Smi::kMinValue, 1);
+ TestSmiDiv(masm, &exit, 0x130, Smi::kMinValue, Smi::kMinValue);
+ TestSmiDiv(masm, &exit, 0x140, Smi::kMinValue, -1);
+
+ __ xor_(r15, r15); // Success.
+ __ bind(&exit);
+ __ movq(rax, r15);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiMod(MacroAssembler* masm, Label* exit, int id, int x, int y) {
+ bool division_by_zero = (y == 0);
+ bool division_overflow = (x == Smi::kMinValue) && (y == -1);
+ bool fraction = !division_by_zero && !division_overflow && ((x % y) != 0);
+ bool negative_zero = (!fraction && x < 0);
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+ __ Move(r12, Smi::FromInt(y));
+ if (!division_overflow && !negative_zero && !division_by_zero) {
+ // Modulo succeeds
+ __ movq(r15, Immediate(id));
+ int result = x % y;
+ __ Move(r8, Smi::FromInt(result));
+ __ SmiMod(r9, rcx, r12, exit);
+
+ __ incq(r15);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(r15);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+
+ __ incq(r15);
+ __ SmiMod(rcx, rcx, r12, exit);
+
+ __ incq(r15);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+ } else {
+ // Modulo fails.
+ __ movq(r15, Immediate(id + 8));
+
+ Label fail_ok, fail_ok2;
+ __ SmiMod(r9, rcx, r12, &fail_ok);
+ __ jmp(exit);
+ __ bind(&fail_ok);
+
+ __ incq(r15);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+
+ __ incq(r15);
+ __ SmiMod(rcx, rcx, r12, &fail_ok2);
+ __ jmp(exit);
+ __ bind(&fail_ok2);
+
+ __ incq(r15);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+}
+
+
+TEST(SmiMod) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 2,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiMod(masm, &exit, 0x10, 1, 1);
+ TestSmiMod(masm, &exit, 0x20, 1, 0);
+ TestSmiMod(masm, &exit, 0x30, -1, 0);
+ TestSmiMod(masm, &exit, 0x40, 0, 1);
+ TestSmiMod(masm, &exit, 0x50, 0, -1);
+ TestSmiMod(masm, &exit, 0x60, 4, 2);
+ TestSmiMod(masm, &exit, 0x70, -4, 2);
+ TestSmiMod(masm, &exit, 0x80, 4, -2);
+ TestSmiMod(masm, &exit, 0x90, -4, -2);
+ TestSmiMod(masm, &exit, 0xa0, 3, 2);
+ TestSmiMod(masm, &exit, 0xb0, 3, 4);
+ TestSmiMod(masm, &exit, 0xc0, 1, Smi::kMaxValue);
+ TestSmiMod(masm, &exit, 0xd0, -1, Smi::kMaxValue);
+ TestSmiMod(masm, &exit, 0xe0, Smi::kMaxValue, 1);
+ TestSmiMod(masm, &exit, 0xf0, Smi::kMaxValue, Smi::kMaxValue);
+ TestSmiMod(masm, &exit, 0x100, Smi::kMaxValue, -Smi::kMaxValue);
+ TestSmiMod(masm, &exit, 0x110, Smi::kMaxValue, -1);
+ TestSmiMod(masm, &exit, 0x120, Smi::kMinValue, 1);
+ TestSmiMod(masm, &exit, 0x130, Smi::kMinValue, Smi::kMinValue);
+ TestSmiMod(masm, &exit, 0x140, Smi::kMinValue, -1);
+
+ __ xor_(r15, r15); // Success.
+ __ bind(&exit);
+ __ movq(rax, r15);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiIndex(MacroAssembler* masm, Label* exit, int id, int x) {
+ __ movl(rax, Immediate(id));
+
+ for (int i = 0; i < 8; i++) {
+ __ Move(rcx, Smi::FromInt(x));
+ SmiIndex index = masm->SmiToIndex(rdx, rcx, i);
+ ASSERT(index.reg.is(rcx) || index.reg.is(rdx));
+ __ shl(index.reg, Immediate(index.scale));
+ __ Set(r8, static_cast<intptr_t>(x) << i);
+ __ SmiCompare(index.reg, r8);
+ __ j(not_equal, exit);
+ __ incq(rax);
+ __ Move(rcx, Smi::FromInt(x));
+ index = masm->SmiToIndex(rcx, rcx, i);
+ ASSERT(index.reg.is(rcx));
+ __ shl(rcx, Immediate(index.scale));
+ __ Set(r8, static_cast<intptr_t>(x) << i);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+ __ incq(rax);
+
+ __ Move(rcx, Smi::FromInt(x));
+ index = masm->SmiToNegativeIndex(rdx, rcx, i);
+ ASSERT(index.reg.is(rcx) || index.reg.is(rdx));
+ __ shl(index.reg, Immediate(index.scale));
+ __ Set(r8, static_cast<intptr_t>(-x) << i);
+ __ SmiCompare(index.reg, r8);
+ __ j(not_equal, exit);
+ __ incq(rax);
+ __ Move(rcx, Smi::FromInt(x));
+ index = masm->SmiToNegativeIndex(rcx, rcx, i);
+ ASSERT(index.reg.is(rcx));
+ __ shl(rcx, Immediate(index.scale));
+ __ Set(r8, static_cast<intptr_t>(-x) << i);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+ __ incq(rax);
+ }
+}
+
+TEST(SmiIndex) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 2,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiIndex(masm, &exit, 0x10, 0);
+ TestSmiIndex(masm, &exit, 0x20, 1);
+ TestSmiIndex(masm, &exit, 0x30, 100);
+ TestSmiIndex(masm, &exit, 0x40, 1000);
+ TestSmiIndex(masm, &exit, 0x50, Smi::kMaxValue);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSelectNonSmi(MacroAssembler* masm, Label* exit, int id, int x, int y) {
+ __ movl(rax, Immediate(id));
+ __ Move(rcx, Smi::FromInt(x));
+ __ Move(rdx, Smi::FromInt(y));
+ __ xor_(rdx, Immediate(kSmiTagMask));
+ __ SelectNonSmi(r9, rcx, rdx, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r9, rdx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(rcx, Smi::FromInt(x));
+ __ Move(rdx, Smi::FromInt(y));
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ __ SelectNonSmi(r9, rcx, rdx, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r9, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ Label fail_ok;
+ __ Move(rcx, Smi::FromInt(x));
+ __ Move(rdx, Smi::FromInt(y));
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ __ xor_(rdx, Immediate(kSmiTagMask));
+ __ SelectNonSmi(r9, rcx, rdx, &fail_ok);
+ __ jmp(exit);
+ __ bind(&fail_ok);
+}
+
+
+TEST(SmiSelectNonSmi) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false); // Avoid inline checks.
+ Label exit;
+
+ TestSelectNonSmi(masm, &exit, 0x10, 0, 0);
+ TestSelectNonSmi(masm, &exit, 0x20, 0, 1);
+ TestSelectNonSmi(masm, &exit, 0x30, 1, 0);
+ TestSelectNonSmi(masm, &exit, 0x40, 0, -1);
+ TestSelectNonSmi(masm, &exit, 0x50, -1, 0);
+ TestSelectNonSmi(masm, &exit, 0x60, -1, -1);
+ TestSelectNonSmi(masm, &exit, 0x70, 1, 1);
+ TestSelectNonSmi(masm, &exit, 0x80, Smi::kMinValue, Smi::kMaxValue);
+ TestSelectNonSmi(masm, &exit, 0x90, Smi::kMinValue, Smi::kMinValue);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiAnd(MacroAssembler* masm, Label* exit, int id, int x, int y) {
+ int result = x & y;
+
+ __ movl(rax, Immediate(id));
+
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+ __ Move(rdx, Smi::FromInt(y));
+ __ Move(r8, Smi::FromInt(result));
+ __ SmiAnd(r9, rcx, rdx);
+ __ SmiCompare(r8, r9);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiAnd(rcx, rcx, rdx);
+ __ SmiCompare(r8, rcx);
+ __ j(not_equal, exit);
+
+ __ movq(rcx, r11);
+ __ incq(rax);
+ __ SmiAndConstant(r9, rcx, Smi::FromInt(y));
+ __ SmiCompare(r8, r9);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiAndConstant(rcx, rcx, Smi::FromInt(y));
+ __ SmiCompare(r8, rcx);
+ __ j(not_equal, exit);
+}
+
+
+TEST(SmiAnd) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiAnd(masm, &exit, 0x10, 0, 0);
+ TestSmiAnd(masm, &exit, 0x20, 0, 1);
+ TestSmiAnd(masm, &exit, 0x30, 1, 0);
+ TestSmiAnd(masm, &exit, 0x40, 0, -1);
+ TestSmiAnd(masm, &exit, 0x50, -1, 0);
+ TestSmiAnd(masm, &exit, 0x60, -1, -1);
+ TestSmiAnd(masm, &exit, 0x70, 1, 1);
+ TestSmiAnd(masm, &exit, 0x80, Smi::kMinValue, Smi::kMaxValue);
+ TestSmiAnd(masm, &exit, 0x90, Smi::kMinValue, Smi::kMinValue);
+ TestSmiAnd(masm, &exit, 0xA0, Smi::kMinValue, -1);
+ TestSmiAnd(masm, &exit, 0xB0, Smi::kMinValue, -1);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiOr(MacroAssembler* masm, Label* exit, int id, int x, int y) {
+ int result = x | y;
+
+ __ movl(rax, Immediate(id));
+
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+ __ Move(rdx, Smi::FromInt(y));
+ __ Move(r8, Smi::FromInt(result));
+ __ SmiOr(r9, rcx, rdx);
+ __ SmiCompare(r8, r9);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiOr(rcx, rcx, rdx);
+ __ SmiCompare(r8, rcx);
+ __ j(not_equal, exit);
+
+ __ movq(rcx, r11);
+ __ incq(rax);
+ __ SmiOrConstant(r9, rcx, Smi::FromInt(y));
+ __ SmiCompare(r8, r9);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiOrConstant(rcx, rcx, Smi::FromInt(y));
+ __ SmiCompare(r8, rcx);
+ __ j(not_equal, exit);
+}
+
+
+TEST(SmiOr) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiOr(masm, &exit, 0x10, 0, 0);
+ TestSmiOr(masm, &exit, 0x20, 0, 1);
+ TestSmiOr(masm, &exit, 0x30, 1, 0);
+ TestSmiOr(masm, &exit, 0x40, 0, -1);
+ TestSmiOr(masm, &exit, 0x50, -1, 0);
+ TestSmiOr(masm, &exit, 0x60, -1, -1);
+ TestSmiOr(masm, &exit, 0x70, 1, 1);
+ TestSmiOr(masm, &exit, 0x80, Smi::kMinValue, Smi::kMaxValue);
+ TestSmiOr(masm, &exit, 0x90, Smi::kMinValue, Smi::kMinValue);
+ TestSmiOr(masm, &exit, 0xA0, Smi::kMinValue, -1);
+ TestSmiOr(masm, &exit, 0xB0, 0x05555555, 0x01234567);
+ TestSmiOr(masm, &exit, 0xC0, 0x05555555, 0x0fedcba9);
+ TestSmiOr(masm, &exit, 0xD0, Smi::kMinValue, -1);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiXor(MacroAssembler* masm, Label* exit, int id, int x, int y) {
+ int result = x ^ y;
+
+ __ movl(rax, Immediate(id));
+
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+ __ Move(rdx, Smi::FromInt(y));
+ __ Move(r8, Smi::FromInt(result));
+ __ SmiXor(r9, rcx, rdx);
+ __ SmiCompare(r8, r9);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiXor(rcx, rcx, rdx);
+ __ SmiCompare(r8, rcx);
+ __ j(not_equal, exit);
+
+ __ movq(rcx, r11);
+ __ incq(rax);
+ __ SmiXorConstant(r9, rcx, Smi::FromInt(y));
+ __ SmiCompare(r8, r9);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiXorConstant(rcx, rcx, Smi::FromInt(y));
+ __ SmiCompare(r8, rcx);
+ __ j(not_equal, exit);
+}
+
+
+TEST(SmiXor) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiXor(masm, &exit, 0x10, 0, 0);
+ TestSmiXor(masm, &exit, 0x20, 0, 1);
+ TestSmiXor(masm, &exit, 0x30, 1, 0);
+ TestSmiXor(masm, &exit, 0x40, 0, -1);
+ TestSmiXor(masm, &exit, 0x50, -1, 0);
+ TestSmiXor(masm, &exit, 0x60, -1, -1);
+ TestSmiXor(masm, &exit, 0x70, 1, 1);
+ TestSmiXor(masm, &exit, 0x80, Smi::kMinValue, Smi::kMaxValue);
+ TestSmiXor(masm, &exit, 0x90, Smi::kMinValue, Smi::kMinValue);
+ TestSmiXor(masm, &exit, 0xA0, Smi::kMinValue, -1);
+ TestSmiXor(masm, &exit, 0xB0, 0x5555555, 0x01234567);
+ TestSmiXor(masm, &exit, 0xC0, 0x5555555, 0x0fedcba9);
+ TestSmiXor(masm, &exit, 0xD0, Smi::kMinValue, -1);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiNot(MacroAssembler* masm, Label* exit, int id, int x) {
+ int result = ~x;
+ __ movl(rax, Immediate(id));
+
+ __ Move(r8, Smi::FromInt(result));
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+
+ __ SmiNot(r9, rcx);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiNot(rcx, rcx);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+}
+
+
+TEST(SmiNot) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiNot(masm, &exit, 0x10, 0);
+ TestSmiNot(masm, &exit, 0x20, 1);
+ TestSmiNot(masm, &exit, 0x30, -1);
+ TestSmiNot(masm, &exit, 0x40, 127);
+ TestSmiNot(masm, &exit, 0x50, 65535);
+ TestSmiNot(masm, &exit, 0x60, Smi::kMinValue);
+ TestSmiNot(masm, &exit, 0x70, Smi::kMaxValue);
+ TestSmiNot(masm, &exit, 0x80, 0x05555555);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiShiftLeft(MacroAssembler* masm, Label* exit, int id, int x) {
+ const int shifts[] = { 0, 1, 7, 24, kSmiValueSize - 1};
+ const int kNumShifts = 5;
+ __ movl(rax, Immediate(id));
+ for (int i = 0; i < kNumShifts; i++) {
+ // rax == id + i * 10.
+ int shift = shifts[i];
+ int result = x << shift;
+ if (Smi::IsValid(result)) {
+ __ Move(r8, Smi::FromInt(result));
+ __ Move(rcx, Smi::FromInt(x));
+ __ SmiShiftLeftConstant(r9, rcx, shift, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(rcx, Smi::FromInt(x));
+ __ SmiShiftLeftConstant(rcx, rcx, shift, exit);
+
+ __ incq(rax);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(rdx, Smi::FromInt(x));
+ __ Move(rcx, Smi::FromInt(shift));
+ __ SmiShiftLeft(r9, rdx, rcx, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(rdx, Smi::FromInt(x));
+ __ Move(r11, Smi::FromInt(shift));
+ __ SmiShiftLeft(r9, rdx, r11, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(rdx, Smi::FromInt(x));
+ __ Move(r11, Smi::FromInt(shift));
+ __ SmiShiftLeft(rdx, rdx, r11, exit);
+
+ __ incq(rax);
+ __ SmiCompare(rdx, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ } else {
+ // Cannot happen with long smis.
+ Label fail_ok;
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+ __ SmiShiftLeftConstant(r9, rcx, shift, &fail_ok);
+ __ jmp(exit);
+ __ bind(&fail_ok);
+
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ Label fail_ok2;
+ __ SmiShiftLeftConstant(rcx, rcx, shift, &fail_ok2);
+ __ jmp(exit);
+ __ bind(&fail_ok2);
+
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(r8, Smi::FromInt(shift));
+ Label fail_ok3;
+ __ SmiShiftLeft(r9, rcx, r8, &fail_ok3);
+ __ jmp(exit);
+ __ bind(&fail_ok3);
+
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(r8, Smi::FromInt(shift));
+ __ movq(rdx, r11);
+ Label fail_ok4;
+ __ SmiShiftLeft(rdx, rdx, r8, &fail_ok4);
+ __ jmp(exit);
+ __ bind(&fail_ok4);
+
+ __ incq(rax);
+ __ SmiCompare(rdx, r11);
+ __ j(not_equal, exit);
+
+ __ addq(rax, Immediate(3));
+ }
+ }
+}
+
+
+TEST(SmiShiftLeft) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 3,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiShiftLeft(masm, &exit, 0x10, 0);
+ TestSmiShiftLeft(masm, &exit, 0x50, 1);
+ TestSmiShiftLeft(masm, &exit, 0x90, 127);
+ TestSmiShiftLeft(masm, &exit, 0xD0, 65535);
+ TestSmiShiftLeft(masm, &exit, 0x110, Smi::kMaxValue);
+ TestSmiShiftLeft(masm, &exit, 0x150, Smi::kMinValue);
+ TestSmiShiftLeft(masm, &exit, 0x190, -1);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiShiftLogicalRight(MacroAssembler* masm,
+ Label* exit,
+ int id,
+ int x) {
+ const int shifts[] = { 0, 1, 7, 24, kSmiValueSize - 1};
+ const int kNumShifts = 5;
+ __ movl(rax, Immediate(id));
+ for (int i = 0; i < kNumShifts; i++) {
+ int shift = shifts[i];
+ intptr_t result = static_cast<unsigned int>(x) >> shift;
+ if (Smi::IsValid(result)) {
+ __ Move(r8, Smi::FromInt(result));
+ __ Move(rcx, Smi::FromInt(x));
+ __ SmiShiftLogicalRightConstant(r9, rcx, shift, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(rdx, Smi::FromInt(x));
+ __ Move(rcx, Smi::FromInt(shift));
+ __ SmiShiftLogicalRight(r9, rdx, rcx, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(rdx, Smi::FromInt(x));
+ __ Move(r11, Smi::FromInt(shift));
+ __ SmiShiftLogicalRight(r9, rdx, r11, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ } else {
+ // Cannot happen with long smis.
+ Label fail_ok;
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+ __ SmiShiftLogicalRightConstant(r9, rcx, shift, &fail_ok);
+ __ jmp(exit);
+ __ bind(&fail_ok);
+
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(r8, Smi::FromInt(shift));
+ Label fail_ok3;
+ __ SmiShiftLogicalRight(r9, rcx, r8, &fail_ok3);
+ __ jmp(exit);
+ __ bind(&fail_ok3);
+
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+
+ __ addq(rax, Immediate(3));
+ }
+ }
+}
+
+
+TEST(SmiShiftLogicalRight) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 2,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiShiftLogicalRight(masm, &exit, 0x10, 0);
+ TestSmiShiftLogicalRight(masm, &exit, 0x30, 1);
+ TestSmiShiftLogicalRight(masm, &exit, 0x50, 127);
+ TestSmiShiftLogicalRight(masm, &exit, 0x70, 65535);
+ TestSmiShiftLogicalRight(masm, &exit, 0x90, Smi::kMaxValue);
+ TestSmiShiftLogicalRight(masm, &exit, 0xB0, Smi::kMinValue);
+ TestSmiShiftLogicalRight(masm, &exit, 0xD0, -1);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiShiftArithmeticRight(MacroAssembler* masm,
+ Label* exit,
+ int id,
+ int x) {
+ const int shifts[] = { 0, 1, 7, 24, kSmiValueSize - 1};
+ const int kNumShifts = 5;
+ __ movl(rax, Immediate(id));
+ for (int i = 0; i < kNumShifts; i++) {
+ int shift = shifts[i];
+ // Guaranteed arithmetic shift.
+ int result = (x < 0) ? ~((~x) >> shift) : (x >> shift);
+ __ Move(r8, Smi::FromInt(result));
+ __ Move(rcx, Smi::FromInt(x));
+ __ SmiShiftArithmeticRightConstant(rcx, rcx, shift);
+
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(rdx, Smi::FromInt(x));
+ __ Move(r11, Smi::FromInt(shift));
+ __ SmiShiftArithmeticRight(rdx, rdx, r11);
+
+ __ SmiCompare(rdx, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ }
+}
+
+
+TEST(SmiShiftArithmeticRight) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 2,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiShiftArithmeticRight(masm, &exit, 0x10, 0);
+ TestSmiShiftArithmeticRight(masm, &exit, 0x20, 1);
+ TestSmiShiftArithmeticRight(masm, &exit, 0x30, 127);
+ TestSmiShiftArithmeticRight(masm, &exit, 0x40, 65535);
+ TestSmiShiftArithmeticRight(masm, &exit, 0x50, Smi::kMaxValue);
+ TestSmiShiftArithmeticRight(masm, &exit, 0x60, Smi::kMinValue);
+ TestSmiShiftArithmeticRight(masm, &exit, 0x70, -1);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestPositiveSmiPowerUp(MacroAssembler* masm, Label* exit, int id, int x) {
+ ASSERT(x >= 0);
+ int powers[] = { 0, 1, 2, 3, 8, 16, 24, 31 };
+ int power_count = 8;
+ __ movl(rax, Immediate(id));
+ for (int i = 0; i < power_count; i++) {
+ int power = powers[i];
+ intptr_t result = static_cast<intptr_t>(x) << power;
+ __ Set(r8, result);
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+ __ PositiveSmiTimesPowerOfTwoToInteger64(rdx, rcx, power);
+ __ SmiCompare(rdx, r8);
+ __ j(not_equal, exit);
+ __ incq(rax);
+ __ SmiCompare(r11, rcx); // rcx unchanged.
+ __ j(not_equal, exit);
+ __ incq(rax);
+ __ PositiveSmiTimesPowerOfTwoToInteger64(rcx, rcx, power);
+ __ SmiCompare(rdx, r8);
+ __ j(not_equal, exit);
+ __ incq(rax);
+ }
+}
+
+
+TEST(PositiveSmiTimesPowerOfTwoToInteger64) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 2,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestPositiveSmiPowerUp(masm, &exit, 0x20, 0);
+ TestPositiveSmiPowerUp(masm, &exit, 0x40, 1);
+ TestPositiveSmiPowerUp(masm, &exit, 0x60, 127);
+ TestPositiveSmiPowerUp(masm, &exit, 0x80, 128);
+ TestPositiveSmiPowerUp(masm, &exit, 0xA0, 255);
+ TestPositiveSmiPowerUp(masm, &exit, 0xC0, 256);
+ TestPositiveSmiPowerUp(masm, &exit, 0x100, 65535);
+ TestPositiveSmiPowerUp(masm, &exit, 0x120, 65536);
+ TestPositiveSmiPowerUp(masm, &exit, 0x140, Smi::kMaxValue);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+#undef __
diff --git a/deps/v8/test/mjsunit/compiler/literals-assignment.js b/deps/v8/test/mjsunit/compiler/literals-assignment.js
new file mode 100644
index 000000000..932bfa7f1
--- /dev/null
+++ b/deps/v8/test/mjsunit/compiler/literals-assignment.js
@@ -0,0 +1,71 @@
+// 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.
+
+// Tests for simple assignments and literals inside an anonymous function
+
+// Test simple return statement.
+assertEquals(8, eval("(function() { return 8; })()"));
+
+// Test simple assignment
+var code = "(function() {\
+ var a;\
+ a = 8;\
+ return a;\
+ })()";
+assertEquals(8, eval(code));
+
+code = "(function() {\
+ var x;\
+ x = 'abc';\
+ return x;\
+ })()";
+assertEquals("abc", eval(code));
+
+// Test assignment as an RHS expression
+
+code = "(function() {\
+ var x, y;\
+ x = y = 8;\
+ return x;\
+ })()";
+assertEquals(8, eval(code));
+
+
+code = "(function() {\
+ var x, y;\
+ x = y = 8;\
+ return y;\
+ })()";
+assertEquals(8, eval(code));
+
+
+code = "(function() {\
+ var x,y,z;\
+ return x = y = z = 8;\
+ })()";
+assertEquals(8, eval(code));
+
diff --git a/deps/v8/test/mjsunit/compiler/literals.js b/deps/v8/test/mjsunit/compiler/literals.js
new file mode 100644
index 000000000..e0e532fa2
--- /dev/null
+++ b/deps/v8/test/mjsunit/compiler/literals.js
@@ -0,0 +1,35 @@
+// 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.
+
+// Test simple literals.
+assertEquals(8, eval("8"));
+
+assertEquals(null, eval("null"));
+
+assertEquals("abc", eval("'abc'"));
+
+assertEquals(8, eval("6;'abc';8"));
diff --git a/deps/v8/test/mjsunit/debug-backtrace.js b/deps/v8/test/mjsunit/debug-backtrace.js
index 0c200aee3..d15b2d261 100644
--- a/deps/v8/test/mjsunit/debug-backtrace.js
+++ b/deps/v8/test/mjsunit/debug-backtrace.js
@@ -69,6 +69,11 @@ ParsedResponse.prototype.body = function() {
}
+ParsedResponse.prototype.running = function() {
+ return this.response_.running;
+}
+
+
ParsedResponse.prototype.lookup = function(handle) {
return this.refs_[handle];
}
@@ -88,8 +93,9 @@ function listener(event, exec_state, event_data, data) {
var frame;
var source;
- // Get the debug command processor.
- var dcp = exec_state.debugCommandProcessor();
+ var dcp;
+ // New copy of debug command processor paused state.
+ dcp = exec_state.debugCommandProcessor(false);
// Get the backtrace.
var json;
@@ -114,6 +120,7 @@ function listener(event, exec_state, event_data, data) {
assertEquals("g", response.lookup(frames[2].func.ref).name);
assertEquals(3, frames[3].index);
assertEquals("", response.lookup(frames[3].func.ref).name);
+ assertFalse(response.running(), "expected not running");
// Get backtrace with two frames.
json = '{"seq":0,"type":"request","command":"backtrace","arguments":{"fromFrame":1,"toFrame":3}}'
@@ -234,6 +241,17 @@ function listener(event, exec_state, event_data, data) {
source = response.body();
assertEquals(Debug.findScript(f).source, source.source);
+ // New copy of debug command processor in running state.
+ dcp = exec_state.debugCommandProcessor(true);
+ // Get the backtrace.
+ json = '{"seq":0,"type":"request","command":"backtrace"}'
+ resp = dcp.processDebugJSONRequest(json);
+ response = new ParsedResponse(resp);
+ // It might be argueable, but we expect response to have body when
+ // not suspended
+ assertTrue(!!response.body(), "response should be null");
+ assertTrue(response.running(), "expected running");
+
listenerCalled = true;
}
} catch (e) {
diff --git a/deps/v8/test/mjsunit/debug-changebreakpoint.js b/deps/v8/test/mjsunit/debug-changebreakpoint.js
index 477c90813..936523a0e 100644
--- a/deps/v8/test/mjsunit/debug-changebreakpoint.js
+++ b/deps/v8/test/mjsunit/debug-changebreakpoint.js
@@ -59,7 +59,7 @@ function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Break) {
// Get the debug command processor.
- var dcp = exec_state.debugCommandProcessor();
+ var dcp = exec_state.debugCommandProcessor("unspecified_running_state");
// Test some illegal clearbreakpoint requests.
var request = '{' + base_request + '}'
diff --git a/deps/v8/test/mjsunit/debug-clearbreakpoint.js b/deps/v8/test/mjsunit/debug-clearbreakpoint.js
index 28920c53a..59479f2da 100644
--- a/deps/v8/test/mjsunit/debug-clearbreakpoint.js
+++ b/deps/v8/test/mjsunit/debug-clearbreakpoint.js
@@ -59,7 +59,7 @@ function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Break) {
// Get the debug command processor.
- var dcp = exec_state.debugCommandProcessor();
+ var dcp = exec_state.debugCommandProcessor("unspecified_running_state");
// Test some illegal clearbreakpoint requests.
var request = '{' + base_request + '}'
diff --git a/deps/v8/test/mjsunit/debug-clearbreakpointgroup.js b/deps/v8/test/mjsunit/debug-clearbreakpointgroup.js
index eca937875..aad6c3aff 100644
--- a/deps/v8/test/mjsunit/debug-clearbreakpointgroup.js
+++ b/deps/v8/test/mjsunit/debug-clearbreakpointgroup.js
@@ -60,7 +60,7 @@ function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Break) {
// Get the debug command processor.
- var dcp = exec_state.debugCommandProcessor();
+ var dcp = exec_state.debugCommandProcessor("unspecified_running_state");
// Clear breakpoint group 1.
testArguments(dcp, '{"groupId":1}', true);
diff --git a/deps/v8/test/mjsunit/debug-continue.js b/deps/v8/test/mjsunit/debug-continue.js
index 0c11abc56..a501aa925 100644
--- a/deps/v8/test/mjsunit/debug-continue.js
+++ b/deps/v8/test/mjsunit/debug-continue.js
@@ -44,7 +44,10 @@ function safeEval(code) {
}
}
-function testArguments(dcp, arguments, success) {
+function testArguments(exec_state, arguments, success) {
+ // Get the debug command processor in paused state.
+ var dcp = exec_state.debugCommandProcessor(false);
+
// Generate request with the supplied arguments
var request;
if (arguments) {
@@ -65,25 +68,23 @@ function testArguments(dcp, arguments, success) {
function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Break) {
- // Get the debug command processor.
- var dcp = exec_state.debugCommandProcessor();
// Test simple continue request.
- testArguments(dcp, void 0, true);
+ testArguments(exec_state, void 0, true);
// Test some illegal continue requests.
- testArguments(dcp, '{"stepaction":"maybe"}', false);
- testArguments(dcp, '{"stepcount":-1}', false);
+ testArguments(exec_state, '{"stepaction":"maybe"}', false);
+ testArguments(exec_state, '{"stepcount":-1}', false);
// Test some legal continue requests.
- testArguments(dcp, '{"stepaction":"in"}', true);
- testArguments(dcp, '{"stepaction":"min"}', true);
- testArguments(dcp, '{"stepaction":"next"}', true);
- testArguments(dcp, '{"stepaction":"out"}', true);
- testArguments(dcp, '{"stepcount":1}', true);
- testArguments(dcp, '{"stepcount":10}', true);
- testArguments(dcp, '{"stepcount":"10"}', true);
- testArguments(dcp, '{"stepaction":"next","stepcount":10}', true);
+ testArguments(exec_state, '{"stepaction":"in"}', true);
+ testArguments(exec_state, '{"stepaction":"min"}', true);
+ testArguments(exec_state, '{"stepaction":"next"}', true);
+ testArguments(exec_state, '{"stepaction":"out"}', true);
+ testArguments(exec_state, '{"stepcount":1}', true);
+ testArguments(exec_state, '{"stepcount":10}', true);
+ testArguments(exec_state, '{"stepcount":"10"}', true);
+ testArguments(exec_state, '{"stepaction":"next","stepcount":10}', true);
// Indicate that all was processed.
listenerComplete = true;
@@ -108,6 +109,6 @@ function g() {
Debug.setBreakPoint(g, 0, 0);
g();
+assertFalse(exception, "exception in listener")
// Make sure that the debug event listener vas invoked.
assertTrue(listenerComplete, "listener did not run to completion");
-assertFalse(exception, "exception in listener")
diff --git a/deps/v8/test/mjsunit/debug-evaluate-bool-constructor.js b/deps/v8/test/mjsunit/debug-evaluate-bool-constructor.js
new file mode 100644
index 000000000..809a5ccc6
--- /dev/null
+++ b/deps/v8/test/mjsunit/debug-evaluate-bool-constructor.js
@@ -0,0 +1,80 @@
+// 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.
+
+// Flags: --expose-debug-as debug
+// Get the Debug object exposed from the debug context global object.
+Debug = debug.Debug
+
+var listenerComplete = false;
+var exception = false;
+
+function listener(event, exec_state, event_data, data) {
+ try {
+ if (event == Debug.DebugEvent.Break) {
+ // Get the debug command processor.
+ var dcp = exec_state.debugCommandProcessor();
+
+ var request = {
+ seq: 0,
+ type: 'request',
+ command: 'evaluate',
+ arguments: {
+ expression: 'a',
+ frame: 0
+ }
+ };
+ request = JSON.stringify(request);
+
+ var resp = dcp.processDebugJSONRequest(request);
+ var response = JSON.parse(resp);
+ assertTrue(response.success, 'Command failed: ' + resp);
+ assertEquals('object', response.body.type);
+ assertEquals('Object', response.body.className);
+
+ // Indicate that all was processed.
+ listenerComplete = true;
+ }
+ } catch (e) {
+ exception = e
+ };
+};
+
+// Add the debug event listener.
+Debug.setListener(listener);
+
+function callDebugger() {
+ // Add set constructor field to a non-function value.
+ var a = {constructor:true};
+ debugger;
+}
+
+callDebugger();
+
+
+// Make sure that the debug event listener vas invoked.
+assertFalse(exception, "exception in listener")
+assertTrue(listenerComplete, "listener did not run to completion");
diff --git a/deps/v8/test/mjsunit/debug-evaluate-recursive.js b/deps/v8/test/mjsunit/debug-evaluate-recursive.js
index 9f037e578..6ee391b63 100644
--- a/deps/v8/test/mjsunit/debug-evaluate-recursive.js
+++ b/deps/v8/test/mjsunit/debug-evaluate-recursive.js
@@ -44,7 +44,10 @@ function safeEval(code) {
}
}
-function testRequest(dcp, arguments, success, result) {
+function testRequest(exec_state, arguments, success, result) {
+ // Get the debug command processor in paused state.
+ var dcp = exec_state.debugCommandProcessor(false);
+
// Generate request with the supplied arguments.
var request;
if (arguments) {
@@ -74,23 +77,20 @@ function listener(event, exec_state, event_data, data) {
assertEquals(1, exec_state.frame(0).evaluate('f()', true).value());
assertEquals(2, exec_state.frame(0).evaluate('g()', true).value());
- // Get the debug command processor.
- var dcp = exec_state.debugCommandProcessor();
-
// Call functions with break using the JSON protocol. Tests that argument
// disable_break is default true.
- testRequest(dcp, '{"expression":"f()"}', true, 1);
- testRequest(dcp, '{"expression":"f()","frame":0}', true, 1);
- testRequest(dcp, '{"expression":"g()"}', true, 2);
- testRequest(dcp, '{"expression":"g()","frame":0}', true, 2);
+ testRequest(exec_state, '{"expression":"f()"}', true, 1);
+ testRequest(exec_state, '{"expression":"f()","frame":0}', true, 1);
+ testRequest(exec_state, '{"expression":"g()"}', true, 2);
+ testRequest(exec_state, '{"expression":"g()","frame":0}', true, 2);
// Call functions with break using the JSON protocol. Tests passing
// argument disable_break is default true.
- testRequest(dcp, '{"expression":"f()","disable_break":true}', true, 1);
- testRequest(dcp, '{"expression":"f()","frame":0,"disable_break":true}',
+ testRequest(exec_state, '{"expression":"f()","disable_break":true}', true, 1);
+ testRequest(exec_state, '{"expression":"f()","frame":0,"disable_break":true}',
true, 1);
- testRequest(dcp, '{"expression":"g()","disable_break":true}', true, 2);
- testRequest(dcp, '{"expression":"g()","frame":0,"disable_break":true}',
+ testRequest(exec_state, '{"expression":"g()","disable_break":true}', true, 2);
+ testRequest(exec_state, '{"expression":"g()","frame":0,"disable_break":true}',
true, 2);
// Indicate that all was processed.
@@ -146,9 +146,9 @@ Debug.setBreakPoint(f, 2, 0);
// Cause a debug break event.
debugger;
+assertFalse(exception, "exception in listener")
// Make sure that the debug event listener vas invoked.
assertTrue(listenerComplete);
-assertFalse(exception, "exception in listener")
// Remove the debug event listener.
Debug.setListener(null);
@@ -161,7 +161,7 @@ Debug.setBreakPoint(f, 2, 0);
debugger;
+assertFalse(exception, "exception in listener")
// Make sure that the debug event listener vas invoked.
assertTrue(listenerComplete);
-assertFalse(exception, "exception in listener")
assertEquals(2, break_count);
diff --git a/deps/v8/test/mjsunit/debug-evaluate.js b/deps/v8/test/mjsunit/debug-evaluate.js
index 5c5734f87..c4779072a 100644
--- a/deps/v8/test/mjsunit/debug-evaluate.js
+++ b/deps/v8/test/mjsunit/debug-evaluate.js
@@ -59,14 +59,15 @@ function testRequest(dcp, arguments, success, result) {
} else {
assertFalse(response.success, request + ' -> ' + response.message);
}
- assertFalse(response.running, request + ' -> expected not running');
+ assertEquals(response.running, "unspecified_running_state",
+ request + ' -> expected not running');
}
function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Break) {
// Get the debug command processor.
- var dcp = exec_state.debugCommandProcessor();
+ var dcp = exec_state.debugCommandProcessor("unspecified_running_state");
// Test some illegal evaluate requests.
testRequest(dcp, void 0, false);
@@ -112,6 +113,6 @@ a = 1;
Debug.setBreakPoint(f, 2, 0);
g();
+assertFalse(exception, "exception in listener")
// Make sure that the debug event listener vas invoked.
assertTrue(listenerComplete, "listener did not run to completion");
-assertFalse(exception, "exception in listener")
diff --git a/deps/v8/test/mjsunit/debug-handle.js b/deps/v8/test/mjsunit/debug-handle.js
index c7ab76af4..98875ceb4 100644
--- a/deps/v8/test/mjsunit/debug-handle.js
+++ b/deps/v8/test/mjsunit/debug-handle.js
@@ -43,7 +43,10 @@ function safeEval(code) {
// Send an evaluation request and return the handle of the result.
-function evaluateRequest(dcp, arguments) {
+function evaluateRequest(exec_state, arguments) {
+ // Get the debug command processor.
+ var dcp = exec_state.debugCommandProcessor("unspecified_running_state");
+
// The base part of all evaluate requests.
var base_request = '"seq":0,"type":"request","command":"evaluate"'
@@ -63,7 +66,10 @@ function evaluateRequest(dcp, arguments) {
// Send a lookup request and return the evaluated JSON response.
-function lookupRequest(dcp, arguments, success) {
+function lookupRequest(exec_state, arguments, success) {
+ // Get the debug command processor.
+ var dcp = exec_state.debugCommandProcessor("unspecified_running_state");
+
// The base part of all lookup requests.
var base_request = '"seq":0,"type":"request","command":"lookup"'
@@ -81,7 +87,7 @@ function lookupRequest(dcp, arguments, success) {
} else {
assertFalse(response.success, request + ' -> ' + response.message);
}
- assertFalse(response.running, request + ' -> expected not running');
+ assertEquals(response.running, dcp.isRunning(), request + ' -> expected not running');
return response;
}
@@ -90,26 +96,23 @@ function lookupRequest(dcp, arguments, success) {
function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Break) {
- // Get the debug command processor.
- var dcp = exec_state.debugCommandProcessor();
-
// Test some illegal lookup requests.
- lookupRequest(dcp, void 0, false);
- lookupRequest(dcp, '{"handles":["a"]}', false);
- lookupRequest(dcp, '{"handles":[-1]}', false);
+ lookupRequest(exec_state, void 0, false);
+ lookupRequest(exec_state, '{"handles":["a"]}', false);
+ lookupRequest(exec_state, '{"handles":[-1]}', false);
// Evaluate and get some handles.
- var handle_o = evaluateRequest(dcp, '{"expression":"o"}');
- var handle_p = evaluateRequest(dcp, '{"expression":"p"}');
- var handle_b = evaluateRequest(dcp, '{"expression":"a"}');
- var handle_a = evaluateRequest(dcp, '{"expression":"b","frame":1}');
+ var handle_o = evaluateRequest(exec_state, '{"expression":"o"}');
+ var handle_p = evaluateRequest(exec_state, '{"expression":"p"}');
+ var handle_b = evaluateRequest(exec_state, '{"expression":"a"}');
+ var handle_a = evaluateRequest(exec_state, '{"expression":"b","frame":1}');
assertEquals(handle_o, handle_a);
assertEquals(handle_a, handle_b);
assertFalse(handle_o == handle_p, "o and p have he same handle");
var response;
var count;
- response = lookupRequest(dcp, '{"handles":[' + handle_o + ']}', true);
+ response = lookupRequest(exec_state, '{"handles":[' + handle_o + ']}', true);
var obj = response.body[handle_o];
assertTrue(!!obj, 'Object not found: ' + handle_o);
assertEquals(handle_o, obj.handle);
@@ -127,20 +130,20 @@ function listener(event, exec_state, event_data, data) {
}
}
assertEquals(2, count, 'Either "o" or "p" not found');
- response = lookupRequest(dcp, '{"handles":[' + handle_p + ']}', true);
+ response = lookupRequest(exec_state, '{"handles":[' + handle_p + ']}', true);
obj = response.body[handle_p];
assertTrue(!!obj, 'Object not found: ' + handle_p);
assertEquals(handle_p, obj.handle);
// Check handles for functions on the stack.
- var handle_f = evaluateRequest(dcp, '{"expression":"f"}');
- var handle_g = evaluateRequest(dcp, '{"expression":"g"}');
- var handle_caller = evaluateRequest(dcp, '{"expression":"f.caller"}');
+ var handle_f = evaluateRequest(exec_state, '{"expression":"f"}');
+ var handle_g = evaluateRequest(exec_state, '{"expression":"g"}');
+ var handle_caller = evaluateRequest(exec_state, '{"expression":"f.caller"}');
assertFalse(handle_f == handle_g, "f and g have he same handle");
assertEquals(handle_g, handle_caller, "caller for f should be g");
- response = lookupRequest(dcp, '{"handles":[' + handle_f + ']}', true);
+ response = lookupRequest(exec_state, '{"handles":[' + handle_f + ']}', true);
obj = response.body[handle_f];
assertEquals(handle_f, obj.handle);
@@ -151,14 +154,14 @@ function listener(event, exec_state, event_data, data) {
switch (obj.properties[i].name) {
case 'name':
var response_name;
- response_name = lookupRequest(dcp, arguments, true);
+ response_name = lookupRequest(exec_state, arguments, true);
assertEquals('string', response_name.body[ref].type);
assertEquals("f", response_name.body[ref].value);
count++;
break;
case 'length':
var response_length;
- response_length = lookupRequest(dcp, arguments, true);
+ response_length = lookupRequest(exec_state, arguments, true);
assertEquals('number', response_length.body[ref].type);
assertEquals(1, response_length.body[ref].value);
count++;
@@ -179,7 +182,7 @@ function listener(event, exec_state, event_data, data) {
}
var arguments = '{"handles":[' + refs.join(',') + ']}';
- response = lookupRequest(dcp, arguments, true);
+ response = lookupRequest(exec_state, arguments, true);
count = 0;
for (i in obj.properties) {
var ref = obj.properties[i].ref;
@@ -244,6 +247,6 @@ p.o = o;
p.p = p;
g(o);
+assertFalse(exception, "exception in listener")
// Make sure that the debug event listener vas invoked.
assertTrue(listenerComplete, "listener did not run to completion: " + exception);
-assertFalse(exception, "exception in listener")
diff --git a/deps/v8/test/mjsunit/debug-mirror-cache.js b/deps/v8/test/mjsunit/debug-mirror-cache.js
index d15146fae..5b85306a1 100644
--- a/deps/v8/test/mjsunit/debug-mirror-cache.js
+++ b/deps/v8/test/mjsunit/debug-mirror-cache.js
@@ -41,7 +41,7 @@ function g() {
Debug = debug.Debug
listenerCallCount = 0;
-listenerExceptionCount = 0;
+listenerExceptions = [];
function listener(event, exec_state, event_data, data) {
@@ -54,8 +54,8 @@ function listener(event, exec_state, event_data, data) {
assertEquals(0, debug.next_handle_, "Mirror cache not cleared");
assertEquals(0, debug.mirror_cache_.length, "Mirror cache not cleared");
- // Get the debug command processor.
- var dcp = exec_state.debugCommandProcessor();
+ // Get the debug command processor in paused state.
+ var dcp = exec_state.debugCommandProcessor(false);
// Make a backtrace request to create some mirrors.
var json;
@@ -68,7 +68,7 @@ function listener(event, exec_state, event_data, data) {
}
} catch (e) {
print(e);
- listenerExceptionCount++;
+ listenerExceptions.push(e);
};
};
@@ -79,7 +79,7 @@ Debug.setListener(listener);
debugger;
debugger;
+assertEquals([], listenerExceptions, "Exception in listener");
// Make sure that the debug event listener vas invoked.
assertEquals(2, listenerCallCount, "Listener not called");
-assertEquals(0, listenerExceptionCount, "Exception in listener");
diff --git a/deps/v8/test/mjsunit/debug-references.js b/deps/v8/test/mjsunit/debug-references.js
index 1fde1ac74..452761cf1 100644
--- a/deps/v8/test/mjsunit/debug-references.js
+++ b/deps/v8/test/mjsunit/debug-references.js
@@ -66,14 +66,14 @@ function testRequest(dcp, arguments, success, count) {
} else {
assertFalse(response.success, request + ' -> ' + response.message);
}
- assertFalse(response.running, request + ' -> expected not running');
+ assertEquals(response.running, dcp.isRunning(), request + ' -> expected not running');
}
function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Break) {
// Get the debug command processor.
- var dcp = exec_state.debugCommandProcessor();
+ var dcp = exec_state.debugCommandProcessor("unspecified_running_state");
// Test some illegal references requests.
testRequest(dcp, void 0, false);
diff --git a/deps/v8/test/mjsunit/debug-scopes.js b/deps/v8/test/mjsunit/debug-scopes.js
index e87cbb7c2..af29df98c 100644
--- a/deps/v8/test/mjsunit/debug-scopes.js
+++ b/deps/v8/test/mjsunit/debug-scopes.js
@@ -92,7 +92,7 @@ function CheckScopeChain(scopes, exec_state) {
}
// Get the debug command processor.
- var dcp = exec_state.debugCommandProcessor();
+ var dcp = exec_state.debugCommandProcessor("unspecified_running_state");
// Send a scopes request and check the result.
var json;
@@ -155,7 +155,7 @@ function CheckScopeContent(content, number, exec_state) {
assertEquals(count, scope_size);
// Get the debug command processor.
- var dcp = exec_state.debugCommandProcessor();
+ var dcp = exec_state.debugCommandProcessor("unspecified_running_state");
// Send a scope request for information on a single scope and check the
// result.
diff --git a/deps/v8/test/mjsunit/debug-scripts-request.js b/deps/v8/test/mjsunit/debug-scripts-request.js
index 80b3bce59..41bff0e80 100644
--- a/deps/v8/test/mjsunit/debug-scripts-request.js
+++ b/deps/v8/test/mjsunit/debug-scripts-request.js
@@ -60,7 +60,7 @@ function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Break) {
// Get the debug command processor.
- var dcp = exec_state.debugCommandProcessor();
+ var dcp = exec_state.debugCommandProcessor("unspecified_running_state");
// Test illegal scripts requests.
testArguments(dcp, '{"types":"xx"}', false);
diff --git a/deps/v8/test/mjsunit/debug-setbreakpoint.js b/deps/v8/test/mjsunit/debug-setbreakpoint.js
index f8d9b157b..08492b4ec 100644
--- a/deps/v8/test/mjsunit/debug-setbreakpoint.js
+++ b/deps/v8/test/mjsunit/debug-setbreakpoint.js
@@ -69,7 +69,7 @@ function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Break) {
// Get the debug command processor.
- var dcp = exec_state.debugCommandProcessor();
+ var dcp = exec_state.debugCommandProcessor("unspecified_running_state");
// Test some illegal setbreakpoint requests.
var request = '{' + base_request + '}'
diff --git a/deps/v8/test/mjsunit/debug-suspend.js b/deps/v8/test/mjsunit/debug-suspend.js
new file mode 100644
index 000000000..73a2e8c24
--- /dev/null
+++ b/deps/v8/test/mjsunit/debug-suspend.js
@@ -0,0 +1,96 @@
+// 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.
+
+// Flags: --expose-debug-as debug
+// Get the Debug object exposed from the debug context global object.
+Debug = debug.Debug
+
+// Simple function which stores the last debug event.
+listenerComplete = false;
+exception = false;
+
+var base_backtrace_request = '"seq":0,"type":"request","command":"backtrace"'
+var base_suspend_request = '"seq":0,"type":"request","command":"suspend"'
+
+function safeEval(code) {
+ try {
+ return eval('(' + code + ')');
+ } catch (e) {
+ assertEquals(void 0, e);
+ return undefined;
+ }
+}
+
+function testArguments(exec_state) {
+ // Get the debug command processor in running state.
+ var dcp = exec_state.debugCommandProcessor(true);
+
+ assertTrue(dcp.isRunning());
+
+ var backtrace_request = '{' + base_backtrace_request + '}'
+ var backtrace_response = safeEval(dcp.processDebugJSONRequest(backtrace_request));
+
+ assertTrue(backtrace_response.success);
+
+ assertTrue(backtrace_response.running, backtrace_request + ' -> expected running');
+
+ assertTrue(dcp.isRunning());
+
+ var suspend_request = '{' + base_suspend_request + '}'
+ var suspend_response = safeEval(dcp.processDebugJSONRequest(suspend_request));
+
+ assertTrue(suspend_response.success);
+
+ assertFalse(suspend_response.running, suspend_request + ' -> expected not running');
+
+ assertFalse(dcp.isRunning());
+}
+
+function listener(event, exec_state, event_data, data) {
+ try {
+ if (event == Debug.DebugEvent.Break) {
+
+ // Test simple suspend request.
+ testArguments(exec_state);
+
+ // Indicate that all was processed.
+ listenerComplete = true;
+ }
+ } catch (e) {
+ exception = e
+ };
+};
+
+// Add the debug event listener.
+Debug.setListener(listener);
+
+// Stop debugger and check that suspend command changes running flag.
+debugger;
+
+assertFalse(exception, "exception in listener")
+// Make sure that the debug event listener vas invoked.
+assertTrue(listenerComplete, "listener did not run to completion");
diff --git a/deps/v8/test/mjsunit/for-in.js b/deps/v8/test/mjsunit/for-in.js
index dfe721dbe..e3436ff3b 100644
--- a/deps/v8/test/mjsunit/for-in.js
+++ b/deps/v8/test/mjsunit/for-in.js
@@ -31,21 +31,21 @@ function props(x) {
return array.sort();
}
-assertEquals(0, props({}).length);
-assertEquals(1, props({x:1}).length);
-assertEquals(2, props({x:1, y:2}).length);
+assertEquals(0, props({}).length, "olen0");
+assertEquals(1, props({x:1}).length, "olen1");
+assertEquals(2, props({x:1, y:2}).length, "olen2");
-assertArrayEquals(["x"], props({x:1}));
-assertArrayEquals(["x", "y"], props({x:1, y:2}));
-assertArrayEquals(["x", "y", "zoom"], props({x:1, y:2, zoom:3}));
+assertArrayEquals(["x"], props({x:1}), "x");
+assertArrayEquals(["x", "y"], props({x:1, y:2}), "xy");
+assertArrayEquals(["x", "y", "zoom"], props({x:1, y:2, zoom:3}), "xyzoom");
-assertEquals(0, props([]).length);
-assertEquals(1, props([1]).length);
-assertEquals(2, props([1,2]).length);
+assertEquals(0, props([]).length, "alen0");
+assertEquals(1, props([1]).length, "alen1");
+assertEquals(2, props([1,2]).length, "alen2");
-assertArrayEquals(["0"], props([1]));
-assertArrayEquals(["0", "1"], props([1,2]));
-assertArrayEquals(["0", "1", "2"], props([1,2,3]));
+assertArrayEquals(["0"], props([1]), "0");
+assertArrayEquals(["0", "1"], props([1,2]), "01");
+assertArrayEquals(["0", "1", "2"], props([1,2,3]), "012");
var o = {};
var a = [];
@@ -54,33 +54,33 @@ for (var i = 0x0020; i < 0x01ff; i+=2) {
a.push(s);
o[s] = i;
}
-assertArrayEquals(a, props(o));
+assertArrayEquals(a, props(o), "charcodes");
var a = [];
-assertEquals(0, props(a).length);
+assertEquals(0, props(a).length, "proplen0");
a[Math.pow(2,30)-1] = 0;
-assertEquals(1, props(a).length);
+assertEquals(1, props(a).length, "proplen1");
a[Math.pow(2,31)-1] = 0;
-assertEquals(2, props(a).length);
+assertEquals(2, props(a).length, "proplen2");
a[1] = 0;
-assertEquals(3, props(a).length);
+assertEquals(3, props(a).length, "proplen3");
for (var hest = 'hest' in {}) { }
-assertEquals('hest', hest);
+assertEquals('hest', hest, "empty-no-override");
var result = '';
for (var p in {a : [0], b : 1}) { result += p; }
-assertEquals('ab', result);
+assertEquals('ab', result, "ab");
var result = '';
for (var p in {a : {v:1}, b : 1}) { result += p; }
-assertEquals('ab', result);
+assertEquals('ab', result, "ab-nodeep");
var result = '';
for (var p in { get a() {}, b : 1}) { result += p; }
-assertEquals('ab', result);
+assertEquals('ab', result, "abget");
var result = '';
for (var p in { get a() {}, set a(x) {}, b : 1}) { result += p; }
-assertEquals('ab', result);
+assertEquals('ab', result, "abgetset");
diff --git a/deps/v8/test/mjsunit/regress/regress-1081309.js b/deps/v8/test/mjsunit/regress/regress-1081309.js
index a771ac044..009ede151 100644
--- a/deps/v8/test/mjsunit/regress/regress-1081309.js
+++ b/deps/v8/test/mjsunit/regress/regress-1081309.js
@@ -69,7 +69,7 @@ function listener(event, exec_state, event_data, data) {
// 0: [anonymous]
// Get the debug command processor.
- var dcp = exec_state.debugCommandProcessor();
+ var dcp = exec_state.debugCommandProcessor(false);
// Get the backtrace.
var json;
@@ -105,6 +105,6 @@ try {
// Ignore the exception "Cannot call method 'x' of undefined"
}
+assertFalse(exception, "exception in listener", exception)
// Make sure that the debug event listener vas invoked.
assertTrue(listenerCalled, "listener not called");
-assertFalse(exception, "exception in listener", exception)
diff --git a/deps/v8/test/mjsunit/regress/regress-1199401.js b/deps/v8/test/mjsunit/regress/regress-1199401.js
index 792faea98..cc7985d82 100644
--- a/deps/v8/test/mjsunit/regress/regress-1199401.js
+++ b/deps/v8/test/mjsunit/regress/regress-1199401.js
@@ -27,35 +27,49 @@
// Ensure that we can correctly change the sign of the most negative smi.
-assertEquals(1073741824, -1073741824 * -1);
-assertEquals(1073741824, -1073741824 / -1);
-assertEquals(1073741824, -(-1073741824));
-assertEquals(1073741824, 0 - (-1073741824));
-
-var min_smi = -1073741824;
-
-assertEquals(1073741824, min_smi * -1);
-assertEquals(1073741824, min_smi / -1);
-assertEquals(1073741824, -min_smi);
-assertEquals(1073741824, 0 - min_smi);
-
-var zero = 0;
-var minus_one = -1;
-
-assertEquals(1073741824, min_smi * minus_one);
-assertEquals(1073741824, min_smi / minus_one);
-assertEquals(1073741824, -min_smi);
-assertEquals(1073741824, zero - min_smi);
-
-assertEquals(1073741824, -1073741824 * minus_one);
-assertEquals(1073741824, -1073741824 / minus_one);
-assertEquals(1073741824, -(-1073741824));
-assertEquals(1073741824, zero - (-1073741824));
-
-var half_min_smi = -(1<<15);
-var half_max_smi = (1<<15);
-
-assertEquals(1073741824, -half_min_smi * half_max_smi);
-assertEquals(1073741824, half_min_smi * -half_max_smi);
-assertEquals(1073741824, half_max_smi * -half_min_smi);
-assertEquals(1073741824, -half_max_smi * half_min_smi);
+// Possible Smi ranges.
+var ranges = [{min: -1073741824, max: 1073741823, bits: 31},
+ {min: -2147483648, max: 2147483647, bits: 32}];
+
+for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i];
+ var min_smi = range.min;
+ var max_smi = range.max;
+ var bits = range.bits;
+ var name = bits + "-bit";
+
+ var result = max_smi + 1;
+
+ // Min smi as literal
+ assertEquals(result, eval(min_smi + " * -1"), name + "-litconmult");
+ assertEquals(result, eval(min_smi + " / -1"), name + "-litcondiv");
+ assertEquals(result, eval("-(" + min_smi + ")"), name + "-litneg");
+ assertEquals(result, eval("0 - (" + min_smi + ")")), name + "-conlitsub";
+
+ // As variable:
+ assertEquals(result, min_smi * -1, name + "-varconmult");
+ assertEquals(result, min_smi / -1, name + "-varcondiv");
+ assertEquals(result, -min_smi, name + "-varneg");
+ assertEquals(result, 0 - min_smi, name + "-convarsub");
+
+ // Only variables:
+ var zero = 0;
+ var minus_one = -1;
+
+ assertEquals(result, min_smi * minus_one, name + "-varvarmult");
+ assertEquals(result, min_smi / minus_one, name + "-varvardiv");
+ assertEquals(result, zero - min_smi, name + "-varvarsub");
+
+ // Constants as variables
+ assertEquals(result, eval(min_smi + " * minus_one"), name + "-litvarmult");
+ assertEquals(result, eval(min_smi + " / minus_one"), name + "-litvarmdiv");
+ assertEquals(result, eval("0 - (" + min_smi + ")"), name + "-varlitsub");
+
+ var half_min_smi = -(1 << (bits >> 1));
+ var half_max_smi = 1 << ((bits - 1) >> 1);
+
+ assertEquals(max_smi + 1, -half_min_smi * half_max_smi, name + "-half1");
+ assertEquals(max_smi + 1, half_min_smi * -half_max_smi, name + "-half2");
+ assertEquals(max_smi + 1, half_max_smi * -half_min_smi, name + "-half3");
+ assertEquals(max_smi + 1, -half_max_smi * half_min_smi, name + "-half4");
+}
diff --git a/deps/v8/test/mjsunit/smi-negative-zero.js b/deps/v8/test/mjsunit/smi-negative-zero.js
index 719ee49f0..690644317 100644
--- a/deps/v8/test/mjsunit/smi-negative-zero.js
+++ b/deps/v8/test/mjsunit/smi-negative-zero.js
@@ -37,64 +37,64 @@ var minus_four = -4;
// variable op variable
-assertEquals(one / (-zero), -Infinity, "one / -0 I");
+assertEquals(-Infinity, one / (-zero), "one / -0 I");
-assertEquals(one / (zero * minus_one), -Infinity, "one / -1");
-assertEquals(one / (minus_one * zero), -Infinity, "one / -0 II");
-assertEquals(one / (zero * zero), Infinity, "one / 0 I");
-assertEquals(one / (minus_one * minus_one), 1, "one / 1");
+assertEquals(-Infinity, one / (zero * minus_one), "one / -1");
+assertEquals(-Infinity, one / (minus_one * zero), "one / -0 II");
+assertEquals(Infinity, one / (zero * zero), "one / 0 I");
+assertEquals(1, one / (minus_one * minus_one), "one / 1");
-assertEquals(one / (zero / minus_one), -Infinity, "one / -0 III");
-assertEquals(one / (zero / one), Infinity, "one / 0 II");
+assertEquals(-Infinity, one / (zero / minus_one), "one / -0 III");
+assertEquals(Infinity, one / (zero / one), "one / 0 II");
-assertEquals(one / (minus_four % two), -Infinity, "foo1");
-assertEquals(one / (minus_four % minus_two), -Infinity, "foo2");
-assertEquals(one / (four % two), Infinity, "foo3");
-assertEquals(one / (four % minus_two), Infinity, "foo4");
+assertEquals(-Infinity, one / (minus_four % two), "foo1");
+assertEquals(-Infinity, one / (minus_four % minus_two), "foo2");
+assertEquals(Infinity, one / (four % two), "foo3");
+assertEquals(Infinity, one / (four % minus_two), "foo4");
// literal op variable
-assertEquals(one / (0 * minus_one), -Infinity, "bar1");
-assertEquals(one / (-1 * zero), -Infinity, "bar2");
-assertEquals(one / (0 * zero), Infinity, "bar3");
-assertEquals(one / (-1 * minus_one), 1, "bar4");
+assertEquals(-Infinity, one / (0 * minus_one), "bar1");
+assertEquals(-Infinity, one / (-1 * zero), "bar2");
+assertEquals(Infinity, one / (0 * zero), "bar3");
+assertEquals(1, one / (-1 * minus_one), "bar4");
-assertEquals(one / (0 / minus_one), -Infinity, "baz1");
-assertEquals(one / (0 / one), Infinity, "baz2");
+assertEquals(-Infinity, one / (0 / minus_one), "baz1");
+assertEquals(Infinity, one / (0 / one), "baz2");
-assertEquals(one / (-4 % two), -Infinity, "baz3");
-assertEquals(one / (-4 % minus_two), -Infinity, "baz4");
-assertEquals(one / (4 % two), Infinity, "baz5");
-assertEquals(one / (4 % minus_two), Infinity, "baz6");
+assertEquals(-Infinity, one / (-4 % two), "baz3");
+assertEquals(-Infinity, one / (-4 % minus_two), "baz4");
+assertEquals(Infinity, one / (4 % two), "baz5");
+assertEquals(Infinity, one / (4 % minus_two), "baz6");
// variable op literal
-assertEquals(one / (zero * -1), -Infinity, "fizz1");
-assertEquals(one / (minus_one * 0), -Infinity, "fizz2");
-assertEquals(one / (zero * 0), Infinity, "fizz3");
-assertEquals(one / (minus_one * -1), 1, "fizz4");
+assertEquals(-Infinity, one / (zero * -1), "fizz1");
+assertEquals(-Infinity, one / (minus_one * 0), "fizz2");
+assertEquals(Infinity, one / (zero * 0), "fizz3");
+assertEquals(1, one / (minus_one * -1), "fizz4");
-assertEquals(one / (zero / -1), -Infinity, "buzz1");
-assertEquals(one / (zero / 1), Infinity, "buzz2");
+assertEquals(-Infinity, one / (zero / -1), "buzz1");
+assertEquals(Infinity, one / (zero / 1), "buzz2");
-assertEquals(one / (minus_four % 2), -Infinity, "buzz3");
-assertEquals(one / (minus_four % -2), -Infinity, "buzz4");
-assertEquals(one / (four % 2), Infinity, "buzz5");
-assertEquals(one / (four % -2), Infinity, "buzz6");
+assertEquals(-Infinity, one / (minus_four % 2), "buzz3");
+assertEquals(-Infinity, one / (minus_four % -2), "buzz4");
+assertEquals(Infinity, one / (four % 2), "buzz5");
+assertEquals(Infinity, one / (four % -2), "buzz6");
// literal op literal
-assertEquals(one / (-0), -Infinity, "fisk1");
+assertEquals(-Infinity, one / (-0), "fisk1");
-assertEquals(one / (0 * -1), -Infinity, "fisk2");
-assertEquals(one / (-1 * 0), -Infinity, "fisk3");
-assertEquals(one / (0 * 0), Infinity, "fisk4");
-assertEquals(one / (-1 * -1), 1, "fisk5");
+assertEquals(-Infinity, one / (0 * -1), "fisk2");
+assertEquals(-Infinity, one / (-1 * 0), "fisk3");
+assertEquals(Infinity, one / (0 * 0), "fisk4");
+assertEquals(1, one / (-1 * -1), "fisk5");
-assertEquals(one / (0 / -1), -Infinity, "hest1");
-assertEquals(one / (0 / 1), Infinity, "hest2");
+assertEquals(-Infinity, one / (0 / -1), "hest1");
+assertEquals(Infinity, one / (0 / 1), "hest2");
-assertEquals(one / (-4 % 2), -Infinity, "fiskhest1");
-assertEquals(one / (-4 % -2), -Infinity, "fiskhest2");
-assertEquals(one / (4 % 2), Infinity, "fiskhest3");
-assertEquals(one / (4 % -2), Infinity, "fiskhest4");
+assertEquals(-Infinity, one / (-4 % 2), "fiskhest1");
+assertEquals(-Infinity, one / (-4 % -2), "fiskhest2");
+assertEquals(Infinity, one / (4 % 2), "fiskhest3");
+assertEquals(Infinity, one / (4 % -2), "fiskhest4");
diff --git a/deps/v8/test/mjsunit/testcfg.py b/deps/v8/test/mjsunit/testcfg.py
index e3f3fcd94..49064b12f 100644
--- a/deps/v8/test/mjsunit/testcfg.py
+++ b/deps/v8/test/mjsunit/testcfg.py
@@ -114,7 +114,8 @@ class MjsunitTestConfiguration(test.TestConfiguration):
bugs = [current_path + ['bugs', t] for t in self.Ls(join(self.root, 'bugs'))]
third_party = [current_path + ['third_party', t] for t in self.Ls(join(self.root, 'third_party'))]
tools = [current_path + ['tools', t] for t in self.Ls(join(self.root, 'tools'))]
- all_tests = mjsunit + regress + bugs + third_party + tools
+ compiler = [current_path + ['compiler', t] for t in self.Ls(join(self.root, 'compiler'))]
+ all_tests = mjsunit + regress + bugs + third_party + tools + compiler
result = []
for test in all_tests:
if self.Contains(path, test):
diff --git a/deps/v8/test/mjsunit/third_party/array-isarray.js b/deps/v8/test/mjsunit/third_party/array-isarray.js
new file mode 100644
index 000000000..0fc42a3f2
--- /dev/null
+++ b/deps/v8/test/mjsunit/third_party/array-isarray.js
@@ -0,0 +1,48 @@
+// Copyright (c) 2009 Apple Computer, Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// 2. 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.
+//
+// 3. Neither the name of the copyright holder(s) nor the names of any
+// 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.
+
+// Based on LayoutTests/fast/js/resources/Array-isArray.js
+
+assertTrue(Array.isArray([]));
+assertTrue(Array.isArray(new Array));
+assertTrue(Array.isArray(Array()));
+assertTrue(Array.isArray('abc'.match(/(a)*/g)));
+assertFalse((function(){ return Array.isArray(arguments); })());
+assertFalse(Array.isArray());
+assertFalse(Array.isArray(null));
+assertFalse(Array.isArray(undefined));
+assertFalse(Array.isArray(true));
+assertFalse(Array.isArray(false));
+assertFalse(Array.isArray('a string'));
+assertFalse(Array.isArray({}));
+assertFalse(Array.isArray({length: 5}));
+assertFalse(Array.isArray({__proto__: Array.prototype, length:1, 0:1, 1:2}));
+
diff --git a/deps/v8/test/mjsunit/third_party/string-trim.js b/deps/v8/test/mjsunit/third_party/string-trim.js
new file mode 100644
index 000000000..234dff6dc
--- /dev/null
+++ b/deps/v8/test/mjsunit/third_party/string-trim.js
@@ -0,0 +1,107 @@
+// Copyright (c) 2009 Apple Computer, Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// 2. 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.
+//
+// 3. Neither the name of the copyright holder(s) nor the names of any
+// 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.
+
+// Based on LayoutTests/fast/js/script-tests/string-trim.js
+
+// References to trim(), trimLeft() and trimRight() functions for
+// testing Function's *.call() and *.apply() methods.
+
+var trim = String.prototype.trim;
+var trimLeft = String.prototype.trimLeft;
+var trimRight = String.prototype.trimRight;
+
+var testString = 'foo bar';
+var trimString = '';
+var leftTrimString = '';
+var rightTrimString = '';
+var wsString = '';
+
+var whitespace = [
+ {s : '\u0009', t : 'HORIZONTAL TAB'},
+ {s : '\u000A', t : 'LINE FEED OR NEW LINE'},
+ {s : '\u000B', t : 'VERTICAL TAB'},
+ {s : '\u000C', t : 'FORMFEED'},
+ {s : '\u000D', t : 'CARRIAGE RETURN'},
+ {s : '\u0020', t : 'SPACE'},
+ {s : '\u00A0', t : 'NO-BREAK SPACE'},
+ {s : '\u2000', t : 'EN QUAD'},
+ {s : '\u2001', t : 'EM QUAD'},
+ {s : '\u2002', t : 'EN SPACE'},
+ {s : '\u2003', t : 'EM SPACE'},
+ {s : '\u2004', t : 'THREE-PER-EM SPACE'},
+ {s : '\u2005', t : 'FOUR-PER-EM SPACE'},
+ {s : '\u2006', t : 'SIX-PER-EM SPACE'},
+ {s : '\u2007', t : 'FIGURE SPACE'},
+ {s : '\u2008', t : 'PUNCTUATION SPACE'},
+ {s : '\u2009', t : 'THIN SPACE'},
+ {s : '\u200A', t : 'HAIR SPACE'},
+ {s : '\u3000', t : 'IDEOGRAPHIC SPACE'},
+ {s : '\u2028', t : 'LINE SEPARATOR'},
+ {s : '\u2029', t : 'PARAGRAPH SEPARATOR'},
+ {s : '\u200B', t : 'ZERO WIDTH SPACE (category Cf)'}
+];
+
+for (var i = 0; i < whitespace.length; i++) {
+ assertEquals(whitespace[i].s.trim(), '');
+ assertEquals(whitespace[i].s.trimLeft(), '');
+ assertEquals(whitespace[i].s.trimRight(), '');
+ wsString += whitespace[i].s;
+}
+
+trimString = wsString + testString + wsString;
+leftTrimString = testString + wsString; // Trimmed from the left.
+rightTrimString = wsString + testString; // Trimmed from the right.
+
+assertEquals(wsString.trim(), '');
+assertEquals(wsString.trimLeft(), '');
+assertEquals(wsString.trimRight(), '');
+
+assertEquals(trimString.trim(), testString);
+assertEquals(trimString.trimLeft(), leftTrimString);
+assertEquals(trimString.trimRight(), rightTrimString);
+
+assertEquals(leftTrimString.trim(), testString);
+assertEquals(leftTrimString.trimLeft(), leftTrimString);
+assertEquals(leftTrimString.trimRight(), testString);
+
+assertEquals(rightTrimString.trim(), testString);
+assertEquals(rightTrimString.trimLeft(), testString);
+assertEquals(rightTrimString.trimRight(), rightTrimString);
+
+var testValues = [0, Infinity, NaN, true, false, ({}), ['an','array'],
+ ({toString:function(){return 'wibble'}})
+];
+
+for (var i = 0; i < testValues.length; i++) {
+ assertEquals(trim.call(testValues[i]), String(testValues[i]));
+ assertEquals(trimLeft.call(testValues[i]), String(testValues[i]));
+ assertEquals(trimRight.call(testValues[i]), String(testValues[i]));
+}
diff --git a/deps/v8/tools/gyp/v8.gyp b/deps/v8/tools/gyp/v8.gyp
index 46a00f4c6..5e2bb88e9 100644
--- a/deps/v8/tools/gyp/v8.gyp
+++ b/deps/v8/tools/gyp/v8.gyp
@@ -256,14 +256,16 @@
'../../src/execution.h',
'../../src/factory.cc',
'../../src/factory.h',
+ '../../src/fast-codegen.cc',
+ '../../src/fast-codegen.h',
'../../src/flag-definitions.h',
'../../src/flags.cc',
'../../src/flags.h',
+ '../../src/frame-element.cc',
+ '../../src/frame-element.h',
'../../src/frames-inl.h',
'../../src/frames.cc',
'../../src/frames.h',
- '../../src/frame-element.cc',
- '../../src/frame-element.h',
'../../src/func-name-inferrer.cc',
'../../src/func-name-inferrer.h',
'../../src/global-handles.cc',
@@ -291,11 +293,12 @@
'../../src/jsregexp.h',
'../../src/list-inl.h',
'../../src/list.h',
- '../../src/log.cc',
+ '../../src/location.h',
'../../src/log-inl.h',
- '../../src/log.h',
'../../src/log-utils.cc',
'../../src/log-utils.h',
+ '../../src/log.cc',
+ '../../src/log.h',
'../../src/macro-assembler.h',
'../../src/mark-compact.cc',
'../../src/mark-compact.h',
@@ -394,6 +397,7 @@
'../../src/arm/cpu-arm.cc',
'../../src/arm/debug-arm.cc',
'../../src/arm/disasm-arm.cc',
+ '../../src/arm/fast-codegen-arm.cc',
'../../src/arm/frames-arm.cc',
'../../src/arm/frames-arm.h',
'../../src/arm/ic-arm.cc',
@@ -423,6 +427,7 @@
'../../src/ia32/cpu-ia32.cc',
'../../src/ia32/debug-ia32.cc',
'../../src/ia32/disasm-ia32.cc',
+ '../../src/ia32/fast-codegen-ia32.cc',
'../../src/ia32/frames-ia32.cc',
'../../src/ia32/frames-ia32.h',
'../../src/ia32/ic-ia32.cc',
@@ -451,6 +456,7 @@
'../../src/x64/cpu-x64.cc',
'../../src/x64/debug-x64.cc',
'../../src/x64/disasm-x64.cc',
+ '../../src/x64/fast-codegen-x64.cc',
'../../src/x64/frames-x64.cc',
'../../src/x64/frames-x64.h',
'../../src/x64/ic-x64.cc',
diff --git a/deps/v8/tools/tickprocessor.js b/deps/v8/tools/tickprocessor.js
index 84f0eea58..fd23987d9 100644
--- a/deps/v8/tools/tickprocessor.js
+++ b/deps/v8/tools/tickprocessor.js
@@ -75,7 +75,18 @@ function TickProcessor(
'tick': { parsers: [this.createAddressParser('code'),
this.createAddressParser('stack'), parseInt, 'var-args'],
processor: this.processTick, backrefs: true },
+ 'heap-sample-begin': { parsers: [null, null, parseInt],
+ processor: this.processHeapSampleBegin },
+ 'heap-sample-end': { parsers: [null, null],
+ processor: this.processHeapSampleEnd },
+ 'heap-js-prod-item': { parsers: [null, 'var-args'],
+ processor: this.processJSProducer, backrefs: true },
+ // Ignored events.
'profiler': null,
+ 'heap-sample-stats': null,
+ 'heap-sample-item': null,
+ 'heap-js-cons-item': null,
+ 'heap-js-ret-item': null,
// Obsolete row types.
'code-allocate': null,
'begin-code-region': null,
@@ -113,6 +124,9 @@ function TickProcessor(
// Count each tick as a time unit.
this.viewBuilder_ = new devtools.profiler.ViewBuilder(1);
this.lastLogFileName_ = null;
+
+ this.generation_ = 1;
+ this.currentProducerProfile_ = null;
};
inherits(TickProcessor, devtools.profiler.LogReader);
@@ -220,6 +234,41 @@ TickProcessor.prototype.processTick = function(pc, sp, vmState, stack) {
};
+TickProcessor.prototype.processHeapSampleBegin = function(space, state, ticks) {
+ if (space != 'Heap') return;
+ this.currentProducerProfile_ = new devtools.profiler.CallTree();
+};
+
+
+TickProcessor.prototype.processHeapSampleEnd = function(space, state) {
+ if (space != 'Heap' || !this.currentProducerProfile_) return;
+
+ print('Generation ' + this.generation_ + ':');
+ var tree = this.currentProducerProfile_;
+ tree.computeTotalWeights();
+ var producersView = this.viewBuilder_.buildView(tree);
+ // Sort by total time, desc, then by name, desc.
+ producersView.sort(function(rec1, rec2) {
+ return rec2.totalTime - rec1.totalTime ||
+ (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
+ this.printHeavyProfile(producersView.head.children);
+
+ this.currentProducerProfile_ = null;
+ this.generation_++;
+};
+
+
+TickProcessor.prototype.processJSProducer = function(constructor, stack) {
+ if (!this.currentProducerProfile_) return;
+ if (stack.length == 0) return;
+ var first = stack.shift();
+ var processedStack =
+ this.profile_.resolveAndFilterFuncs_(this.processStack(first, stack));
+ processedStack.unshift(constructor);
+ this.currentProducerProfile_.addPath(processedStack);
+};
+
+
TickProcessor.prototype.printStatistics = function() {
print('Statistical profiling result from ' + this.lastLogFileName_ +
', (' + this.ticks_.total +
diff --git a/deps/v8/tools/v8.xcodeproj/project.pbxproj b/deps/v8/tools/v8.xcodeproj/project.pbxproj
index 2d386811e..d2af6262b 100644
--- a/deps/v8/tools/v8.xcodeproj/project.pbxproj
+++ b/deps/v8/tools/v8.xcodeproj/project.pbxproj
@@ -56,6 +56,7 @@
89495E480E79FC23001F68C3 /* compilation-cache.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89495E460E79FC23001F68C3 /* compilation-cache.cc */; };
89495E490E79FC23001F68C3 /* compilation-cache.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89495E460E79FC23001F68C3 /* compilation-cache.cc */; };
8956B6CF0F5D86730033B5A2 /* debug-agent.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8956B6CD0F5D86570033B5A2 /* debug-agent.cc */; };
+ 895FA753107FFED3006F39D4 /* constants-arm.cc in Sources */ = {isa = PBXBuildFile; fileRef = 895FA748107FFE73006F39D4 /* constants-arm.cc */; };
896FD03A0E78D717003DFB6A /* libv8-arm.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 89F23C870E78D5B2006B2466 /* libv8-arm.a */; };
897F767F0E71B690007ACF34 /* shell.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1B50E719C0900D62E90 /* shell.cc */; };
897F76850E71B6B1007ACF34 /* libv8.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8970F2F00E719FB2006AE7B5 /* libv8.a */; };
@@ -309,6 +310,14 @@
89495E470E79FC23001F68C3 /* compilation-cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "compilation-cache.h"; sourceTree = "<group>"; };
8956B6CD0F5D86570033B5A2 /* debug-agent.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "debug-agent.cc"; sourceTree = "<group>"; };
8956B6CE0F5D86570033B5A2 /* debug-agent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "debug-agent.h"; sourceTree = "<group>"; };
+ 895FA720107FFB15006F39D4 /* jump-target-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "jump-target-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>"; };
8970F2F00E719FB2006AE7B5 /* libv8.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libv8.a; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -662,8 +671,10 @@
897FF1110E719B8F00D62E90 /* code-stubs.cc */,
897FF1120E719B8F00D62E90 /* code-stubs.h */,
897FF1130E719B8F00D62E90 /* code.h */,
+ 895FA74B107FFE82006F39D4 /* codegen-arm-inl.h */,
897FF1140E719B8F00D62E90 /* codegen-arm.cc */,
896448BC0E9D530500E7C516 /* codegen-arm.h */,
+ 895FA725107FFB57006F39D4 /* codegen-ia32-inl.h */,
897FF1150E719B8F00D62E90 /* codegen-ia32.cc */,
8964482B0E9C00F700E7C516 /* codegen-ia32.h */,
897FF1160E719B8F00D62E90 /* codegen-inl.h */,
@@ -673,6 +684,7 @@
89495E470E79FC23001F68C3 /* compilation-cache.h */,
897FF1190E719B8F00D62E90 /* compiler.cc */,
897FF11A0E719B8F00D62E90 /* compiler.h */,
+ 895FA748107FFE73006F39D4 /* constants-arm.cc */,
897FF11B0E719B8F00D62E90 /* constants-arm.h */,
897FF11C0E719B8F00D62E90 /* contexts.cc */,
897FF11D0E719B8F00D62E90 /* contexts.h */,
@@ -739,6 +751,7 @@
89A15C670EE4665300B48DEB /* interpreter-irregexp.h */,
897FF14E0E719B8F00D62E90 /* jsregexp.cc */,
897FF14F0E719B8F00D62E90 /* jsregexp.h */,
+ 895FA720107FFB15006F39D4 /* jump-target-inl.h */,
58950D4E0F55514900F3E8BA /* jump-target-arm.cc */,
58950D4F0F55514900F3E8BA /* jump-target-ia32.cc */,
58950D500F55514900F3E8BA /* jump-target.cc */,
@@ -794,8 +807,12 @@
89A15C7A0EE466D000B48DEB /* regexp-macro-assembler.h */,
8944AD0E0F1D4D3A0028D560 /* regexp-stack.cc */,
8944AD0F0F1D4D3A0028D560 /* regexp-stack.h */,
+ 895FA750107FFEAE006F39D4 /* register-allocator-arm-inl.h */,
58950D520F55514900F3E8BA /* register-allocator-arm.cc */,
+ 895FA751107FFEAE006F39D4 /* register-allocator-arm.h */,
+ 895FA72A107FFB85006F39D4 /* register-allocator-ia32-inl.h */,
58950D530F55514900F3E8BA /* register-allocator-ia32.cc */,
+ 895FA72B107FFB85006F39D4 /* register-allocator-ia32.h */,
893A722D0F7B4A7100303DD2 /* register-allocator-inl.h */,
58950D540F55514900F3E8BA /* register-allocator.cc */,
58950D550F55514900F3E8BA /* register-allocator.h */,
@@ -1238,6 +1255,7 @@
89F23C4B0E78D5B2006B2466 /* codegen.cc in Sources */,
89495E490E79FC23001F68C3 /* compilation-cache.cc in Sources */,
89F23C4C0E78D5B2006B2466 /* compiler.cc in Sources */,
+ 895FA753107FFED3006F39D4 /* constants-arm.cc in Sources */,
89F23C4D0E78D5B2006B2466 /* contexts.cc in Sources */,
89F23C4E0E78D5B2006B2466 /* conversions.cc in Sources */,
89F23C4F0E78D5B2006B2466 /* counters.cc in Sources */,
@@ -1467,6 +1485,7 @@
V8_NATIVE_REGEXP,
DEBUG,
V8_ENABLE_CHECKS,
+ ENABLE_DEBUGGER_SUPPORT,
);
HEADER_SEARCH_PATHS = ../src;
PRODUCT_NAME = v8_shell;
@@ -1481,6 +1500,7 @@
V8_TARGET_ARCH_IA32,
V8_NATIVE_REGEXP,
NDEBUG,
+ ENABLE_DEBUGGER_SUPPORT,
);
HEADER_SEARCH_PATHS = ../src;
PRODUCT_NAME = v8_shell;
@@ -1514,6 +1534,7 @@
V8_TARGET_ARCH_IA32,
V8_NATIVE_REGEXP,
NDEBUG,
+ ENABLE_DEBUGGER_SUPPORT,
);
HEADER_SEARCH_PATHS = ../src;
PRODUCT_NAME = v8;
@@ -1524,6 +1545,10 @@
897F767C0E71B4CC007ACF34 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "$(GCC_PREPROCESSOR_DEFINITIONS)",
+ V8_TARGET_ARCH_IA32,
+ );
HEADER_SEARCH_PATHS = ../src;
PRODUCT_NAME = v8_shell;
};
@@ -1532,6 +1557,10 @@
897F767D0E71B4CC007ACF34 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "$(GCC_PREPROCESSOR_DEFINITIONS)",
+ V8_TARGET_ARCH_IA32,
+ );
HEADER_SEARCH_PATHS = ../src;
PRODUCT_NAME = v8_shell;
};
@@ -1571,6 +1600,10 @@
89F23C930E78D5B6006B2466 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "$(GCC_PREPROCESSOR_DEFINITIONS)",
+ V8_TARGET_ARCH_ARM,
+ );
HEADER_SEARCH_PATHS = ../src;
PRODUCT_NAME = "v8_shell-arm";
};
@@ -1579,6 +1612,10 @@
89F23C940E78D5B6006B2466 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "$(GCC_PREPROCESSOR_DEFINITIONS)",
+ V8_TARGET_ARCH_ARM,
+ );
HEADER_SEARCH_PATHS = ../src;
PRODUCT_NAME = "v8_shell-arm";
};
diff --git a/deps/v8/tools/visual_studio/v8_base.vcproj b/deps/v8/tools/visual_studio/v8_base.vcproj
index 7a013c0c1..fc7402aeb 100644
--- a/deps/v8/tools/visual_studio/v8_base.vcproj
+++ b/deps/v8/tools/visual_studio/v8_base.vcproj
@@ -388,6 +388,18 @@
RelativePath="..\..\src\factory.h"
>
</File>
+ <File
+ RelativePath="..\..\src\ia32\fast-codegen-ia32.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\fast-codegen.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\fast-codegen.h"
+ >
+ </File>
<File
RelativePath="..\..\src\flags.cc"
>
@@ -545,6 +557,10 @@
>
</File>
<File
+ RelativePath="..\..\src\location.h"
+ >
+ </File>
+ <File
RelativePath="..\..\src\log.cc"
>
</File>
@@ -945,7 +961,7 @@
Name="include"
>
<File
- RelativePath="..\..\include\debug.h"
+ RelativePath="..\..\include\v8-debug.h"
>
</File>
<File
diff --git a/deps/v8/tools/visual_studio/v8_base_arm.vcproj b/deps/v8/tools/visual_studio/v8_base_arm.vcproj
index abdb41818..fca4a9605 100644
--- a/deps/v8/tools/visual_studio/v8_base_arm.vcproj
+++ b/deps/v8/tools/visual_studio/v8_base_arm.vcproj
@@ -396,6 +396,18 @@
RelativePath="..\..\src\factory.h"
>
</File>
+ <File
+ RelativePath="..\..\src\arm\fast-codegen-arm.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\fast-codegen.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\fast-codegen.h"
+ >
+ </File>
<File
RelativePath="..\..\src\flags.cc"
>
@@ -549,6 +561,10 @@
>
</File>
<File
+ RelativePath="..\..\src\location.h"
+ >
+ </File>
+ <File
RelativePath="..\..\src\log.cc"
>
</File>
@@ -957,7 +973,7 @@
Name="include"
>
<File
- RelativePath="..\..\include\debug.h"
+ RelativePath="..\..\include\v8-debug.h"
>
</File>
<File
diff --git a/deps/v8/tools/visual_studio/v8_base_x64.vcproj b/deps/v8/tools/visual_studio/v8_base_x64.vcproj
index 7b8b4d35c..a8c8b55fa 100644
--- a/deps/v8/tools/visual_studio/v8_base_x64.vcproj
+++ b/deps/v8/tools/visual_studio/v8_base_x64.vcproj
@@ -945,7 +945,7 @@
Name="include"
>
<File
- RelativePath="..\..\include\debug.h"
+ RelativePath="..\..\include\v8-debug.h"
>
</File>
<File