summaryrefslogtreecommitdiff
path: root/deps/v8/src
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2011-01-17 11:32:56 -0800
committerRyan Dahl <ry@tinyclouds.org>2011-01-17 11:32:56 -0800
commitcf2e4f44afbfb208c5976786c96ec963930323cc (patch)
tree7e9ddac16d51490f1428abb610afd02eda98aacf /deps/v8/src
parent082a4b6033df22a68518c58d320e86f688db7bda (diff)
downloadnode-new-cf2e4f44afbfb208c5976786c96ec963930323cc.tar.gz
Upgrade V8 to 3.0.8
Diffstat (limited to 'deps/v8/src')
-rwxr-xr-xdeps/v8/src/SConscript11
-rw-r--r--deps/v8/src/accessors.cc6
-rw-r--r--deps/v8/src/api.cc56
-rw-r--r--deps/v8/src/arm/assembler-arm.cc6
-rw-r--r--deps/v8/src/arm/assembler-arm.h21
-rw-r--r--deps/v8/src/arm/builtins-arm.cc4
-rw-r--r--deps/v8/src/arm/code-stubs-arm.cc74
-rw-r--r--deps/v8/src/arm/code-stubs-arm.h43
-rw-r--r--deps/v8/src/arm/codegen-arm.cc21
-rw-r--r--deps/v8/src/arm/deoptimizer-arm.cc5
-rw-r--r--deps/v8/src/arm/full-codegen-arm.cc13
-rw-r--r--deps/v8/src/arm/ic-arm.cc2
-rw-r--r--deps/v8/src/arm/lithium-arm.cc466
-rw-r--r--deps/v8/src/arm/lithium-arm.h416
-rw-r--r--deps/v8/src/arm/lithium-codegen-arm.cc1262
-rw-r--r--deps/v8/src/arm/lithium-codegen-arm.h39
-rw-r--r--deps/v8/src/arm/macro-assembler-arm.cc84
-rw-r--r--deps/v8/src/arm/macro-assembler-arm.h66
-rw-r--r--deps/v8/src/arm/regexp-macro-assembler-arm.cc10
-rw-r--r--deps/v8/src/arm/regexp-macro-assembler-arm.h12
-rw-r--r--deps/v8/src/arm/simulator-arm.cc12
-rw-r--r--deps/v8/src/arm/stub-cache-arm.cc6
-rw-r--r--deps/v8/src/array.js54
-rw-r--r--deps/v8/src/assembler.cc12
-rw-r--r--deps/v8/src/assembler.h5
-rw-r--r--deps/v8/src/ast.cc34
-rw-r--r--deps/v8/src/ast.h10
-rw-r--r--deps/v8/src/builtins.cc67
-rw-r--r--deps/v8/src/code-stubs.cc34
-rw-r--r--deps/v8/src/code-stubs.h60
-rwxr-xr-xdeps/v8/src/compiler.cc23
-rw-r--r--deps/v8/src/compiler.h6
-rw-r--r--deps/v8/src/cpu-profiler.cc3
-rw-r--r--deps/v8/src/d8-debug.cc19
-rw-r--r--deps/v8/src/d8-debug.h6
-rw-r--r--deps/v8/src/d8.cc3
-rw-r--r--deps/v8/src/d8.js611
-rw-r--r--deps/v8/src/data-flow.h7
-rw-r--r--deps/v8/src/date.js2
-rw-r--r--deps/v8/src/debug-agent.cc33
-rw-r--r--deps/v8/src/debug-agent.h6
-rw-r--r--deps/v8/src/debug-debugger.js167
-rw-r--r--deps/v8/src/debug.cc11
-rw-r--r--deps/v8/src/debug.h10
-rw-r--r--deps/v8/src/deoptimizer.cc14
-rw-r--r--deps/v8/src/deoptimizer.h6
-rw-r--r--deps/v8/src/disassembler.cc11
-rw-r--r--deps/v8/src/extensions/experimental/experimental.gyp50
-rw-r--r--deps/v8/src/factory.cc8
-rw-r--r--deps/v8/src/factory.h2
-rw-r--r--deps/v8/src/flag-definitions.h12
-rw-r--r--deps/v8/src/frames.cc50
-rw-r--r--deps/v8/src/frames.h6
-rw-r--r--deps/v8/src/globals.h13
-rw-r--r--deps/v8/src/handles.cc13
-rw-r--r--deps/v8/src/handles.h11
-rw-r--r--deps/v8/src/heap-inl.h78
-rw-r--r--deps/v8/src/heap.cc76
-rw-r--r--deps/v8/src/heap.h19
-rw-r--r--deps/v8/src/hydrogen-instructions.cc5
-rw-r--r--deps/v8/src/hydrogen-instructions.h176
-rw-r--r--deps/v8/src/hydrogen.cc246
-rw-r--r--deps/v8/src/hydrogen.h124
-rw-r--r--deps/v8/src/ia32/assembler-ia32.cc45
-rw-r--r--deps/v8/src/ia32/assembler-ia32.h4
-rw-r--r--deps/v8/src/ia32/builtins-ia32.cc8
-rw-r--r--deps/v8/src/ia32/code-stubs-ia32.cc196
-rw-r--r--deps/v8/src/ia32/code-stubs-ia32.h11
-rw-r--r--deps/v8/src/ia32/codegen-ia32.cc351
-rw-r--r--deps/v8/src/ia32/debug-ia32.cc2
-rw-r--r--deps/v8/src/ia32/deoptimizer-ia32.cc23
-rw-r--r--deps/v8/src/ia32/disasm-ia32.cc28
-rw-r--r--deps/v8/src/ia32/full-codegen-ia32.cc373
-rw-r--r--deps/v8/src/ia32/ic-ia32.cc15
-rw-r--r--deps/v8/src/ia32/lithium-codegen-ia32.cc512
-rw-r--r--deps/v8/src/ia32/lithium-codegen-ia32.h34
-rw-r--r--deps/v8/src/ia32/lithium-ia32.cc678
-rw-r--r--deps/v8/src/ia32/lithium-ia32.h859
-rw-r--r--deps/v8/src/ia32/macro-assembler-ia32.cc98
-rw-r--r--deps/v8/src/ia32/macro-assembler-ia32.h23
-rw-r--r--deps/v8/src/ia32/regexp-macro-assembler-ia32.cc6
-rw-r--r--deps/v8/src/ia32/stub-cache-ia32.cc1
-rw-r--r--deps/v8/src/json.js93
-rw-r--r--deps/v8/src/jsregexp.cc6
-rw-r--r--deps/v8/src/jsregexp.h2
-rw-r--r--deps/v8/src/lithium-allocator.cc57
-rw-r--r--deps/v8/src/lithium-allocator.h57
-rw-r--r--deps/v8/src/lithium.cc93
-rw-r--r--deps/v8/src/lithium.h169
-rw-r--r--deps/v8/src/liveedit-debugger.js12
-rw-r--r--deps/v8/src/liveedit.cc123
-rw-r--r--deps/v8/src/liveedit.h9
-rw-r--r--deps/v8/src/log.cc3
-rw-r--r--deps/v8/src/macros.py2
-rw-r--r--deps/v8/src/math.js42
-rw-r--r--deps/v8/src/messages.js34
-rw-r--r--deps/v8/src/mirror-debugger.js2
-rw-r--r--deps/v8/src/objects-debug.cc864
-rw-r--r--deps/v8/src/objects-inl.h21
-rw-r--r--deps/v8/src/objects-printer.cc778
-rw-r--r--deps/v8/src/objects-visiting.h6
-rw-r--r--deps/v8/src/objects.cc194
-rw-r--r--deps/v8/src/objects.h65
-rw-r--r--deps/v8/src/parser.cc241
-rw-r--r--deps/v8/src/parser.h35
-rw-r--r--deps/v8/src/platform-cygwin.cc865
-rw-r--r--deps/v8/src/platform-freebsd.cc12
-rw-r--r--deps/v8/src/platform-linux.cc14
-rw-r--r--deps/v8/src/platform-macos.cc33
-rw-r--r--deps/v8/src/platform-nullos.cc13
-rw-r--r--deps/v8/src/platform-openbsd.cc12
-rw-r--r--deps/v8/src/platform-solaris.cc12
-rw-r--r--deps/v8/src/platform-win32.cc13
-rw-r--r--deps/v8/src/platform.h18
-rw-r--r--deps/v8/src/preparse-data.cc21
-rw-r--r--deps/v8/src/preparse-data.h48
-rw-r--r--deps/v8/src/preparser-api.cc10
-rw-r--r--deps/v8/src/preparser.cc43
-rw-r--r--deps/v8/src/preparser.h9
-rw-r--r--deps/v8/src/profile-generator.cc2
-rw-r--r--deps/v8/src/regexp-macro-assembler-irregexp.h12
-rw-r--r--deps/v8/src/regexp-macro-assembler-tracer.cc12
-rw-r--r--deps/v8/src/regexp-macro-assembler-tracer.h12
-rw-r--r--deps/v8/src/regexp-macro-assembler.h12
-rw-r--r--deps/v8/src/rewriter.cc2
-rw-r--r--deps/v8/src/runtime-profiler.cc6
-rw-r--r--deps/v8/src/runtime.cc171
-rw-r--r--deps/v8/src/runtime.h10
-rw-r--r--deps/v8/src/runtime.js67
-rw-r--r--deps/v8/src/safepoint-table.cc95
-rw-r--r--deps/v8/src/safepoint-table.h127
-rw-r--r--deps/v8/src/scanner-base.cc45
-rw-r--r--deps/v8/src/scanner-base.h229
-rwxr-xr-xdeps/v8/src/scanner.cc21
-rw-r--r--deps/v8/src/scanner.h8
-rw-r--r--deps/v8/src/scopes.cc158
-rw-r--r--deps/v8/src/scopes.h39
-rw-r--r--deps/v8/src/serialize.cc14
-rw-r--r--deps/v8/src/string-search.h7
-rw-r--r--deps/v8/src/string.js2
-rw-r--r--deps/v8/src/token.h6
-rw-r--r--deps/v8/src/top.cc4
-rw-r--r--deps/v8/src/type-info.cc71
-rw-r--r--deps/v8/src/type-info.h9
-rw-r--r--deps/v8/src/unicode.cc328
-rw-r--r--deps/v8/src/utils.h24
-rw-r--r--deps/v8/src/v8-counters.h10
-rw-r--r--deps/v8/src/v8globals.h3
-rw-r--r--deps/v8/src/v8natives.js22
-rw-r--r--deps/v8/src/v8threads.cc3
-rw-r--r--deps/v8/src/v8utils.h2
-rw-r--r--deps/v8/src/variables.cc6
-rw-r--r--deps/v8/src/variables.h7
-rw-r--r--deps/v8/src/version.cc4
-rw-r--r--deps/v8/src/x64/assembler-x64.cc16
-rw-r--r--deps/v8/src/x64/assembler-x64.h30
-rw-r--r--deps/v8/src/x64/builtins-x64.cc10
-rw-r--r--deps/v8/src/x64/code-stubs-x64.cc197
-rw-r--r--deps/v8/src/x64/code-stubs-x64.h140
-rw-r--r--deps/v8/src/x64/codegen-x64.cc29
-rw-r--r--deps/v8/src/x64/debug-x64.cc148
-rw-r--r--deps/v8/src/x64/deoptimizer-x64.cc4
-rw-r--r--deps/v8/src/x64/full-codegen-x64.cc194
-rw-r--r--deps/v8/src/x64/ic-x64.cc464
-rw-r--r--deps/v8/src/x64/lithium-codegen-x64.cc1475
-rw-r--r--deps/v8/src/x64/lithium-codegen-x64.h249
-rw-r--r--deps/v8/src/x64/lithium-x64.cc1435
-rw-r--r--deps/v8/src/x64/lithium-x64.h2026
-rw-r--r--deps/v8/src/x64/macro-assembler-x64.cc8
-rw-r--r--deps/v8/src/x64/macro-assembler-x64.h2
-rw-r--r--deps/v8/src/x64/regexp-macro-assembler-x64.cc6
-rw-r--r--deps/v8/src/x64/stub-cache-x64.cc2259
-rw-r--r--deps/v8/src/zone-inl.h3
-rw-r--r--deps/v8/src/zone.cc3
-rw-r--r--deps/v8/src/zone.h4
175 files changed, 15220 insertions, 7068 deletions
diff --git a/deps/v8/src/SConscript b/deps/v8/src/SConscript
index 8ccc6f2d8b..79b12040bd 100755
--- a/deps/v8/src/SConscript
+++ b/deps/v8/src/SConscript
@@ -1,4 +1,4 @@
-# Copyright 2008 the V8 project authors. All rights reserved.
+# Copyright 2011 the V8 project authors. All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
@@ -85,6 +85,7 @@ SOURCES = {
jsregexp.cc
jump-target.cc
lithium-allocator.cc
+ lithium.cc
liveedit.cc
log-utils.cc
log.cc
@@ -211,6 +212,8 @@ SOURCES = {
x64/full-codegen-x64.cc
x64/ic-x64.cc
x64/jump-target-x64.cc
+ x64/lithium-x64.cc
+ x64/lithium-codegen-x64.cc
x64/macro-assembler-x64.cc
x64/regexp-macro-assembler-x64.cc
x64/register-allocator-x64.cc
@@ -225,14 +228,14 @@ SOURCES = {
'os:android': ['platform-linux.cc', 'platform-posix.cc'],
'os:macos': ['platform-macos.cc', 'platform-posix.cc'],
'os:solaris': ['platform-solaris.cc', 'platform-posix.cc'],
- 'os:cygwin': ['platform-cygwin.cc', 'platform-posix.cc'],
'os:nullos': ['platform-nullos.cc'],
'os:win32': ['platform-win32.cc'],
'mode:release': [],
'mode:debug': [
- 'objects-debug.cc', 'prettyprinter.cc', 'regexp-macro-assembler-tracer.cc'
+ 'objects-debug.cc', 'objects-printer.cc', 'prettyprinter.cc',
+ 'regexp-macro-assembler-tracer.cc'
],
- 'objectprint:on': ['objects-debug.cc']
+ 'objectprint:on': ['objects-printer.cc']
}
diff --git a/deps/v8/src/accessors.cc b/deps/v8/src/accessors.cc
index 43d54fe474..c7d9cfe94c 100644
--- a/deps/v8/src/accessors.cc
+++ b/deps/v8/src/accessors.cc
@@ -126,8 +126,8 @@ MaybeObject* Accessors::ArraySetLength(JSObject* object, Object* value, void*) {
// This means one of the object's prototypes is a JSArray and
// the object does not have a 'length' property.
// Calling SetProperty causes an infinite loop.
- return object->IgnoreAttributesAndSetLocalProperty(Heap::length_symbol(),
- value, NONE);
+ return object->SetLocalPropertyIgnoreAttributes(Heap::length_symbol(),
+ value, NONE);
}
}
return Top::Throw(*Factory::NewRangeError("invalid_array_length",
@@ -775,7 +775,7 @@ MaybeObject* Accessors::FunctionGetArguments(Object* object, void*) {
if (index >= 0) {
Handle<Object> arguments =
Handle<Object>(frame->GetExpression(index));
- if (!arguments->IsTheHole()) return *arguments;
+ if (!arguments->IsArgumentsMarker()) return *arguments;
}
// If there isn't an arguments variable in the stack, we need to
diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc
index 110468e231..073306f071 100644
--- a/deps/v8/src/api.cc
+++ b/deps/v8/src/api.cc
@@ -3266,18 +3266,35 @@ void v8::Object::SetInternalField(int index, v8::Handle<Value> value) {
}
+static bool CanBeEncodedAsSmi(void* ptr) {
+ const intptr_t address = reinterpret_cast<intptr_t>(ptr);
+ return ((address & i::kEncodablePointerMask) == 0);
+}
+
+
+static i::Smi* EncodeAsSmi(void* ptr) {
+ ASSERT(CanBeEncodedAsSmi(ptr));
+ const intptr_t address = reinterpret_cast<intptr_t>(ptr);
+ i::Smi* result = reinterpret_cast<i::Smi*>(address << i::kPointerToSmiShift);
+ ASSERT(i::Internals::HasSmiTag(result));
+ ASSERT_EQ(result, i::Smi::FromInt(result->value()));
+ ASSERT_EQ(ptr, i::Internals::GetExternalPointerFromSmi(result));
+ return result;
+}
+
+
void v8::Object::SetPointerInInternalField(int index, void* value) {
ENTER_V8;
- i::Object* as_object = reinterpret_cast<i::Object*>(value);
- if (as_object->IsSmi()) {
- Utils::OpenHandle(this)->SetInternalField(index, as_object);
- return;
+ if (CanBeEncodedAsSmi(value)) {
+ Utils::OpenHandle(this)->SetInternalField(index, EncodeAsSmi(value));
+ } else {
+ 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);
}
- 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);
+ ASSERT_EQ(value, GetPointerFromInternalField(index));
}
@@ -3299,7 +3316,8 @@ bool v8::V8::Dispose() {
HeapStatistics::HeapStatistics(): total_heap_size_(0),
total_heap_size_executable_(0),
- used_heap_size_(0) { }
+ used_heap_size_(0),
+ heap_size_limit_(0) { }
void v8::V8::GetHeapStatistics(HeapStatistics* heap_statistics) {
@@ -3307,6 +3325,7 @@ void v8::V8::GetHeapStatistics(HeapStatistics* heap_statistics) {
heap_statistics->set_total_heap_size_executable(
i::Heap::CommittedMemoryExecutable());
heap_statistics->set_used_heap_size(i::Heap::SizeOfObjects());
+ heap_statistics->set_heap_size_limit(i::Heap::MaxReserved());
}
@@ -3560,11 +3579,13 @@ Local<Value> v8::External::Wrap(void* data) {
LOG_API("External::Wrap");
EnsureInitialized("v8::External::Wrap()");
ENTER_V8;
- 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);
+
+ v8::Local<v8::Value> result = CanBeEncodedAsSmi(data)
+ ? Utils::ToLocal(i::Handle<i::Object>(EncodeAsSmi(data)))
+ : v8::Local<v8::Value>(ExternalNewImpl(data));
+
+ ASSERT_EQ(data, Unwrap(result));
+ return result;
}
@@ -3572,7 +3593,7 @@ 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;
+ return i::Internals::GetExternalPointerFromSmi(value);
} else if (value->IsProxy()) {
return reinterpret_cast<void*>(i::Proxy::cast(value)->proxy());
} else {
@@ -3586,8 +3607,7 @@ void* v8::External::FullUnwrap(v8::Handle<v8::Value> wrapper) {
i::Handle<i::Object> obj = Utils::OpenHandle(*wrapper);
void* result;
if (obj->IsSmi()) {
- // The external value was an aligned pointer.
- result = *obj;
+ result = i::Internals::GetExternalPointerFromSmi(*obj);
} else if (obj->IsProxy()) {
result = ExternalValueImpl(obj);
} else {
diff --git a/deps/v8/src/arm/assembler-arm.cc b/deps/v8/src/arm/assembler-arm.cc
index 8fdcf18216..a7c1897aed 100644
--- a/deps/v8/src/arm/assembler-arm.cc
+++ b/deps/v8/src/arm/assembler-arm.cc
@@ -2337,12 +2337,11 @@ void Assembler::vdiv(const DwVfpRegister dst,
void Assembler::vcmp(const DwVfpRegister src1,
const DwVfpRegister src2,
- const SBit s,
const Condition cond) {
// vcmp(Dd, Dm) double precision floating point comparison.
// Instruction details available in ARM DDI 0406A, A8-570.
// cond(31-28) | 11101 (27-23)| D=?(22) | 11 (21-20) | 0100 (19-16) |
- // Vd(15-12) | 101(11-9) | sz(8)=1 | E(7)=? | 1(6) | M(5)=? | 0(4) | Vm(3-0)
+ // Vd(15-12) | 101(11-9) | sz(8)=1 | E(7)=0 | 1(6) | M(5)=? | 0(4) | Vm(3-0)
ASSERT(CpuFeatures::IsEnabled(VFP3));
emit(cond | 0xE*B24 |B23 | 0x3*B20 | B18 |
src1.code()*B12 | 0x5*B9 | B8 | B6 | src2.code());
@@ -2351,12 +2350,11 @@ void Assembler::vcmp(const DwVfpRegister src1,
void Assembler::vcmp(const DwVfpRegister src1,
const double src2,
- const SBit s,
const Condition cond) {
// vcmp(Dd, Dm) double precision floating point comparison.
// Instruction details available in ARM DDI 0406A, A8-570.
// cond(31-28) | 11101 (27-23)| D=?(22) | 11 (21-20) | 0101 (19-16) |
- // Vd(15-12) | 101(11-9) | sz(8)=1 | E(7)=? | 1(6) | M(5)=? | 0(4) | 0000(3-0)
+ // Vd(15-12) | 101(11-9) | sz(8)=1 | E(7)=0 | 1(6) | M(5)=? | 0(4) | 0000(3-0)
ASSERT(CpuFeatures::IsEnabled(VFP3));
ASSERT(src2 == 0.0);
emit(cond | 0xE*B24 |B23 | 0x3*B20 | B18 | B16 |
diff --git a/deps/v8/src/arm/assembler-arm.h b/deps/v8/src/arm/assembler-arm.h
index 36f7507fe7..e0ea819e99 100644
--- a/deps/v8/src/arm/assembler-arm.h
+++ b/deps/v8/src/arm/assembler-arm.h
@@ -66,13 +66,14 @@ namespace internal {
// such that we use an enum in optimized mode, and the struct in debug
// mode. This way we get the compile-time error checking in debug mode
// and best performance in optimized code.
-//
+
// Core register
struct Register {
static const int kNumRegisters = 16;
static const int kNumAllocatableRegisters = 8;
static int ToAllocationIndex(Register reg) {
+ ASSERT(reg.code() < kNumAllocatableRegisters);
return reg.code();
}
@@ -132,7 +133,7 @@ const Register r5 = { 5 };
const Register r6 = { 6 };
const Register r7 = { 7 };
const Register r8 = { 8 }; // Used as context register.
-const Register r9 = { 9 };
+const Register r9 = { 9 }; // Used as lithium codegen scratch register.
const Register r10 = { 10 }; // Used as roots register.
const Register fp = { 11 };
const Register ip = { 12 };
@@ -166,6 +167,9 @@ struct SwVfpRegister {
struct DwVfpRegister {
// d0 has been excluded from allocation. This is following ia32
// where xmm0 is excluded. This should be revisited.
+ // Currently d0 is used as a scratch register.
+ // d1 has also been excluded from allocation to be used as a scratch
+ // register as well.
static const int kNumRegisters = 16;
static const int kNumAllocatableRegisters = 15;
@@ -297,11 +301,18 @@ const DwVfpRegister d14 = { 14 };
const DwVfpRegister d15 = { 15 };
// VFP FPSCR constants.
-static const uint32_t kVFPExceptionMask = 0xf;
-static const uint32_t kVFPRoundingModeMask = 3 << 22;
+static const uint32_t kVFPNConditionFlagBit = 1 << 31;
+static const uint32_t kVFPZConditionFlagBit = 1 << 30;
+static const uint32_t kVFPCConditionFlagBit = 1 << 29;
+static const uint32_t kVFPVConditionFlagBit = 1 << 28;
+
static const uint32_t kVFPFlushToZeroMask = 1 << 24;
+
+static const uint32_t kVFPRoundingModeMask = 3 << 22;
static const uint32_t kVFPRoundToMinusInfinityBits = 2 << 22;
+static const uint32_t kVFPExceptionMask = 0xf;
+
// Coprocessor register
struct CRegister {
bool is_valid() const { return 0 <= code_ && code_ < 16; }
@@ -1144,11 +1155,9 @@ class Assembler : public Malloced {
const Condition cond = al);
void vcmp(const DwVfpRegister src1,
const DwVfpRegister src2,
- const SBit s = LeaveCC,
const Condition cond = al);
void vcmp(const DwVfpRegister src1,
const double src2,
- const SBit s = LeaveCC,
const Condition cond = al);
void vmrs(const Register dst,
const Condition cond = al);
diff --git a/deps/v8/src/arm/builtins-arm.cc b/deps/v8/src/arm/builtins-arm.cc
index 6480a9162b..0210b1b96e 100644
--- a/deps/v8/src/arm/builtins-arm.cc
+++ b/deps/v8/src/arm/builtins-arm.cc
@@ -502,7 +502,7 @@ void Builtins::Generate_StringConstructCode(MacroAssembler* masm) {
// Load the first arguments in r0 and get rid of the rest.
Label no_arguments;
- __ cmp(r0, Operand(0));
+ __ cmp(r0, Operand(0, RelocInfo::NONE));
__ b(eq, &no_arguments);
// First args = sp[(argc - 1) * 4].
__ sub(r0, r0, Operand(1));
@@ -546,7 +546,7 @@ void Builtins::Generate_StringConstructCode(MacroAssembler* masm) {
__ cmp(r4, Operand(JSValue::kSize >> kPointerSizeLog2));
__ Assert(eq, "Unexpected string wrapper instance size");
__ ldrb(r4, FieldMemOperand(map, Map::kUnusedPropertyFieldsOffset));
- __ cmp(r4, Operand(0));
+ __ cmp(r4, Operand(0, RelocInfo::NONE));
__ Assert(eq, "Unexpected unused properties of string wrapper");
}
__ str(map, FieldMemOperand(r0, HeapObject::kMapOffset));
diff --git a/deps/v8/src/arm/code-stubs-arm.cc b/deps/v8/src/arm/code-stubs-arm.cc
index 5ec8584f9e..8589cf0ef9 100644
--- a/deps/v8/src/arm/code-stubs-arm.cc
+++ b/deps/v8/src/arm/code-stubs-arm.cc
@@ -866,8 +866,7 @@ void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
__ vldr(d0, scratch2, HeapNumber::kValueOffset);
__ sub(probe, probe, Operand(kHeapObjectTag));
__ vldr(d1, probe, HeapNumber::kValueOffset);
- __ vcmp(d0, d1);
- __ vmrs(pc);
+ __ VFPCompareAndSetFlags(d0, d1);
__ b(ne, not_found); // The cache did not contain this value.
__ b(&load_result_from_cache);
} else {
@@ -917,13 +916,6 @@ void NumberToStringStub::Generate(MacroAssembler* masm) {
}
-void RecordWriteStub::Generate(MacroAssembler* masm) {
- __ add(offset_, object_, Operand(offset_));
- __ RecordWriteHelper(object_, offset_, scratch_);
- __ Ret();
-}
-
-
// On entry lhs_ and rhs_ are the values to be compared.
// On exit r0 is 0, positive or negative to indicate the result of
// the comparison.
@@ -982,8 +974,7 @@ void CompareStub::Generate(MacroAssembler* masm) {
CpuFeatures::Scope scope(VFP3);
Label no_nan;
// ARMv7 VFP3 instructions to implement double precision comparison.
- __ vcmp(d7, d6);
- __ vmrs(pc); // Move vector status bits to normal status bits.
+ __ VFPCompareAndSetFlags(d7, d6);
Label nan;
__ b(vs, &nan);
__ mov(r0, Operand(EQUAL), LeaveCC, eq);
@@ -1103,8 +1094,7 @@ void ToBooleanStub::Generate(MacroAssembler* masm) {
__ sub(ip, tos_, Operand(kHeapObjectTag));
__ vldr(d1, ip, HeapNumber::kValueOffset);
- __ vcmp(d1, 0.0);
- __ vmrs(pc);
+ __ VFPCompareAndSetFlags(d1, 0.0);
// "tos_" is a register, and contains a non zero value by default.
// Hence we only need to overwrite "tos_" with zero to return false for
// FP_ZERO or FP_NAN cases. Otherwise, by default it returns true.
@@ -1229,16 +1219,22 @@ void GenericBinaryOpStub::HandleBinaryOpSlowCases(
bool generate_code_to_calculate_answer = true;
if (ShouldGenerateFPCode()) {
+ // DIV has neither SmiSmi fast code nor specialized slow code.
+ // So don't try to patch a DIV Stub.
if (runtime_operands_type_ == BinaryOpIC::DEFAULT) {
switch (op_) {
case Token::ADD:
case Token::SUB:
case Token::MUL:
- case Token::DIV:
GenerateTypeTransition(masm); // Tail call.
generate_code_to_calculate_answer = false;
break;
+ case Token::DIV:
+ // DIV has neither SmiSmi fast code nor specialized slow code.
+ // So don't try to patch a DIV Stub.
+ break;
+
default:
break;
}
@@ -1299,7 +1295,8 @@ void GenericBinaryOpStub::HandleBinaryOpSlowCases(
// HEAP_NUMBERS stub is slower than GENERIC on a pair of smis.
// r0 is known to be a smi. If r1 is also a smi then switch to GENERIC.
Label r1_is_not_smi;
- if (runtime_operands_type_ == BinaryOpIC::HEAP_NUMBERS) {
+ if ((runtime_operands_type_ == BinaryOpIC::HEAP_NUMBERS) &&
+ HasSmiSmiFastPath()) {
__ tst(r1, Operand(kSmiTagMask));
__ b(ne, &r1_is_not_smi);
GenerateTypeTransition(masm); // Tail call.
@@ -2519,7 +2516,7 @@ void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
if (type == OUT_OF_MEMORY) {
// Set external caught exception to false.
ExternalReference external_caught(Top::k_external_caught_exception_address);
- __ mov(r0, Operand(false));
+ __ mov(r0, Operand(false, RelocInfo::NONE));
__ mov(r2, Operand(external_caught));
__ str(r0, MemOperand(r2));
@@ -2894,45 +2891,45 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
// Uses registers r0 to r4. Expected input is
-// function in r0 (or at sp+1*ptrsz) and object in
+// object in r0 (or at sp+1*kPointerSize) and function in
// r1 (or at sp), depending on whether or not
// args_in_registers() is true.
void InstanceofStub::Generate(MacroAssembler* masm) {
// Fixed register usage throughout the stub:
- const Register object = r1; // Object (lhs).
+ const Register object = r0; // Object (lhs).
const Register map = r3; // Map of the object.
- const Register function = r0; // Function (rhs).
+ const Register function = r1; // Function (rhs).
const Register prototype = r4; // Prototype of the function.
const Register scratch = r2;
Label slow, loop, is_instance, is_not_instance, not_js_object;
- if (!args_in_registers()) {
- __ ldr(function, MemOperand(sp, 1 * kPointerSize));
- __ ldr(object, MemOperand(sp, 0));
+ if (!HasArgsInRegisters()) {
+ __ ldr(object, MemOperand(sp, 1 * kPointerSize));
+ __ ldr(function, MemOperand(sp, 0));
}
// Check that the left hand is a JS object and load map.
- __ BranchOnSmi(object, &slow);
- __ IsObjectJSObjectType(object, map, scratch, &slow);
+ __ BranchOnSmi(object, &not_js_object);
+ __ IsObjectJSObjectType(object, map, scratch, &not_js_object);
// Look up the function and the map in the instanceof cache.
Label miss;
__ LoadRoot(ip, Heap::kInstanceofCacheFunctionRootIndex);
- __ cmp(object, ip);
+ __ cmp(function, ip);
__ b(ne, &miss);
__ LoadRoot(ip, Heap::kInstanceofCacheMapRootIndex);
__ cmp(map, ip);
__ b(ne, &miss);
- __ LoadRoot(function, Heap::kInstanceofCacheAnswerRootIndex);
- __ Ret(args_in_registers() ? 0 : 2);
+ __ LoadRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
+ __ Ret(HasArgsInRegisters() ? 0 : 2);
__ bind(&miss);
- __ TryGetFunctionPrototype(object, prototype, scratch, &slow);
+ __ TryGetFunctionPrototype(function, prototype, scratch, &slow);
// Check that the function prototype is a JS object.
__ BranchOnSmi(prototype, &slow);
__ IsObjectJSObjectType(prototype, scratch, scratch, &slow);
- __ StoreRoot(object, Heap::kInstanceofCacheFunctionRootIndex);
+ __ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
__ StoreRoot(map, Heap::kInstanceofCacheMapRootIndex);
// Register mapping: r3 is object map and r4 is function prototype.
@@ -2953,11 +2950,12 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
__ bind(&is_instance);
__ mov(r0, Operand(Smi::FromInt(0)));
__ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
- __ Ret(args_in_registers() ? 0 : 2);
+ __ Ret(HasArgsInRegisters() ? 0 : 2);
__ bind(&is_not_instance);
__ mov(r0, Operand(Smi::FromInt(1)));
- __ Ret(args_in_registers() ? 0 : 2);
+ __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
+ __ Ret(HasArgsInRegisters() ? 0 : 2);
Label object_not_null, object_not_null_or_smi;
__ bind(&not_js_object);
@@ -2971,22 +2969,25 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
__ cmp(scratch, Operand(Factory::null_value()));
__ b(ne, &object_not_null);
__ mov(r0, Operand(Smi::FromInt(1)));
- __ Ret(args_in_registers() ? 0 : 2);
+ __ Ret(HasArgsInRegisters() ? 0 : 2);
__ bind(&object_not_null);
// Smi values are not instances of anything.
__ BranchOnNotSmi(object, &object_not_null_or_smi);
__ mov(r0, Operand(Smi::FromInt(1)));
- __ Ret(args_in_registers() ? 0 : 2);
+ __ Ret(HasArgsInRegisters() ? 0 : 2);
__ bind(&object_not_null_or_smi);
// String values are not instances of anything.
__ IsObjectJSStringType(object, scratch, &slow);
__ mov(r0, Operand(Smi::FromInt(1)));
- __ Ret(args_in_registers() ? 0 : 2);
+ __ Ret(HasArgsInRegisters() ? 0 : 2);
// Slow-case. Tail call builtin.
__ bind(&slow);
+ if (HasArgsInRegisters()) {
+ __ Push(r0, r1);
+ }
__ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_JS);
}
@@ -3012,7 +3013,7 @@ void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
// through register r0. Use unsigned comparison to get negative
// check for free.
__ cmp(r1, r0);
- __ b(cs, &slow);
+ __ b(hs, &slow);
// Read the argument from the stack and return it.
__ sub(r3, r0, r1);
@@ -4911,8 +4912,7 @@ void ICCompareStub::GenerateHeapNumbers(MacroAssembler* masm) {
__ vldr(d1, r2, HeapNumber::kValueOffset);
// Compare operands
- __ vcmp(d0, d1);
- __ vmrs(pc); // Move vector status bits to normal status bits.
+ __ VFPCompareAndSetFlags(d0, d1);
// Don't base result on status bits when a NaN is involved.
__ b(vs, &unordered);
diff --git a/deps/v8/src/arm/code-stubs-arm.h b/deps/v8/src/arm/code-stubs-arm.h
index 8ffca773f7..9fa868798e 100644
--- a/deps/v8/src/arm/code-stubs-arm.h
+++ b/deps/v8/src/arm/code-stubs-arm.h
@@ -77,7 +77,7 @@ class GenericBinaryOpStub : public CodeStub {
rhs_(rhs),
constant_rhs_(constant_rhs),
specialized_on_rhs_(RhsIsOneWeWantToOptimizeFor(op, constant_rhs)),
- runtime_operands_type_(BinaryOpIC::DEFAULT),
+ runtime_operands_type_(BinaryOpIC::UNINIT_OR_SMI),
name_(NULL) { }
GenericBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info)
@@ -178,6 +178,10 @@ class GenericBinaryOpStub : public CodeStub {
return lhs_is_r0 ? r1 : r0;
}
+ bool HasSmiSmiFastPath() {
+ return op_ != Token::DIV;
+ }
+
bool ShouldGenerateSmiCode() {
return ((op_ != Token::DIV && op_ != Token::MOD) || specialized_on_rhs_) &&
runtime_operands_type_ != BinaryOpIC::HEAP_NUMBERS &&
@@ -437,43 +441,6 @@ class NumberToStringStub: public CodeStub {
};
-class RecordWriteStub : public CodeStub {
- public:
- RecordWriteStub(Register object, Register offset, Register scratch)
- : object_(object), offset_(offset), scratch_(scratch) { }
-
- void Generate(MacroAssembler* masm);
-
- private:
- Register object_;
- Register offset_;
- Register scratch_;
-
- // Minor key encoding in 12 bits. 4 bits for each of the three
- // registers (object, offset and scratch) OOOOAAAASSSS.
- class ScratchBits: public BitField<uint32_t, 0, 4> {};
- class OffsetBits: public BitField<uint32_t, 4, 4> {};
- class ObjectBits: public BitField<uint32_t, 8, 4> {};
-
- Major MajorKey() { return RecordWrite; }
-
- int MinorKey() {
- // Encode the registers.
- return ObjectBits::encode(object_.code()) |
- OffsetBits::encode(offset_.code()) |
- ScratchBits::encode(scratch_.code());
- }
-
-#ifdef DEBUG
- void Print() {
- PrintF("RecordWriteStub (object reg %d), (offset reg %d),"
- " (scratch reg %d)\n",
- object_.code(), offset_.code(), scratch_.code());
- }
-#endif
-};
-
-
// Enter C code from generated RegExp code in a way that allows
// the C code to fix the return address in case of a GC.
// Currently only needed on ARM.
diff --git a/deps/v8/src/arm/codegen-arm.cc b/deps/v8/src/arm/codegen-arm.cc
index 59bc14e72f..4a982f6e5c 100644
--- a/deps/v8/src/arm/codegen-arm.cc
+++ b/deps/v8/src/arm/codegen-arm.cc
@@ -596,7 +596,7 @@ void CodeGenerator::StoreArgumentsObject(bool initial) {
// When using lazy arguments allocation, we store the hole value
// as a sentinel indicating that the arguments object hasn't been
// allocated yet.
- frame_->EmitPushRoot(Heap::kTheHoleValueRootIndex);
+ frame_->EmitPushRoot(Heap::kArgumentsMarkerRootIndex);
} else {
frame_->SpillAll();
ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
@@ -623,7 +623,7 @@ void CodeGenerator::StoreArgumentsObject(bool initial) {
// has a local variable named 'arguments'.
LoadFromSlot(scope()->arguments()->AsSlot(), NOT_INSIDE_TYPEOF);
Register arguments = frame_->PopToRegister();
- __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ LoadRoot(ip, Heap::kArgumentsMarkerRootIndex);
__ cmp(arguments, ip);
done.Branch(ne);
}
@@ -1748,7 +1748,7 @@ void CodeGenerator::CallApplyLazy(Expression* applicand,
// named 'arguments' has been introduced.
JumpTarget slow;
Label done;
- __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ LoadRoot(ip, Heap::kArgumentsMarkerRootIndex);
__ cmp(ip, arguments_reg);
slow.Branch(ne);
@@ -3255,7 +3255,7 @@ void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
// If the loaded value is the sentinel that indicates that we
// haven't loaded the arguments object yet, we need to do it now.
JumpTarget exit;
- __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ LoadRoot(ip, Heap::kArgumentsMarkerRootIndex);
__ cmp(tos, ip);
exit.Branch(ne);
frame_->Drop();
@@ -4667,8 +4667,7 @@ void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
__ mov(scratch2, Operand(0x7FF00000));
__ mov(scratch1, Operand(0, RelocInfo::NONE));
__ vmov(d1, scratch1, scratch2); // Load infinity into d1.
- __ vcmp(d0, d1);
- __ vmrs(pc);
+ __ VFPCompareAndSetFlags(d0, d1);
runtime.Branch(eq); // d0 reached infinity.
__ vdiv(d0, d2, d0);
__ b(&allocate_return);
@@ -5618,12 +5617,10 @@ void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) {
// (or them and test against Smi mask.)
__ mov(tmp2, tmp1);
- RecordWriteStub recordWrite1(tmp1, index1, tmp3);
- __ CallStub(&recordWrite1);
-
- RecordWriteStub recordWrite2(tmp2, index2, tmp3);
- __ CallStub(&recordWrite2);
-
+ __ add(index1, index1, tmp1);
+ __ add(index2, index2, tmp1);
+ __ RecordWriteHelper(tmp1, index1, tmp3);
+ __ RecordWriteHelper(tmp2, index2, tmp3);
__ bind(&done);
deferred->BindExit();
diff --git a/deps/v8/src/arm/deoptimizer-arm.cc b/deps/v8/src/arm/deoptimizer-arm.cc
index 3917d6dfb7..8a53d1cbd6 100644
--- a/deps/v8/src/arm/deoptimizer-arm.cc
+++ b/deps/v8/src/arm/deoptimizer-arm.cc
@@ -55,8 +55,9 @@ void Deoptimizer::DeoptimizeFunction(JSFunction* function) {
SafepointTable table(function->code());
for (unsigned i = 0; i < table.length(); i++) {
unsigned pc_offset = table.GetPcOffset(i);
- int deoptimization_index = table.GetDeoptimizationIndex(i);
- int gap_code_size = table.GetGapCodeSize(i);
+ SafepointEntry safepoint_entry = table.GetEntry(i);
+ int deoptimization_index = safepoint_entry.deoptimization_index();
+ int gap_code_size = safepoint_entry.gap_code_size();
// Check that we did not shoot past next safepoint.
// TODO(srdjan): How do we guarantee that safepoint code does not
// overlap other safepoint patching code?
diff --git a/deps/v8/src/arm/full-codegen-arm.cc b/deps/v8/src/arm/full-codegen-arm.cc
index d254918318..338e39cbe8 100644
--- a/deps/v8/src/arm/full-codegen-arm.cc
+++ b/deps/v8/src/arm/full-codegen-arm.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -38,6 +38,8 @@
#include "scopes.h"
#include "stub-cache.h"
+#include "arm/code-stubs-arm.h"
+
namespace v8 {
namespace internal {
@@ -219,10 +221,17 @@ void FullCodeGenerator::EmitStackCheck(IterationStatement* stmt) {
__ b(hs, &ok);
StackCheckStub stub;
__ CallStub(&stub);
+ // Record a mapping of this PC offset to the OSR id. This is used to find
+ // the AST id from the unoptimized code in order to use it as a key into
+ // the deoptimization input data found in the optimized code.
+ RecordStackCheck(stmt->OsrEntryId());
+
__ bind(&ok);
PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
+ // Record a mapping of the OSR id to this PC. This is used if the OSR
+ // entry becomes the target of a bailout. We don't expect it to be, but
+ // we want it to work if it is.
PrepareForBailoutForId(stmt->OsrEntryId(), NO_REGISTERS);
- RecordStackCheck(stmt->OsrEntryId());
}
diff --git a/deps/v8/src/arm/ic-arm.cc b/deps/v8/src/arm/ic-arm.cc
index e5a1bae968..340bc1ef90 100644
--- a/deps/v8/src/arm/ic-arm.cc
+++ b/deps/v8/src/arm/ic-arm.cc
@@ -1591,7 +1591,7 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm,
__ and_(r1, r1, Operand(kBinary32ExponentMask >> kBinary32MantissaBits));
Label exponent_rebiased;
- __ teq(r1, Operand(0x00));
+ __ teq(r1, Operand(0x00, RelocInfo::NONE));
__ b(eq, &exponent_rebiased);
__ teq(r1, Operand(0xff));
diff --git a/deps/v8/src/arm/lithium-arm.cc b/deps/v8/src/arm/lithium-arm.cc
index e31d2e1d8a..df890ab55b 100644
--- a/deps/v8/src/arm/lithium-arm.cc
+++ b/deps/v8/src/arm/lithium-arm.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -93,32 +93,6 @@ void LLabel::PrintDataTo(StringStream* stream) const {
}
-bool LParallelMove::IsRedundant() const {
- for (int i = 0; i < move_operands_.length(); ++i) {
- if (!move_operands_[i].IsRedundant()) return false;
- }
- return true;
-}
-
-
-void LParallelMove::PrintDataTo(StringStream* stream) const {
- for (int i = move_operands_.length() - 1; i >= 0; --i) {
- if (!move_operands_[i].IsEliminated()) {
- LOperand* from = move_operands_[i].from();
- LOperand* to = move_operands_[i].to();
- if (from->Equals(to)) {
- to->PrintTo(stream);
- } else {
- to->PrintTo(stream);
- stream->Add(" = ");
- from->PrintTo(stream);
- }
- stream->Add("; ");
- }
- }
-}
-
-
bool LGap::IsRedundant() const {
for (int i = 0; i < 4; i++) {
if (parallel_moves_[i] != NULL && !parallel_moves_[i]->IsRedundant()) {
@@ -270,6 +244,11 @@ void LUnaryMathOperation::PrintDataTo(StringStream* stream) const {
}
+void LLoadContextSlot::PrintDataTo(StringStream* stream) {
+ stream->Add("(%d, %d)", context_chain_length(), slot_index());
+}
+
+
void LCallKeyed::PrintDataTo(StringStream* stream) const {
stream->Add("[r2] #%d / ", arity());
}
@@ -472,151 +451,6 @@ void LChunk::AddGapMove(int index, LOperand* from, LOperand* to) {
}
-class LGapNode: public ZoneObject {
- public:
- explicit LGapNode(LOperand* operand)
- : operand_(operand), resolved_(false), visited_id_(-1) { }
-
- LOperand* operand() const { return operand_; }
- bool IsResolved() const { return !IsAssigned() || resolved_; }
- void MarkResolved() {
- ASSERT(!IsResolved());
- resolved_ = true;
- }
- int visited_id() const { return visited_id_; }
- void set_visited_id(int id) {
- ASSERT(id > visited_id_);
- visited_id_ = id;
- }
-
- bool IsAssigned() const { return assigned_from_.is_set(); }
- LGapNode* assigned_from() const { return assigned_from_.get(); }
- void set_assigned_from(LGapNode* n) { assigned_from_.set(n); }
-
- private:
- LOperand* operand_;
- SetOncePointer<LGapNode> assigned_from_;
- bool resolved_;
- int visited_id_;
-};
-
-
-LGapResolver::LGapResolver(const ZoneList<LMoveOperands>* moves,
- LOperand* marker_operand)
- : nodes_(4),
- identified_cycles_(4),
- result_(4),
- marker_operand_(marker_operand),
- next_visited_id_(0) {
- for (int i = 0; i < moves->length(); ++i) {
- LMoveOperands move = moves->at(i);
- if (!move.IsRedundant()) RegisterMove(move);
- }
-}
-
-
-const ZoneList<LMoveOperands>* LGapResolver::ResolveInReverseOrder() {
- for (int i = 0; i < identified_cycles_.length(); ++i) {
- ResolveCycle(identified_cycles_[i]);
- }
-
- int unresolved_nodes;
- do {
- unresolved_nodes = 0;
- for (int j = 0; j < nodes_.length(); j++) {
- LGapNode* node = nodes_[j];
- if (!node->IsResolved() && node->assigned_from()->IsResolved()) {
- AddResultMove(node->assigned_from(), node);
- node->MarkResolved();
- }
- if (!node->IsResolved()) ++unresolved_nodes;
- }
- } while (unresolved_nodes > 0);
- return &result_;
-}
-
-
-void LGapResolver::AddResultMove(LGapNode* from, LGapNode* to) {
- AddResultMove(from->operand(), to->operand());
-}
-
-
-void LGapResolver::AddResultMove(LOperand* from, LOperand* to) {
- result_.Add(LMoveOperands(from, to));
-}
-
-
-void LGapResolver::ResolveCycle(LGapNode* start) {
- ZoneList<LOperand*> circle_operands(8);
- circle_operands.Add(marker_operand_);
- LGapNode* cur = start;
- do {
- cur->MarkResolved();
- circle_operands.Add(cur->operand());
- cur = cur->assigned_from();
- } while (cur != start);
- circle_operands.Add(marker_operand_);
-
- for (int i = circle_operands.length() - 1; i > 0; --i) {
- LOperand* from = circle_operands[i];
- LOperand* to = circle_operands[i - 1];
- AddResultMove(from, to);
- }
-}
-
-
-bool LGapResolver::CanReach(LGapNode* a, LGapNode* b, int visited_id) {
- ASSERT(a != b);
- LGapNode* cur = a;
- while (cur != b && cur->visited_id() != visited_id && cur->IsAssigned()) {
- cur->set_visited_id(visited_id);
- cur = cur->assigned_from();
- }
-
- return cur == b;
-}
-
-
-bool LGapResolver::CanReach(LGapNode* a, LGapNode* b) {
- ASSERT(a != b);
- return CanReach(a, b, next_visited_id_++);
-}
-
-
-void LGapResolver::RegisterMove(LMoveOperands move) {
- if (move.from()->IsConstantOperand()) {
- // Constant moves should be last in the machine code. Therefore add them
- // first to the result set.
- AddResultMove(move.from(), move.to());
- } else {
- LGapNode* from = LookupNode(move.from());
- LGapNode* to = LookupNode(move.to());
- if (to->IsAssigned() && to->assigned_from() == from) {
- move.Eliminate();
- return;
- }
- ASSERT(!to->IsAssigned());
- if (CanReach(from, to)) {
- // This introduces a circle. Save.
- identified_cycles_.Add(from);
- }
- to->set_assigned_from(from);
- }
-}
-
-
-LGapNode* LGapResolver::LookupNode(LOperand* operand) {
- for (int i = 0; i < nodes_.length(); ++i) {
- if (nodes_[i]->operand()->Equals(operand)) return nodes_[i];
- }
-
- // No node found => create a new one.
- LGapNode* result = new LGapNode(operand);
- nodes_.Add(result);
- return result;
-}
-
-
Handle<Object> LChunk::LookupLiteral(LConstantOperand* operand) const {
return HConstant::cast(graph_->LookupValue(operand->index()))->handle();
}
@@ -767,11 +601,6 @@ LInstruction* LChunkBuilder::DefineAsSpilled(LInstruction* instr, int index) {
}
-LInstruction* LChunkBuilder::DefineSameAsAny(LInstruction* instr) {
- return Define(instr, new LUnallocated(LUnallocated::SAME_AS_ANY_INPUT));
-}
-
-
LInstruction* LChunkBuilder::DefineSameAsFirst(LInstruction* instr) {
return Define(instr, new LUnallocated(LUnallocated::SAME_AS_FIRST_INPUT));
}
@@ -852,13 +681,6 @@ LInstruction* LChunkBuilder::Define(LInstruction* instr, LUnallocated* result) {
}
-LOperand* LChunkBuilder::Temp() {
- LUnallocated* operand = new LUnallocated(LUnallocated::NONE);
- allocator_->RecordTemporary(operand);
- return operand;
-}
-
-
LUnallocated* LChunkBuilder::TempRegister() {
LUnallocated* operand = new LUnallocated(LUnallocated::MUST_HAVE_REGISTER);
allocator_->RecordTemporary(operand);
@@ -1016,9 +838,6 @@ void LChunkBuilder::DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block) {
HInstruction* current = block->first();
int start = chunk_->instructions()->length();
while (current != NULL && !is_aborted()) {
- if (FLAG_trace_environment) {
- PrintF("Process instruction %d\n", current->id());
- }
// Code for constants in registers is generated lazily.
if (!current->EmitAtUses()) {
VisitInstruction(current);
@@ -1066,66 +885,13 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) {
}
-void LEnvironment::WriteTranslation(LCodeGen* cgen,
- Translation* translation) const {
- if (this == NULL) return;
-
- // The translation includes one command per value in the environment.
- int translation_size = values()->length();
- // The output frame height does not include the parameters.
- int height = translation_size - parameter_count();
-
- outer()->WriteTranslation(cgen, translation);
- int closure_id = cgen->DefineDeoptimizationLiteral(closure());
- translation->BeginFrame(ast_id(), closure_id, height);
- for (int i = 0; i < translation_size; ++i) {
- LOperand* value = values()->at(i);
- // spilled_registers_ and spilled_double_registers_ are either
- // both NULL or both set.
- if (spilled_registers_ != NULL && value != NULL) {
- if (value->IsRegister() &&
- spilled_registers_[value->index()] != NULL) {
- translation->MarkDuplicate();
- cgen->AddToTranslation(translation,
- spilled_registers_[value->index()],
- HasTaggedValueAt(i));
- } else if (value->IsDoubleRegister() &&
- spilled_double_registers_[value->index()] != NULL) {
- translation->MarkDuplicate();
- cgen->AddToTranslation(translation,
- spilled_double_registers_[value->index()],
- false);
- }
- }
-
- cgen->AddToTranslation(translation, value, HasTaggedValueAt(i));
- }
-}
-
-
-void LEnvironment::PrintTo(StringStream* stream) const {
- stream->Add("[id=%d|", ast_id());
- stream->Add("[parameters=%d|", parameter_count());
- stream->Add("[arguments_stack_height=%d|", arguments_stack_height());
- for (int i = 0; i < values_.length(); ++i) {
- if (i != 0) stream->Add(";");
- if (values_[i] == NULL) {
- stream->Add("[hole]");
- } else {
- values_[i]->PrintTo(stream);
- }
- }
- stream->Add("]");
-}
-
-
LEnvironment* LChunkBuilder::CreateEnvironment(HEnvironment* hydrogen_env) {
if (hydrogen_env == NULL) return NULL;
LEnvironment* outer = CreateEnvironment(hydrogen_env->outer());
int ast_id = hydrogen_env->ast_id();
ASSERT(ast_id != AstNode::kNoNumber);
- int value_count = hydrogen_env->values()->length();
+ int value_count = hydrogen_env->length();
LEnvironment* result = new LEnvironment(hydrogen_env->closure(),
ast_id,
hydrogen_env->parameter_count(),
@@ -1177,7 +943,6 @@ LInstruction* LChunkBuilder::DoBranch(HBranch* instr) {
return new LClassOfTestAndBranch(UseTempRegister(compare->value()),
TempRegister(),
- TempRegister(),
first_id,
second_id);
} else if (v->IsCompare()) {
@@ -1185,22 +950,21 @@ LInstruction* LChunkBuilder::DoBranch(HBranch* instr) {
Token::Value op = compare->token();
HValue* left = compare->left();
HValue* right = compare->right();
- if (left->representation().IsInteger32()) {
+ Representation r = compare->GetInputRepresentation();
+ if (r.IsInteger32()) {
+ ASSERT(left->representation().IsInteger32());
ASSERT(right->representation().IsInteger32());
- return new LCmpIDAndBranch(op,
- UseRegisterAtStart(left),
+ return new LCmpIDAndBranch(UseRegisterAtStart(left),
UseOrConstantAtStart(right),
first_id,
- second_id,
- false);
- } else if (left->representation().IsDouble()) {
+ second_id);
+ } else if (r.IsDouble()) {
+ ASSERT(left->representation().IsDouble());
ASSERT(right->representation().IsDouble());
- return new LCmpIDAndBranch(op,
- UseRegisterAtStart(left),
+ return new LCmpIDAndBranch(UseRegisterAtStart(left),
UseRegisterAtStart(right),
first_id,
- second_id,
- true);
+ second_id);
} else {
ASSERT(left->representation().IsTagged());
ASSERT(right->representation().IsTagged());
@@ -1225,7 +989,6 @@ LInstruction* LChunkBuilder::DoBranch(HBranch* instr) {
ASSERT(compare->value()->representation().IsTagged());
return new LHasInstanceTypeAndBranch(UseRegisterAtStart(compare->value()),
- TempRegister(),
first_id,
second_id);
} else if (v->IsHasCachedArrayIndex()) {
@@ -1238,11 +1001,7 @@ LInstruction* LChunkBuilder::DoBranch(HBranch* instr) {
HIsNull* compare = HIsNull::cast(v);
ASSERT(compare->value()->representation().IsTagged());
- // We only need a temp register for non-strict compare.
- LOperand* temp = compare->is_strict() ? NULL : TempRegister();
return new LIsNullAndBranch(UseRegisterAtStart(compare->value()),
- compare->is_strict(),
- temp,
first_id,
second_id);
} else if (v->IsIsObject()) {
@@ -1295,17 +1054,13 @@ LInstruction* LChunkBuilder::DoCompareMapAndBranch(
HCompareMapAndBranch* instr) {
ASSERT(instr->value()->representation().IsTagged());
LOperand* value = UseRegisterAtStart(instr->value());
- HBasicBlock* first = instr->FirstSuccessor();
- HBasicBlock* second = instr->SecondSuccessor();
- return new LCmpMapAndBranch(value,
- instr->map(),
- first->block_id(),
- second->block_id());
+ LOperand* temp = TempRegister();
+ return new LCmpMapAndBranch(value, temp);
}
LInstruction* LChunkBuilder::DoArgumentsLength(HArgumentsLength* length) {
- return DefineAsRegister(new LArgumentsLength(Use(length->value())));
+ return DefineAsRegister(new LArgumentsLength(UseRegister(length->value())));
}
@@ -1316,8 +1071,16 @@ LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) {
LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
LInstruction* result =
- new LInstanceOf(UseFixed(instr->left(), r1),
- UseFixed(instr->right(), r0));
+ new LInstanceOf(UseFixed(instr->left(), r0),
+ UseFixed(instr->right(), r1));
+ return MarkAsCall(DefineFixed(result, r0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal(
+ HInstanceOfKnownGlobal* instr) {
+ LInstruction* result =
+ new LInstanceOfKnownGlobal(UseFixed(instr->value(), r0));
return MarkAsCall(DefineFixed(result, r0), instr);
}
@@ -1362,7 +1125,8 @@ LInstruction* LChunkBuilder::DoCallConstantFunction(
LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
BuiltinFunctionId op = instr->op();
LOperand* input = UseRegisterAtStart(instr->value());
- LInstruction* result = new LUnaryMathOperation(input);
+ LOperand* temp = (op == kMathFloor) ? TempRegister() : NULL;
+ LInstruction* result = new LUnaryMathOperation(input, temp);
switch (op) {
case kMathAbs:
return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result)));
@@ -1370,6 +1134,9 @@ LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
return AssignEnvironment(DefineAsRegister(result));
case kMathSqrt:
return DefineSameAsFirst(result);
+ case kMathRound:
+ Abort("MathRound LUnaryMathOperation not implemented");
+ return NULL;
case kMathPowHalf:
Abort("MathPowHalf LUnaryMathOperation not implemented");
return NULL;
@@ -1476,12 +1243,15 @@ LInstruction* LChunkBuilder::DoDiv(HDiv* instr) {
if (instr->representation().IsDouble()) {
return DoArithmeticD(Token::DIV, instr);
} else if (instr->representation().IsInteger32()) {
- // The temporary operand is necessary to ensure that right is not allocated
- // into edx.
- FixedTemp(r1);
+ // TODO(1042) The fixed register allocation
+ // is needed because we call GenericBinaryOpStub from
+ // the generated code, which requires registers r0
+ // and r1 to be used. We should remove that
+ // when we provide a native implementation.
LOperand* value = UseFixed(instr->left(), r0);
- LOperand* divisor = UseRegister(instr->right());
- return AssignEnvironment(DefineFixed(new LDivI(value, divisor), r0));
+ LOperand* divisor = UseFixed(instr->right(), r1);
+ return AssignEnvironment(AssignPointerMap(
+ DefineFixed(new LDivI(value, divisor), r0)));
} else {
return DoArithmeticT(Token::DIV, instr);
}
@@ -1490,18 +1260,17 @@ LInstruction* LChunkBuilder::DoDiv(HDiv* instr) {
LInstruction* LChunkBuilder::DoMod(HMod* instr) {
if (instr->representation().IsInteger32()) {
+ // TODO(1042) The fixed register allocation
+ // is needed because we call GenericBinaryOpStub from
+ // the generated code, which requires registers r0
+ // and r1 to be used. We should remove that
+ // when we provide a native implementation.
ASSERT(instr->left()->representation().IsInteger32());
ASSERT(instr->right()->representation().IsInteger32());
- // The temporary operand is necessary to ensure that right is not allocated
- // into edx.
- FixedTemp(r1);
LOperand* value = UseFixed(instr->left(), r0);
- LOperand* divisor = UseRegister(instr->right());
- LInstruction* result = DefineFixed(new LModI(value, divisor), r1);
- if (instr->CheckFlag(HValue::kBailoutOnMinusZero) ||
- instr->CheckFlag(HValue::kCanBeDivByZero)) {
- result = AssignEnvironment(result);
- }
+ LOperand* divisor = UseFixed(instr->right(), r1);
+ LInstruction* result = DefineFixed(new LModI(value, divisor), r0);
+ result = AssignEnvironment(AssignPointerMap(result));
return result;
} else if (instr->representation().IsTagged()) {
return DoArithmeticT(Token::MOD, instr);
@@ -1587,17 +1356,22 @@ LInstruction* LChunkBuilder::DoPower(HPower* instr) {
LInstruction* LChunkBuilder::DoCompare(HCompare* instr) {
Token::Value op = instr->token();
- if (instr->left()->representation().IsInteger32()) {
+ Representation r = instr->GetInputRepresentation();
+ if (r.IsInteger32()) {
+ ASSERT(instr->left()->representation().IsInteger32());
ASSERT(instr->right()->representation().IsInteger32());
LOperand* left = UseRegisterAtStart(instr->left());
LOperand* right = UseOrConstantAtStart(instr->right());
- return DefineAsRegister(new LCmpID(op, left, right, false));
- } else if (instr->left()->representation().IsDouble()) {
+ return DefineAsRegister(new LCmpID(left, right));
+ } else if (r.IsDouble()) {
+ ASSERT(instr->left()->representation().IsDouble());
ASSERT(instr->right()->representation().IsDouble());
LOperand* left = UseRegisterAtStart(instr->left());
LOperand* right = UseRegisterAtStart(instr->right());
- return DefineAsRegister(new LCmpID(op, left, right, true));
+ return DefineAsRegister(new LCmpID(left, right));
} else {
+ ASSERT(instr->left()->representation().IsTagged());
+ ASSERT(instr->right()->representation().IsTagged());
bool reversed = (op == Token::GT || op == Token::LTE);
LOperand* left = UseFixed(instr->left(), reversed ? r0 : r1);
LOperand* right = UseFixed(instr->right(), reversed ? r1 : r0);
@@ -1620,8 +1394,7 @@ LInstruction* LChunkBuilder::DoIsNull(HIsNull* instr) {
ASSERT(instr->value()->representation().IsTagged());
LOperand* value = UseRegisterAtStart(instr->value());
- return DefineAsRegister(new LIsNull(value,
- instr->is_strict()));
+ return DefineAsRegister(new LIsNull(value));
}
@@ -1661,24 +1434,19 @@ LInstruction* LChunkBuilder::DoHasCachedArrayIndex(
LInstruction* LChunkBuilder::DoClassOfTest(HClassOfTest* instr) {
ASSERT(instr->value()->representation().IsTagged());
LOperand* value = UseTempRegister(instr->value());
-
- return DefineSameAsFirst(new LClassOfTest(value, TempRegister()));
+ return DefineSameAsFirst(new LClassOfTest(value));
}
-LInstruction* LChunkBuilder::DoArrayLength(HArrayLength* instr) {
- LOperand* array = NULL;
- LOperand* temporary = NULL;
+LInstruction* LChunkBuilder::DoJSArrayLength(HJSArrayLength* instr) {
+ LOperand* array = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new LJSArrayLength(array));
+}
- if (instr->value()->IsLoadElements()) {
- array = UseRegisterAtStart(instr->value());
- } else {
- array = UseRegister(instr->value());
- temporary = TempRegister();
- }
- LInstruction* result = new LArrayLength(array, temporary);
- return AssignEnvironment(DefineAsRegister(result));
+LInstruction* LChunkBuilder::DoFixedArrayLength(HFixedArrayLength* instr) {
+ LOperand* array = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new LFixedArrayLength(array));
}
@@ -1691,7 +1459,7 @@ LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) {
LInstruction* LChunkBuilder::DoBoundsCheck(HBoundsCheck* instr) {
return AssignEnvironment(new LBoundsCheck(UseRegisterAtStart(instr->index()),
- Use(instr->length())));
+ UseRegister(instr->length())));
}
@@ -1771,18 +1539,15 @@ LInstruction* LChunkBuilder::DoCheckNonSmi(HCheckNonSmi* instr) {
LInstruction* LChunkBuilder::DoCheckInstanceType(HCheckInstanceType* instr) {
LOperand* value = UseRegisterAtStart(instr->value());
- LOperand* temp = TempRegister();
- LInstruction* result = new LCheckInstanceType(value, temp);
+ LInstruction* result = new LCheckInstanceType(value);
return AssignEnvironment(result);
}
LInstruction* LChunkBuilder::DoCheckPrototypeMaps(HCheckPrototypeMaps* instr) {
- LOperand* temp = TempRegister();
- LInstruction* result =
- new LCheckPrototypeMaps(temp,
- instr->holder(),
- instr->receiver_map());
+ LOperand* temp1 = TempRegister();
+ LOperand* temp2 = TempRegister();
+ LInstruction* result = new LCheckPrototypeMaps(temp1, temp2);
return AssignEnvironment(result);
}
@@ -1822,7 +1587,7 @@ LInstruction* LChunkBuilder::DoConstant(HConstant* instr) {
} else if (r.IsTagged()) {
return DefineAsRegister(new LConstantT(instr->handle()));
} else {
- Abort("unsupported constant of type double");
+ UNREACHABLE();
return NULL;
}
}
@@ -1841,6 +1606,11 @@ LInstruction* LChunkBuilder::DoStoreGlobal(HStoreGlobal* instr) {
}
+LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) {
+ return DefineAsRegister(new LLoadContextSlot);
+}
+
+
LInstruction* LChunkBuilder::DoLoadNamedField(HLoadNamedField* instr) {
return DefineAsRegister(
new LLoadNamedField(UseRegisterAtStart(instr->object())));
@@ -1854,6 +1624,13 @@ LInstruction* LChunkBuilder::DoLoadNamedGeneric(HLoadNamedGeneric* instr) {
}
+LInstruction* LChunkBuilder::DoLoadFunctionPrototype(
+ HLoadFunctionPrototype* instr) {
+ return AssignEnvironment(DefineAsRegister(
+ new LLoadFunctionPrototype(UseRegister(instr->function()))));
+}
+
+
LInstruction* LChunkBuilder::DoLoadElements(HLoadElements* instr) {
LOperand* input = UseRegisterAtStart(instr->value());
return DefineSameAsFirst(new LLoadElements(input));
@@ -1862,23 +1639,12 @@ LInstruction* LChunkBuilder::DoLoadElements(HLoadElements* instr) {
LInstruction* LChunkBuilder::DoLoadKeyedFastElement(
HLoadKeyedFastElement* instr) {
- Representation r = instr->representation();
- LOperand* obj = UseRegisterAtStart(instr->object());
+ ASSERT(instr->representation().IsTagged());
ASSERT(instr->key()->representation().IsInteger32());
+ LOperand* obj = UseRegisterAtStart(instr->object());
LOperand* key = UseRegisterAtStart(instr->key());
- LOperand* load_result = NULL;
- // Double needs an extra temp, because the result is converted from heap
- // number to a double register.
- if (r.IsDouble()) load_result = TempRegister();
- LInstruction* result = new LLoadKeyedFastElement(obj,
- key,
- load_result);
- if (r.IsDouble()) {
- result = DefineAsRegister(result);
- } else {
- result = DefineSameAsFirst(result);
- }
- return AssignEnvironment(result);
+ LInstruction* result = new LLoadKeyedFastElement(obj, key);
+ return AssignEnvironment(DefineSameAsFirst(result));
}
@@ -1925,7 +1691,7 @@ LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) {
LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
- bool needs_write_barrier = !instr->value()->type().IsSmi();
+ bool needs_write_barrier = instr->NeedsWriteBarrier();
LOperand* obj = needs_write_barrier
? UseTempRegister(instr->object())
@@ -1935,19 +1701,7 @@ LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
? UseTempRegister(instr->value())
: UseRegister(instr->value());
- // We only need a scratch register if we have a write barrier or we
- // have a store into the properties array (not in-object-property).
- LOperand* temp = (!instr->is_in_object() || needs_write_barrier)
- ? TempRegister() : NULL;
-
- return new LStoreNamedField(obj,
- instr->name(),
- val,
- instr->is_in_object(),
- instr->offset(),
- temp,
- needs_write_barrier,
- instr->transition());
+ return new LStoreNamedField(obj, val);
}
@@ -1955,7 +1709,7 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
LOperand* obj = UseFixed(instr->object(), r1);
LOperand* val = UseFixed(instr->value(), r0);
- LInstruction* result = new LStoreNamedGeneric(obj, instr->name(), val);
+ LInstruction* result = new LStoreNamedGeneric(obj, val);
return MarkAsCall(result, instr);
}
@@ -1981,8 +1735,9 @@ LInstruction* LChunkBuilder::DoFunctionLiteral(HFunctionLiteral* instr) {
LInstruction* LChunkBuilder::DoDeleteProperty(HDeleteProperty* instr) {
- LInstruction* result = new LDeleteProperty(Use(instr->object()),
- UseOrConstant(instr->key()));
+ LOperand* object = UseRegisterAtStart(instr->object());
+ LOperand* key = UseRegisterAtStart(instr->key());
+ LInstruction* result = new LDeleteProperty(object, key);
return MarkAsCall(DefineFixed(result, r0), instr);
}
@@ -2022,14 +1777,14 @@ LInstruction* LChunkBuilder::DoArgumentsObject(HArgumentsObject* instr) {
LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) {
LOperand* arguments = UseRegister(instr->arguments());
LOperand* length = UseTempRegister(instr->length());
- LOperand* index = Use(instr->index());
+ LOperand* index = UseRegister(instr->index());
LInstruction* result = new LAccessArgumentsAt(arguments, length, index);
return DefineAsRegister(AssignEnvironment(result));
}
LInstruction* LChunkBuilder::DoTypeof(HTypeof* instr) {
- LInstruction* result = new LTypeof(Use(instr->value()));
+ LInstruction* result = new LTypeof(UseRegisterAtStart(instr->value()));
return MarkAsCall(DefineFixed(result, r0), instr);
}
@@ -2054,13 +1809,7 @@ LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
}
}
- if (FLAG_trace_environment) {
- PrintF("Reconstructed environment ast_id=%d, instr_id=%d\n",
- instr->ast_id(),
- instr->id());
- env->PrintToStd();
- }
- ASSERT(env->values()->length() == instr->environment_height());
+ ASSERT(env->length() == instr->environment_length());
// If there is an instruction pending deoptimization environment create a
// lazy bailout instruction to capture the environment.
@@ -2102,21 +1851,4 @@ LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) {
}
-void LPointerMap::RecordPointer(LOperand* op) {
- // Do not record arguments as pointers.
- if (op->IsStackSlot() && op->index() < 0) return;
- ASSERT(!op->IsDoubleRegister() && !op->IsDoubleStackSlot());
- pointer_operands_.Add(op);
-}
-
-
-void LPointerMap::PrintTo(StringStream* stream) const {
- stream->Add("{");
- for (int i = 0; i < pointer_operands_.length(); ++i) {
- if (i != 0) stream->Add(";");
- pointer_operands_[i]->PrintTo(stream);
- }
- stream->Add("} @%d", position());
-}
-
} } // namespace v8::internal
diff --git a/deps/v8/src/arm/lithium-arm.h b/deps/v8/src/arm/lithium-arm.h
index 41209c67d5..c6b89a5046 100644
--- a/deps/v8/src/arm/lithium-arm.h
+++ b/deps/v8/src/arm/lithium-arm.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -30,6 +30,7 @@
#include "hydrogen.h"
#include "lithium-allocator.h"
+#include "lithium.h"
#include "safepoint-table.h"
namespace v8 {
@@ -37,8 +38,6 @@ namespace internal {
// Forward declarations.
class LCodeGen;
-class LEnvironment;
-class Translation;
// Type hierarchy:
@@ -62,6 +61,7 @@ class Translation;
// LDivI
// LInstanceOf
// LInstanceOfAndBranch
+// LInstanceOfKnownGlobal
// LLoadKeyedFastElement
// LLoadKeyedGeneric
// LModI
@@ -76,6 +76,7 @@ class Translation;
// LCallNamed
// LCallRuntime
// LCallStub
+// LCheckPrototypeMaps
// LConstant
// LConstantD
// LConstantI
@@ -85,7 +86,8 @@ class Translation;
// LGlobalObject
// LGlobalReceiver
// LLabel
-// LLayzBailout
+// LLazyBailout
+// LLoadContextSlot
// LLoadGlobal
// LMaterializedLiteral
// LArrayLiteral
@@ -101,14 +103,14 @@ class Translation;
// LStoreNamedField
// LStoreNamedGeneric
// LUnaryOperation
-// LArrayLength
+// LJSArrayLength
+// LFixedArrayLength
// LBitNotI
// LBranch
// LCallNew
// LCheckFunction
// LCheckInstanceType
// LCheckMap
-// LCheckPrototypeMaps
// LCheckSmi
// LClassOfTest
// LClassOfTestAndBranch
@@ -127,6 +129,7 @@ class Translation;
// LIsSmiAndBranch
// LLoadNamedField
// LLoadNamedGeneric
+// LLoadFunctionPrototype
// LNumberTagD
// LNumberTagI
// LPushArgument
@@ -161,7 +164,6 @@ class Translation;
V(ArgumentsLength) \
V(ArithmeticD) \
V(ArithmeticT) \
- V(ArrayLength) \
V(ArrayLiteral) \
V(BitI) \
V(BitNotI) \
@@ -195,6 +197,7 @@ class Translation;
V(Deoptimize) \
V(DivI) \
V(DoubleToI) \
+ V(FixedArrayLength) \
V(FunctionLiteral) \
V(Gap) \
V(GlobalObject) \
@@ -202,6 +205,7 @@ class Translation;
V(Goto) \
V(InstanceOf) \
V(InstanceOfAndBranch) \
+ V(InstanceOfKnownGlobal) \
V(Integer32ToDouble) \
V(IsNull) \
V(IsNullAndBranch) \
@@ -209,6 +213,7 @@ class Translation;
V(IsObjectAndBranch) \
V(IsSmi) \
V(IsSmiAndBranch) \
+ V(JSArrayLength) \
V(HasInstanceType) \
V(HasInstanceTypeAndBranch) \
V(HasCachedArrayIndex) \
@@ -217,12 +222,14 @@ class Translation;
V(ClassOfTestAndBranch) \
V(Label) \
V(LazyBailout) \
+ V(LoadContextSlot) \
V(LoadElements) \
V(LoadGlobal) \
V(LoadKeyedFastElement) \
V(LoadKeyedGeneric) \
V(LoadNamedField) \
V(LoadNamedGeneric) \
+ V(LoadFunctionPrototype) \
V(ModI) \
V(MulI) \
V(NumberTagD) \
@@ -325,53 +332,6 @@ class LInstruction: public ZoneObject {
};
-class LGapNode;
-
-
-class LGapResolver BASE_EMBEDDED {
- public:
- LGapResolver(const ZoneList<LMoveOperands>* moves, LOperand* marker_operand);
- const ZoneList<LMoveOperands>* ResolveInReverseOrder();
-
- private:
- LGapNode* LookupNode(LOperand* operand);
- bool CanReach(LGapNode* a, LGapNode* b, int visited_id);
- bool CanReach(LGapNode* a, LGapNode* b);
- void RegisterMove(LMoveOperands move);
- void AddResultMove(LOperand* from, LOperand* to);
- void AddResultMove(LGapNode* from, LGapNode* to);
- void ResolveCycle(LGapNode* start);
-
- ZoneList<LGapNode*> nodes_;
- ZoneList<LGapNode*> identified_cycles_;
- ZoneList<LMoveOperands> result_;
- LOperand* marker_operand_;
- int next_visited_id_;
- int bailout_after_ast_id_;
-};
-
-
-class LParallelMove : public ZoneObject {
- public:
- LParallelMove() : move_operands_(4) { }
-
- void AddMove(LOperand* from, LOperand* to) {
- move_operands_.Add(LMoveOperands(from, to));
- }
-
- bool IsRedundant() const;
-
- const ZoneList<LMoveOperands>* move_operands() const {
- return &move_operands_;
- }
-
- void PrintDataTo(StringStream* stream) const;
-
- private:
- ZoneList<LMoveOperands> move_operands_;
-};
-
-
class LGap: public LInstruction {
public:
explicit LGap(HBasicBlock* block)
@@ -485,6 +445,10 @@ class LCallStub: public LInstruction {
public:
DECLARE_CONCRETE_INSTRUCTION(CallStub, "call-stub")
DECLARE_HYDROGEN_ACCESSOR(CallStub)
+
+ TranscendentalCache::Type transcendental_type() {
+ return hydrogen()->transcendental_type();
+ }
};
@@ -621,29 +585,26 @@ class LMulI: public LBinaryOperation {
class LCmpID: public LBinaryOperation {
public:
- LCmpID(Token::Value op, LOperand* left, LOperand* right, bool is_double)
- : LBinaryOperation(left, right), op_(op), is_double_(is_double) { }
+ LCmpID(LOperand* left, LOperand* right)
+ : LBinaryOperation(left, right) { }
- Token::Value op() const { return op_; }
- bool is_double() const { return is_double_; }
+ Token::Value op() const { return hydrogen()->token(); }
+ bool is_double() const {
+ return hydrogen()->GetInputRepresentation().IsDouble();
+ }
DECLARE_CONCRETE_INSTRUCTION(CmpID, "cmp-id")
-
- private:
- Token::Value op_;
- bool is_double_;
+ DECLARE_HYDROGEN_ACCESSOR(Compare)
};
class LCmpIDAndBranch: public LCmpID {
public:
- LCmpIDAndBranch(Token::Value op,
- LOperand* left,
+ LCmpIDAndBranch(LOperand* left,
LOperand* right,
int true_block_id,
- int false_block_id,
- bool is_double)
- : LCmpID(op, left, right, is_double),
+ int false_block_id)
+ : LCmpID(left, right),
true_block_id_(true_block_id),
false_block_id_(false_block_id) { }
@@ -662,14 +623,18 @@ class LCmpIDAndBranch: public LCmpID {
class LUnaryMathOperation: public LUnaryOperation {
public:
- explicit LUnaryMathOperation(LOperand* value)
- : LUnaryOperation(value) { }
+ explicit LUnaryMathOperation(LOperand* value, LOperand* temp)
+ : LUnaryOperation(value), temp_(temp) { }
DECLARE_CONCRETE_INSTRUCTION(UnaryMathOperation, "unary-math-operation")
DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation)
virtual void PrintDataTo(StringStream* stream) const;
BuiltinFunctionId op() const { return hydrogen()->op(); }
+ LOperand* temp() const { return temp_; }
+
+ private:
+ LOperand* temp_;
};
@@ -706,27 +671,21 @@ class LCmpJSObjectEqAndBranch: public LCmpJSObjectEq {
class LIsNull: public LUnaryOperation {
public:
- LIsNull(LOperand* value, bool is_strict)
- : LUnaryOperation(value), is_strict_(is_strict) {}
+ explicit LIsNull(LOperand* value) : LUnaryOperation(value) {}
DECLARE_CONCRETE_INSTRUCTION(IsNull, "is-null")
+ DECLARE_HYDROGEN_ACCESSOR(IsNull);
- bool is_strict() const { return is_strict_; }
-
- private:
- bool is_strict_;
+ bool is_strict() const { return hydrogen()->is_strict(); }
};
class LIsNullAndBranch: public LIsNull {
public:
LIsNullAndBranch(LOperand* value,
- bool is_strict,
- LOperand* temp,
int true_block_id,
int false_block_id)
- : LIsNull(value, is_strict),
- temp_(temp),
+ : LIsNull(value),
true_block_id_(true_block_id),
false_block_id_(false_block_id) { }
@@ -737,10 +696,7 @@ class LIsNullAndBranch: public LIsNull {
int true_block_id() const { return true_block_id_; }
int false_block_id() const { return false_block_id_; }
- LOperand* temp() const { return temp_; }
-
private:
- LOperand* temp_;
int true_block_id_;
int false_block_id_;
};
@@ -835,11 +791,9 @@ class LHasInstanceType: public LUnaryOperation {
class LHasInstanceTypeAndBranch: public LHasInstanceType {
public:
LHasInstanceTypeAndBranch(LOperand* value,
- LOperand* temporary,
int true_block_id,
int false_block_id)
: LHasInstanceType(value),
- temp_(temporary),
true_block_id_(true_block_id),
false_block_id_(false_block_id) { }
@@ -851,10 +805,7 @@ class LHasInstanceTypeAndBranch: public LHasInstanceType {
int true_block_id() const { return true_block_id_; }
int false_block_id() const { return false_block_id_; }
- LOperand* temp() { return temp_; }
-
private:
- LOperand* temp_;
int true_block_id_;
int false_block_id_;
};
@@ -894,18 +845,12 @@ class LHasCachedArrayIndexAndBranch: public LHasCachedArrayIndex {
class LClassOfTest: public LUnaryOperation {
public:
- LClassOfTest(LOperand* value, LOperand* temp)
- : LUnaryOperation(value), temporary_(temp) {}
+ explicit LClassOfTest(LOperand* value) : LUnaryOperation(value) {}
DECLARE_CONCRETE_INSTRUCTION(ClassOfTest, "class-of-test")
DECLARE_HYDROGEN_ACCESSOR(ClassOfTest)
virtual void PrintDataTo(StringStream* stream) const;
-
- LOperand* temporary() { return temporary_; }
-
- private:
- LOperand *temporary_;
};
@@ -913,11 +858,10 @@ class LClassOfTestAndBranch: public LClassOfTest {
public:
LClassOfTestAndBranch(LOperand* value,
LOperand* temporary,
- LOperand* temporary2,
int true_block_id,
int false_block_id)
- : LClassOfTest(value, temporary),
- temporary2_(temporary2),
+ : LClassOfTest(value),
+ temporary_(temporary),
true_block_id_(true_block_id),
false_block_id_(false_block_id) { }
@@ -928,10 +872,10 @@ class LClassOfTestAndBranch: public LClassOfTest {
int true_block_id() const { return true_block_id_; }
int false_block_id() const { return false_block_id_; }
- LOperand* temporary2() { return temporary2_; }
+ LOperand* temporary() { return temporary_; }
private:
- LOperand* temporary2_;
+ LOperand* temporary_;
int true_block_id_;
int false_block_id_;
};
@@ -999,6 +943,19 @@ class LInstanceOfAndBranch: public LInstanceOf {
};
+class LInstanceOfKnownGlobal: public LUnaryOperation {
+ public:
+ explicit LInstanceOfKnownGlobal(LOperand* left)
+ : LUnaryOperation(left) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal,
+ "instance-of-known-global")
+ DECLARE_HYDROGEN_ACCESSOR(InstanceOfKnownGlobal)
+
+ Handle<JSFunction> function() const { return hydrogen()->function(); }
+};
+
+
class LBoundsCheck: public LBinaryOperation {
public:
LBoundsCheck(LOperand* index, LOperand* length)
@@ -1117,42 +1074,43 @@ class LBranch: public LUnaryOperation {
class LCmpMapAndBranch: public LUnaryOperation {
public:
- LCmpMapAndBranch(LOperand* value,
- Handle<Map> map,
- int true_block_id,
- int false_block_id)
- : LUnaryOperation(value),
- map_(map),
- true_block_id_(true_block_id),
- false_block_id_(false_block_id) { }
+ LCmpMapAndBranch(LOperand* value, LOperand* temp)
+ : LUnaryOperation(value), temp_(temp) { }
DECLARE_CONCRETE_INSTRUCTION(CmpMapAndBranch, "cmp-map-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareMapAndBranch)
virtual bool IsControl() const { return true; }
- Handle<Map> map() const { return map_; }
- int true_block_id() const { return true_block_id_; }
- int false_block_id() const { return false_block_id_; }
+ LOperand* temp() const { return temp_; }
+ Handle<Map> map() const { return hydrogen()->map(); }
+ int true_block_id() const {
+ return hydrogen()->true_destination()->block_id();
+ }
+ int false_block_id() const {
+ return hydrogen()->false_destination()->block_id();
+ }
private:
- Handle<Map> map_;
- int true_block_id_;
- int false_block_id_;
+ LOperand* temp_;
};
-class LArrayLength: public LUnaryOperation {
+class LJSArrayLength: public LUnaryOperation {
public:
- LArrayLength(LOperand* input, LOperand* temporary)
- : LUnaryOperation(input), temporary_(temporary) { }
+ explicit LJSArrayLength(LOperand* input) : LUnaryOperation(input) { }
- LOperand* temporary() const { return temporary_; }
+ DECLARE_CONCRETE_INSTRUCTION(JSArrayLength, "js-array-length")
+ DECLARE_HYDROGEN_ACCESSOR(JSArrayLength)
+};
- DECLARE_CONCRETE_INSTRUCTION(ArrayLength, "array-length")
- DECLARE_HYDROGEN_ACCESSOR(ArrayLength)
- private:
- LOperand* temporary_;
+class LFixedArrayLength: public LUnaryOperation {
+ public:
+ explicit LFixedArrayLength(LOperand* input) : LUnaryOperation(input) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(FixedArrayLength, "fixed-array-length")
+ DECLARE_HYDROGEN_ACCESSOR(FixedArrayLength)
};
@@ -1256,6 +1214,18 @@ class LLoadNamedGeneric: public LUnaryOperation {
};
+class LLoadFunctionPrototype: public LUnaryOperation {
+ public:
+ explicit LLoadFunctionPrototype(LOperand* function)
+ : LUnaryOperation(function) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadFunctionPrototype, "load-function-prototype")
+ DECLARE_HYDROGEN_ACCESSOR(LoadFunctionPrototype)
+
+ LOperand* function() const { return input(); }
+};
+
+
class LLoadElements: public LUnaryOperation {
public:
explicit LLoadElements(LOperand* obj) : LUnaryOperation(obj) { }
@@ -1266,21 +1236,14 @@ class LLoadElements: public LUnaryOperation {
class LLoadKeyedFastElement: public LBinaryOperation {
public:
- LLoadKeyedFastElement(LOperand* elements,
- LOperand* key,
- LOperand* load_result)
- : LBinaryOperation(elements, key),
- load_result_(load_result) { }
+ LLoadKeyedFastElement(LOperand* elements, LOperand* key)
+ : LBinaryOperation(elements, key) { }
DECLARE_CONCRETE_INSTRUCTION(LoadKeyedFastElement, "load-keyed-fast-element")
DECLARE_HYDROGEN_ACCESSOR(LoadKeyedFastElement)
LOperand* elements() const { return left(); }
LOperand* key() const { return right(); }
- LOperand* load_result() const { return load_result_; }
-
- private:
- LOperand* load_result_;
};
@@ -1312,6 +1275,20 @@ class LStoreGlobal: public LUnaryOperation {
};
+class LLoadContextSlot: public LInstruction {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(LoadContextSlot, "load-context-slot")
+ DECLARE_HYDROGEN_ACCESSOR(LoadContextSlot)
+
+ int context_chain_length() const {
+ return hydrogen()->context_chain_length();
+ }
+ int slot_index() const { return hydrogen()->slot_index(); }
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
class LPushArgument: public LUnaryOperation {
public:
explicit LPushArgument(LOperand* argument) : LUnaryOperation(argument) {}
@@ -1516,67 +1493,46 @@ class LSmiUntag: public LUnaryOperation {
class LStoreNamed: public LInstruction {
public:
- LStoreNamed(LOperand* obj, Handle<Object> name, LOperand* val)
- : object_(obj), name_(name), value_(val) { }
+ LStoreNamed(LOperand* obj, LOperand* val)
+ : object_(obj), value_(val) { }
DECLARE_INSTRUCTION(StoreNamed)
+ DECLARE_HYDROGEN_ACCESSOR(StoreNamed)
virtual void PrintDataTo(StringStream* stream) const;
LOperand* object() const { return object_; }
- Handle<Object> name() const { return name_; }
+ Handle<Object> name() const { return hydrogen()->name(); }
LOperand* value() const { return value_; }
private:
LOperand* object_;
- Handle<Object> name_;
LOperand* value_;
};
class LStoreNamedField: public LStoreNamed {
public:
- LStoreNamedField(LOperand* obj,
- Handle<Object> name,
- LOperand* val,
- bool in_object,
- int offset,
- LOperand* temp,
- bool needs_write_barrier,
- Handle<Map> transition)
- : LStoreNamed(obj, name, val),
- is_in_object_(in_object),
- offset_(offset),
- temp_(temp),
- needs_write_barrier_(needs_write_barrier),
- transition_(transition) { }
+ LStoreNamedField(LOperand* obj, LOperand* val)
+ : LStoreNamed(obj, val) { }
DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field")
+ DECLARE_HYDROGEN_ACCESSOR(StoreNamedField)
- bool is_in_object() { return is_in_object_; }
- int offset() { return offset_; }
- LOperand* temp() { return temp_; }
- bool needs_write_barrier() { return needs_write_barrier_; }
- Handle<Map> transition() const { return transition_; }
- void set_transition(Handle<Map> map) { transition_ = map; }
-
- private:
- bool is_in_object_;
- int offset_;
- LOperand* temp_;
- bool needs_write_barrier_;
- Handle<Map> transition_;
+ bool is_in_object() { return hydrogen()->is_in_object(); }
+ int offset() { return hydrogen()->offset(); }
+ bool needs_write_barrier() { return hydrogen()->NeedsWriteBarrier(); }
+ Handle<Map> transition() { return hydrogen()->transition(); }
};
class LStoreNamedGeneric: public LStoreNamed {
public:
- LStoreNamedGeneric(LOperand* obj,
- Handle<Object> name,
- LOperand* val)
- : LStoreNamed(obj, name, val) { }
+ LStoreNamedGeneric(LOperand* obj, LOperand* val)
+ : LStoreNamed(obj, val) { }
DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric, "store-named-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreNamedGeneric)
};
@@ -1631,8 +1587,7 @@ class LCheckFunction: public LUnaryOperation {
class LCheckInstanceType: public LUnaryOperation {
public:
- LCheckInstanceType(LOperand* use, LOperand* temp)
- : LUnaryOperation(use), temp_(temp) { }
+ explicit LCheckInstanceType(LOperand* use) : LUnaryOperation(use) { }
DECLARE_CONCRETE_INSTRUCTION(CheckInstanceType, "check-instance-type")
DECLARE_HYDROGEN_ACCESSOR(CheckInstanceType)
@@ -1655,23 +1610,21 @@ class LCheckMap: public LUnaryOperation {
class LCheckPrototypeMaps: public LInstruction {
public:
- LCheckPrototypeMaps(LOperand* temp,
- Handle<JSObject> holder,
- Handle<Map> receiver_map)
- : temp_(temp),
- holder_(holder),
- receiver_map_(receiver_map) { }
+ LCheckPrototypeMaps(LOperand* temp1, LOperand* temp2)
+ : temp1_(temp1), temp2_(temp2) { }
DECLARE_CONCRETE_INSTRUCTION(CheckPrototypeMaps, "check-prototype-maps")
+ DECLARE_HYDROGEN_ACCESSOR(CheckPrototypeMaps)
- LOperand* temp() const { return temp_; }
- Handle<JSObject> holder() const { return holder_; }
- Handle<Map> receiver_map() const { return receiver_map_; }
+ Handle<JSObject> prototype() const { return hydrogen()->prototype(); }
+ Handle<JSObject> holder() const { return hydrogen()->holder(); }
+
+ LOperand* temp1() const { return temp1_; }
+ LOperand* temp2() const { return temp2_; }
private:
- LOperand* temp_;
- Handle<JSObject> holder_;
- Handle<Map> receiver_map_;
+ LOperand* temp1_;
+ LOperand* temp2_;
};
@@ -1811,108 +1764,6 @@ class LStackCheck: public LInstruction {
};
-class LPointerMap: public ZoneObject {
- public:
- explicit LPointerMap(int position)
- : pointer_operands_(8), position_(position), lithium_position_(-1) { }
-
- const ZoneList<LOperand*>* operands() const { return &pointer_operands_; }
- int position() const { return position_; }
- int lithium_position() const { return lithium_position_; }
-
- void set_lithium_position(int pos) {
- ASSERT(lithium_position_ == -1);
- lithium_position_ = pos;
- }
-
- void RecordPointer(LOperand* op);
- void PrintTo(StringStream* stream) const;
-
- private:
- ZoneList<LOperand*> pointer_operands_;
- int position_;
- int lithium_position_;
-};
-
-
-class LEnvironment: public ZoneObject {
- public:
- LEnvironment(Handle<JSFunction> closure,
- int ast_id,
- int parameter_count,
- int argument_count,
- int value_count,
- LEnvironment* outer)
- : closure_(closure),
- arguments_stack_height_(argument_count),
- deoptimization_index_(Safepoint::kNoDeoptimizationIndex),
- translation_index_(-1),
- ast_id_(ast_id),
- parameter_count_(parameter_count),
- values_(value_count),
- representations_(value_count),
- spilled_registers_(NULL),
- spilled_double_registers_(NULL),
- outer_(outer) {
- }
-
- Handle<JSFunction> closure() const { return closure_; }
- int arguments_stack_height() const { return arguments_stack_height_; }
- int deoptimization_index() const { return deoptimization_index_; }
- int translation_index() const { return translation_index_; }
- int ast_id() const { return ast_id_; }
- int parameter_count() const { return parameter_count_; }
- const ZoneList<LOperand*>* values() const { return &values_; }
- LEnvironment* outer() const { return outer_; }
-
- void AddValue(LOperand* operand, Representation representation) {
- values_.Add(operand);
- representations_.Add(representation);
- }
-
- bool HasTaggedValueAt(int index) const {
- return representations_[index].IsTagged();
- }
-
- void Register(int deoptimization_index, int translation_index) {
- ASSERT(!HasBeenRegistered());
- deoptimization_index_ = deoptimization_index;
- translation_index_ = translation_index;
- }
- bool HasBeenRegistered() const {
- return deoptimization_index_ != Safepoint::kNoDeoptimizationIndex;
- }
-
- void SetSpilledRegisters(LOperand** registers,
- LOperand** double_registers) {
- spilled_registers_ = registers;
- spilled_double_registers_ = double_registers;
- }
-
- // Emit frame translation commands for this environment.
- void WriteTranslation(LCodeGen* cgen, Translation* translation) const;
-
- void PrintTo(StringStream* stream) const;
-
- private:
- Handle<JSFunction> closure_;
- int arguments_stack_height_;
- int deoptimization_index_;
- int translation_index_;
- int ast_id_;
- int parameter_count_;
- ZoneList<LOperand*> values_;
- ZoneList<Representation> representations_;
-
- // Allocation index indexed arrays of spill slot operands for registers
- // that are also in spill slots at an OSR entry. NULL for environments
- // that do not correspond to an OSR entry.
- LOperand** spilled_registers_;
- LOperand** spilled_double_registers_;
-
- LEnvironment* outer_;
-};
-
class LChunkBuilder;
class LChunk: public ZoneObject {
public:
@@ -2051,7 +1902,6 @@ class LChunkBuilder BASE_EMBEDDED {
LInstruction* Define(LInstruction* instr);
LInstruction* DefineAsRegister(LInstruction* instr);
LInstruction* DefineAsSpilled(LInstruction* instr, int index);
- LInstruction* DefineSameAsAny(LInstruction* instr);
LInstruction* DefineSameAsFirst(LInstruction* instr);
LInstruction* DefineFixed(LInstruction* instr, Register reg);
LInstruction* DefineFixedDouble(LInstruction* instr, DoubleRegister reg);
@@ -2074,8 +1924,6 @@ class LChunkBuilder BASE_EMBEDDED {
LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env);
- // Temporary operand that may be a memory location.
- LOperand* Temp();
// Temporary operand that must be in a register.
LUnallocated* TempRegister();
LOperand* FixedTemp(Register reg);
diff --git a/deps/v8/src/arm/lithium-codegen-arm.cc b/deps/v8/src/arm/lithium-codegen-arm.cc
index dfc48917da..dca95f2361 100644
--- a/deps/v8/src/arm/lithium-codegen-arm.cc
+++ b/deps/v8/src/arm/lithium-codegen-arm.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -54,6 +54,157 @@ class SafepointGenerator : public PostCallGenerator {
};
+class LGapNode: public ZoneObject {
+ public:
+ explicit LGapNode(LOperand* operand)
+ : operand_(operand), resolved_(false), visited_id_(-1) { }
+
+ LOperand* operand() const { return operand_; }
+ bool IsResolved() const { return !IsAssigned() || resolved_; }
+ void MarkResolved() {
+ ASSERT(!IsResolved());
+ resolved_ = true;
+ }
+ int visited_id() const { return visited_id_; }
+ void set_visited_id(int id) {
+ ASSERT(id > visited_id_);
+ visited_id_ = id;
+ }
+
+ bool IsAssigned() const { return assigned_from_.is_set(); }
+ LGapNode* assigned_from() const { return assigned_from_.get(); }
+ void set_assigned_from(LGapNode* n) { assigned_from_.set(n); }
+
+ private:
+ LOperand* operand_;
+ SetOncePointer<LGapNode> assigned_from_;
+ bool resolved_;
+ int visited_id_;
+};
+
+
+LGapResolver::LGapResolver()
+ : nodes_(32),
+ identified_cycles_(4),
+ result_(16),
+ next_visited_id_(0) {
+}
+
+
+const ZoneList<LMoveOperands>* LGapResolver::Resolve(
+ const ZoneList<LMoveOperands>* moves,
+ LOperand* marker_operand) {
+ nodes_.Rewind(0);
+ identified_cycles_.Rewind(0);
+ result_.Rewind(0);
+ next_visited_id_ = 0;
+
+ for (int i = 0; i < moves->length(); ++i) {
+ LMoveOperands move = moves->at(i);
+ if (!move.IsRedundant()) RegisterMove(move);
+ }
+
+ for (int i = 0; i < identified_cycles_.length(); ++i) {
+ ResolveCycle(identified_cycles_[i], marker_operand);
+ }
+
+ int unresolved_nodes;
+ do {
+ unresolved_nodes = 0;
+ for (int j = 0; j < nodes_.length(); j++) {
+ LGapNode* node = nodes_[j];
+ if (!node->IsResolved() && node->assigned_from()->IsResolved()) {
+ AddResultMove(node->assigned_from(), node);
+ node->MarkResolved();
+ }
+ if (!node->IsResolved()) ++unresolved_nodes;
+ }
+ } while (unresolved_nodes > 0);
+ return &result_;
+}
+
+
+void LGapResolver::AddResultMove(LGapNode* from, LGapNode* to) {
+ AddResultMove(from->operand(), to->operand());
+}
+
+
+void LGapResolver::AddResultMove(LOperand* from, LOperand* to) {
+ result_.Add(LMoveOperands(from, to));
+}
+
+
+void LGapResolver::ResolveCycle(LGapNode* start, LOperand* marker_operand) {
+ ZoneList<LOperand*> cycle_operands(8);
+ cycle_operands.Add(marker_operand);
+ LGapNode* cur = start;
+ do {
+ cur->MarkResolved();
+ cycle_operands.Add(cur->operand());
+ cur = cur->assigned_from();
+ } while (cur != start);
+ cycle_operands.Add(marker_operand);
+
+ for (int i = cycle_operands.length() - 1; i > 0; --i) {
+ LOperand* from = cycle_operands[i];
+ LOperand* to = cycle_operands[i - 1];
+ AddResultMove(from, to);
+ }
+}
+
+
+bool LGapResolver::CanReach(LGapNode* a, LGapNode* b, int visited_id) {
+ ASSERT(a != b);
+ LGapNode* cur = a;
+ while (cur != b && cur->visited_id() != visited_id && cur->IsAssigned()) {
+ cur->set_visited_id(visited_id);
+ cur = cur->assigned_from();
+ }
+
+ return cur == b;
+}
+
+
+bool LGapResolver::CanReach(LGapNode* a, LGapNode* b) {
+ ASSERT(a != b);
+ return CanReach(a, b, next_visited_id_++);
+}
+
+
+void LGapResolver::RegisterMove(LMoveOperands move) {
+ if (move.from()->IsConstantOperand()) {
+ // Constant moves should be last in the machine code. Therefore add them
+ // first to the result set.
+ AddResultMove(move.from(), move.to());
+ } else {
+ LGapNode* from = LookupNode(move.from());
+ LGapNode* to = LookupNode(move.to());
+ if (to->IsAssigned() && to->assigned_from() == from) {
+ move.Eliminate();
+ return;
+ }
+ ASSERT(!to->IsAssigned());
+ if (CanReach(from, to)) {
+ // This introduces a cycle. Save.
+ identified_cycles_.Add(from);
+ }
+ to->set_assigned_from(from);
+ }
+}
+
+
+LGapNode* LGapResolver::LookupNode(LOperand* operand) {
+ for (int i = 0; i < nodes_.length(); ++i) {
+ if (nodes_[i]->operand()->Equals(operand)) return nodes_[i];
+ }
+
+ // No node found => create a new one.
+ LGapNode* result = new LGapNode(operand);
+ nodes_.Add(result);
+ return result;
+}
+
+
#define __ masm()->
bool LCodeGen::GenerateCode() {
@@ -324,6 +475,45 @@ MemOperand LCodeGen::ToMemOperand(LOperand* op) const {
}
+void LCodeGen::WriteTranslation(LEnvironment* environment,
+ Translation* translation) {
+ if (environment == NULL) return;
+
+ // The translation includes one command per value in the environment.
+ int translation_size = environment->values()->length();
+ // The output frame height does not include the parameters.
+ int height = translation_size - environment->parameter_count();
+
+ WriteTranslation(environment->outer(), translation);
+ int closure_id = DefineDeoptimizationLiteral(environment->closure());
+ translation->BeginFrame(environment->ast_id(), closure_id, height);
+ for (int i = 0; i < translation_size; ++i) {
+ LOperand* value = environment->values()->at(i);
+ // spilled_registers_ and spilled_double_registers_ are either
+ // both NULL or both set.
+ if (environment->spilled_registers() != NULL && value != NULL) {
+ if (value->IsRegister() &&
+ environment->spilled_registers()[value->index()] != NULL) {
+ translation->MarkDuplicate();
+ AddToTranslation(translation,
+ environment->spilled_registers()[value->index()],
+ environment->HasTaggedValueAt(i));
+ } else if (
+ value->IsDoubleRegister() &&
+ environment->spilled_double_registers()[value->index()] != NULL) {
+ translation->MarkDuplicate();
+ AddToTranslation(
+ translation,
+ environment->spilled_double_registers()[value->index()],
+ false);
+ }
+ }
+
+ AddToTranslation(translation, value, environment->HasTaggedValueAt(i));
+ }
+}
+
+
void LCodeGen::AddToTranslation(Translation* translation,
LOperand* op,
bool is_tagged) {
@@ -439,7 +629,7 @@ void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment) {
++frame_count;
}
Translation translation(&translations_, frame_count);
- environment->WriteTranslation(this, &translation);
+ WriteTranslation(environment, &translation);
int deoptimization_index = deoptimizations_.length();
environment->Register(deoptimization_index, translation.index());
deoptimizations_.Add(environment);
@@ -575,6 +765,27 @@ void LCodeGen::RecordSafepointWithRegisters(LPointerMap* pointers,
}
+void LCodeGen::RecordSafepointWithRegistersAndDoubles(
+ LPointerMap* pointers,
+ int arguments,
+ int deoptimization_index) {
+ const ZoneList<LOperand*>* operands = pointers->operands();
+ Safepoint safepoint =
+ safepoints_.DefineSafepointWithRegistersAndDoubles(
+ masm(), arguments, deoptimization_index);
+ for (int i = 0; i < operands->length(); i++) {
+ LOperand* pointer = operands->at(i);
+ if (pointer->IsStackSlot()) {
+ safepoint.DefinePointerSlot(pointer->index());
+ } else if (pointer->IsRegister()) {
+ safepoint.DefinePointerRegister(ToRegister(pointer));
+ }
+ }
+ // Register cp always contains a pointer to the context.
+ safepoint.DefinePointerRegister(cp);
+}
+
+
void LCodeGen::RecordPosition(int position) {
if (!FLAG_debug_info || position == RelocInfo::kNoPosition) return;
masm()->positions_recorder()->RecordPosition(position);
@@ -598,11 +809,11 @@ void LCodeGen::DoParallelMove(LParallelMove* move) {
DoubleRegister dbl_scratch = d0;
LUnallocated marker_operand(LUnallocated::NONE);
- Register core_scratch = r9;
+ Register core_scratch = scratch0();
bool destroys_core_scratch = false;
- LGapResolver resolver(move->move_operands(), &marker_operand);
- const ZoneList<LMoveOperands>* moves = resolver.ResolveInReverseOrder();
+ const ZoneList<LMoveOperands>* moves =
+ resolver_.Resolve(move->move_operands(), &marker_operand);
for (int i = moves->length() - 1; i >= 0; --i) {
LMoveOperands move = moves->at(i);
LOperand* from = move.from();
@@ -730,7 +941,55 @@ void LCodeGen::DoParameter(LParameter* instr) {
void LCodeGen::DoCallStub(LCallStub* instr) {
- Abort("DoCallStub unimplemented.");
+ ASSERT(ToRegister(instr->result()).is(r0));
+ switch (instr->hydrogen()->major_key()) {
+ case CodeStub::RegExpConstructResult: {
+ RegExpConstructResultStub stub;
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::RegExpExec: {
+ RegExpExecStub stub;
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::SubString: {
+ SubStringStub stub;
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::StringCharAt: {
+ Abort("StringCharAtStub unimplemented.");
+ break;
+ }
+ case CodeStub::MathPow: {
+ Abort("MathPowStub unimplemented.");
+ break;
+ }
+ case CodeStub::NumberToString: {
+ NumberToStringStub stub;
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::StringAdd: {
+ StringAddStub stub(NO_STRING_ADD_FLAGS);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::StringCompare: {
+ StringCompareStub stub;
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::TranscendentalCache: {
+ __ ldr(r0, MemOperand(sp, 0));
+ TranscendentalCacheStub stub(instr->transcendental_type());
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
}
@@ -740,18 +999,163 @@ void LCodeGen::DoUnknownOSRValue(LUnknownOSRValue* instr) {
void LCodeGen::DoModI(LModI* instr) {
- Abort("DoModI unimplemented.");
+ Abort("ModI not implemented");
+ class DeferredModI: public LDeferredCode {
+ public:
+ DeferredModI(LCodeGen* codegen, LModI* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() {
+ codegen()->DoDeferredGenericBinaryStub(instr_, Token::MOD);
+ }
+ private:
+ LModI* instr_;
+ };
+ // These registers hold untagged 32 bit values.
+ Register left = ToRegister(instr->left());
+ Register right = ToRegister(instr->right());
+ Register result = ToRegister(instr->result());
+ Register scratch = scratch0();
+
+ Label deoptimize, done;
+ // Check for x % 0.
+ if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) {
+ __ tst(right, Operand(right));
+ __ b(eq, &deoptimize);
+ }
+
+ // Check for (0 % -x) that will produce negative zero.
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ Label ok;
+ __ tst(left, Operand(left));
+ __ b(ne, &ok);
+ __ tst(right, Operand(right));
+ __ b(pl, &ok);
+ __ b(al, &deoptimize);
+ __ bind(&ok);
+ }
+
+ // Call the generic stub. The numbers in r0 and r1 have
+ // to be tagged to Smis. If that is not possible, deoptimize.
+ DeferredModI* deferred = new DeferredModI(this, instr);
+ __ TrySmiTag(left, &deoptimize, scratch);
+ __ TrySmiTag(right, &deoptimize, scratch);
+
+ __ b(al, deferred->entry());
+ __ bind(deferred->exit());
+
+ // If the result in r0 is a Smi, untag it, else deoptimize.
+ __ BranchOnNotSmi(result, &deoptimize);
+ __ mov(result, Operand(result, ASR, 1));
+
+ __ b(al, &done);
+ __ bind(&deoptimize);
+ DeoptimizeIf(al, instr->environment());
+ __ bind(&done);
}
void LCodeGen::DoDivI(LDivI* instr) {
- Abort("DoDivI unimplemented.");
+ Abort("DivI not implemented");
+ class DeferredDivI: public LDeferredCode {
+ public:
+ DeferredDivI(LCodeGen* codegen, LDivI* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() {
+ codegen()->DoDeferredGenericBinaryStub(instr_, Token::DIV);
+ }
+ private:
+ LDivI* instr_;
+ };
+
+ const Register left = ToRegister(instr->left());
+ const Register right = ToRegister(instr->right());
+ const Register scratch = scratch0();
+ const Register result = ToRegister(instr->result());
+
+ // Check for x / 0.
+ if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) {
+ __ tst(right, right);
+ DeoptimizeIf(eq, instr->environment());
+ }
+
+ // Check for (0 / -x) that will produce negative zero.
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ Label left_not_zero;
+ __ tst(left, Operand(left));
+ __ b(ne, &left_not_zero);
+ __ tst(right, Operand(right));
+ DeoptimizeIf(mi, instr->environment());
+ __ bind(&left_not_zero);
+ }
+
+ // Check for (-kMinInt / -1).
+ if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
+ Label left_not_min_int;
+ __ cmp(left, Operand(kMinInt));
+ __ b(ne, &left_not_min_int);
+ __ cmp(right, Operand(-1));
+ DeoptimizeIf(eq, instr->environment());
+ __ bind(&left_not_min_int);
+ }
+
+ Label done, deoptimize;
+ // Test for a few common cases first.
+ __ cmp(right, Operand(1));
+ __ mov(result, left, LeaveCC, eq);
+ __ b(eq, &done);
+
+ __ cmp(right, Operand(2));
+ __ tst(left, Operand(1), eq);
+ __ mov(result, Operand(left, ASR, 1), LeaveCC, eq);
+ __ b(eq, &done);
+
+ __ cmp(right, Operand(4));
+ __ tst(left, Operand(3), eq);
+ __ mov(result, Operand(left, ASR, 2), LeaveCC, eq);
+ __ b(eq, &done);
+
+ // Call the generic stub. The numbers in r0 and r1 have
+ // to be tagged to Smis. If that is not possible, deoptimize.
+ DeferredDivI* deferred = new DeferredDivI(this, instr);
+
+ __ TrySmiTag(left, &deoptimize, scratch);
+ __ TrySmiTag(right, &deoptimize, scratch);
+
+ __ b(al, deferred->entry());
+ __ bind(deferred->exit());
+
+ // If the result in r0 is a Smi, untag it, else deoptimize.
+ __ BranchOnNotSmi(result, &deoptimize);
+ __ SmiUntag(result);
+ __ b(&done);
+
+ __ bind(&deoptimize);
+ DeoptimizeIf(al, instr->environment());
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoDeferredGenericBinaryStub(LBinaryOperation* instr,
+ Token::Value op) {
+ Register left = ToRegister(instr->left());
+ Register right = ToRegister(instr->right());
+
+ __ PushSafepointRegistersAndDoubles();
+ GenericBinaryOpStub stub(op, OVERWRITE_LEFT, left, right);
+ __ CallStub(&stub);
+ RecordSafepointWithRegistersAndDoubles(instr->pointer_map(),
+ 0,
+ Safepoint::kNoDeoptimizationIndex);
+ // Overwrite the stored value of r0 with the result of the stub.
+ __ str(r0, MemOperand(sp, DwVfpRegister::kNumAllocatableRegisters *
+ kDoubleSize));
+ __ PopSafepointRegistersAndDoubles();
}
void LCodeGen::DoMulI(LMulI* instr) {
+ Register scratch = scratch0();
Register left = ToRegister(instr->left());
- Register scratch = r9;
Register right = EmitLoadRegister(instr->right(), scratch);
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero) &&
@@ -813,6 +1217,7 @@ void LCodeGen::DoBitI(LBitI* instr) {
void LCodeGen::DoShiftI(LShiftI* instr) {
+ Register scratch = scratch0();
LOperand* left = instr->left();
LOperand* right = instr->right();
ASSERT(left->Equals(instr->result()));
@@ -820,21 +1225,21 @@ void LCodeGen::DoShiftI(LShiftI* instr) {
Register result = ToRegister(left);
if (right->IsRegister()) {
// Mask the right operand.
- __ and_(r9, ToRegister(right), Operand(0x1F));
+ __ and_(scratch, ToRegister(right), Operand(0x1F));
switch (instr->op()) {
case Token::SAR:
- __ mov(result, Operand(result, ASR, r9));
+ __ mov(result, Operand(result, ASR, scratch));
break;
case Token::SHR:
if (instr->can_deopt()) {
- __ mov(result, Operand(result, LSR, r9), SetCC);
+ __ mov(result, Operand(result, LSR, scratch), SetCC);
DeoptimizeIf(mi, instr->environment());
} else {
- __ mov(result, Operand(result, LSR, r9));
+ __ mov(result, Operand(result, LSR, scratch));
}
break;
case Token::SHL:
- __ mov(result, Operand(result, LSL, r9));
+ __ mov(result, Operand(result, LSL, scratch));
break;
default:
UNREACHABLE();
@@ -898,29 +1303,37 @@ void LCodeGen::DoConstantT(LConstantT* instr) {
}
-void LCodeGen::DoArrayLength(LArrayLength* instr) {
+void LCodeGen::DoJSArrayLength(LJSArrayLength* instr) {
Register result = ToRegister(instr->result());
+ Register array = ToRegister(instr->input());
+ __ ldr(result, FieldMemOperand(array, JSArray::kLengthOffset));
+}
- if (instr->hydrogen()->value()->IsLoadElements()) {
- // We load the length directly from the elements array.
- Register elements = ToRegister(instr->input());
- __ ldr(result, FieldMemOperand(elements, FixedArray::kLengthOffset));
- } else {
- // Check that the receiver really is an array.
- Register array = ToRegister(instr->input());
- Register temporary = ToRegister(instr->temporary());
- __ CompareObjectType(array, temporary, temporary, JS_ARRAY_TYPE);
- DeoptimizeIf(ne, instr->environment());
- // Load length directly from the array.
- __ ldr(result, FieldMemOperand(array, JSArray::kLengthOffset));
- }
- Abort("DoArrayLength untested.");
+void LCodeGen::DoFixedArrayLength(LFixedArrayLength* instr) {
+ Register result = ToRegister(instr->result());
+ Register array = ToRegister(instr->input());
+ __ ldr(result, FieldMemOperand(array, FixedArray::kLengthOffset));
}
void LCodeGen::DoValueOf(LValueOf* instr) {
- Abort("DoValueOf unimplemented.");
+ Register input = ToRegister(instr->input());
+ Register result = ToRegister(instr->result());
+ Register map = ToRegister(instr->temporary());
+ ASSERT(input.is(result));
+ Label done;
+
+ // If the object is a smi return the object.
+ __ tst(input, Operand(kSmiTagMask));
+ __ b(eq, &done);
+
+ // If the object is not a value type, return the object.
+ __ CompareObjectType(input, map, map, JS_VALUE_TYPE);
+ __ b(ne, &done);
+ __ ldr(result, FieldMemOperand(input, JSValue::kValueOffset));
+
+ __ bind(&done);
}
@@ -928,7 +1341,6 @@ void LCodeGen::DoBitNotI(LBitNotI* instr) {
LOperand* input = instr->input();
ASSERT(input->Equals(instr->result()));
__ mvn(ToRegister(input), Operand(ToRegister(input)));
- Abort("DoBitNotI untested.");
}
@@ -1035,7 +1447,11 @@ void LCodeGen::DoBranch(LBranch* instr) {
EmitBranch(true_block, false_block, nz);
} else if (r.IsDouble()) {
DoubleRegister reg = ToDoubleRegister(instr->input());
- __ vcmp(reg, 0.0);
+ Register scratch = scratch0();
+
+ // Test the double value. Zero and NaN are false.
+ __ VFPCompareAndLoadFlags(reg, 0.0, scratch);
+ __ tst(scratch, Operand(kVFPZConditionFlagBit | kVFPVConditionFlagBit));
EmitBranch(true_block, false_block, ne);
} else {
ASSERT(r.IsTagged());
@@ -1062,19 +1478,19 @@ void LCodeGen::DoBranch(LBranch* instr) {
__ tst(reg, Operand(kSmiTagMask));
__ b(eq, true_label);
- // Test for double values. Zero is false.
+ // Test double values. Zero and NaN are false.
Label call_stub;
DoubleRegister dbl_scratch = d0;
- Register core_scratch = r9;
- ASSERT(!reg.is(core_scratch));
- __ ldr(core_scratch, FieldMemOperand(reg, HeapObject::kMapOffset));
+ Register scratch = scratch0();
+ __ ldr(scratch, FieldMemOperand(reg, HeapObject::kMapOffset));
__ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
- __ cmp(core_scratch, Operand(ip));
+ __ cmp(scratch, Operand(ip));
__ b(ne, &call_stub);
__ sub(ip, reg, Operand(kHeapObjectTag));
__ vldr(dbl_scratch, ip, HeapNumber::kValueOffset);
- __ vcmp(dbl_scratch, 0.0);
- __ b(eq, false_label);
+ __ VFPCompareAndLoadFlags(dbl_scratch, 0.0, scratch);
+ __ tst(scratch, Operand(kVFPZConditionFlagBit | kVFPVConditionFlagBit));
+ __ b(ne, false_label);
__ b(true_label);
// The conversion stub doesn't cause garbage collections so it's
@@ -1093,24 +1509,47 @@ void LCodeGen::DoBranch(LBranch* instr) {
void LCodeGen::EmitGoto(int block, LDeferredCode* deferred_stack_check) {
- // TODO(srdjan): Perform stack overflow check if this goto needs it
- // before jumping.
block = chunk_->LookupDestination(block);
int next_block = GetNextEmittedBlock(current_block_);
if (block != next_block) {
- __ jmp(chunk_->GetAssemblyLabel(block));
+ // Perform stack overflow check if this goto needs it before jumping.
+ if (deferred_stack_check != NULL) {
+ __ LoadRoot(ip, Heap::kStackLimitRootIndex);
+ __ cmp(sp, Operand(ip));
+ __ b(hs, chunk_->GetAssemblyLabel(block));
+ __ jmp(deferred_stack_check->entry());
+ deferred_stack_check->SetExit(chunk_->GetAssemblyLabel(block));
+ } else {
+ __ jmp(chunk_->GetAssemblyLabel(block));
+ }
}
}
void LCodeGen::DoDeferredStackCheck(LGoto* instr) {
- UNIMPLEMENTED();
+ __ PushSafepointRegisters();
+ __ CallRuntimeSaveDoubles(Runtime::kStackGuard);
+ RecordSafepointWithRegisters(
+ instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
+ __ PopSafepointRegisters();
}
void LCodeGen::DoGoto(LGoto* instr) {
- // TODO(srdjan): Implement deferred stack check.
- EmitGoto(instr->block_id(), NULL);
+ class DeferredStackCheck: public LDeferredCode {
+ public:
+ DeferredStackCheck(LCodeGen* codegen, LGoto* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() { codegen()->DoDeferredStackCheck(instr_); }
+ private:
+ LGoto* instr_;
+ };
+
+ DeferredStackCheck* deferred = NULL;
+ if (instr->include_stack_check()) {
+ deferred = new DeferredStackCheck(this, instr);
+ }
+ EmitGoto(instr->block_id(), deferred);
}
@@ -1176,11 +1615,41 @@ void LCodeGen::DoCmpJSObjectEqAndBranch(LCmpJSObjectEqAndBranch* instr) {
void LCodeGen::DoIsNull(LIsNull* instr) {
- Abort("DoIsNull unimplemented.");
+ Register reg = ToRegister(instr->input());
+ Register result = ToRegister(instr->result());
+
+ __ LoadRoot(ip, Heap::kNullValueRootIndex);
+ __ cmp(reg, ip);
+ if (instr->is_strict()) {
+ __ LoadRoot(result, Heap::kTrueValueRootIndex, eq);
+ __ LoadRoot(result, Heap::kFalseValueRootIndex, ne);
+ } else {
+ Label true_value, false_value, done;
+ __ b(eq, &true_value);
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ __ cmp(ip, reg);
+ __ b(eq, &true_value);
+ __ tst(reg, Operand(kSmiTagMask));
+ __ b(eq, &false_value);
+ // Check for undetectable objects by looking in the bit field in
+ // the map. The object has already been smi checked.
+ Register scratch = result;
+ __ ldr(scratch, FieldMemOperand(reg, HeapObject::kMapOffset));
+ __ ldrb(scratch, FieldMemOperand(scratch, Map::kBitFieldOffset));
+ __ tst(scratch, Operand(1 << Map::kIsUndetectable));
+ __ b(ne, &true_value);
+ __ bind(&false_value);
+ __ LoadRoot(result, Heap::kFalseValueRootIndex);
+ __ jmp(&done);
+ __ bind(&true_value);
+ __ LoadRoot(result, Heap::kTrueValueRootIndex);
+ __ bind(&done);
+ }
}
void LCodeGen::DoIsNullAndBranch(LIsNullAndBranch* instr) {
+ Register scratch = scratch0();
Register reg = ToRegister(instr->input());
// TODO(fsc): If the expression is known to be a smi, then it's
@@ -1204,7 +1673,6 @@ void LCodeGen::DoIsNullAndBranch(LIsNullAndBranch* instr) {
__ b(eq, false_label);
// Check for undetectable objects by looking in the bit field in
// the map. The object has already been smi checked.
- Register scratch = ToRegister(instr->temp());
__ ldr(scratch, FieldMemOperand(reg, HeapObject::kMapOffset));
__ ldrb(scratch, FieldMemOperand(scratch, Map::kBitFieldOffset));
__ tst(scratch, Operand(1 << Map::kIsUndetectable));
@@ -1282,8 +1750,8 @@ void LCodeGen::DoHasInstanceType(LHasInstanceType* instr) {
void LCodeGen::DoHasInstanceTypeAndBranch(LHasInstanceTypeAndBranch* instr) {
+ Register scratch = scratch0();
Register input = ToRegister(instr->input());
- Register temp = ToRegister(instr->temp());
int true_block = chunk_->LookupDestination(instr->true_block_id());
int false_block = chunk_->LookupDestination(instr->false_block_id());
@@ -1293,7 +1761,7 @@ void LCodeGen::DoHasInstanceTypeAndBranch(LHasInstanceTypeAndBranch* instr) {
__ tst(input, Operand(kSmiTagMask));
__ b(eq, false_label);
- __ CompareObjectType(input, temp, temp, instr->TestType());
+ __ CompareObjectType(input, scratch, scratch, instr->TestType());
EmitBranch(true_block, false_block, instr->BranchCondition());
}
@@ -1309,7 +1777,7 @@ void LCodeGen::DoHasCachedArrayIndexAndBranch(
}
-// Branches to a label or falls through with the answer in the z flag. Trashes
+// Branches to a label or falls through with the answer in flags. Trashes
// the temp registers, but not the input. Only input and temp2 may alias.
void LCodeGen::EmitClassOfTest(Label* is_true,
Label* is_false,
@@ -1317,34 +1785,117 @@ void LCodeGen::EmitClassOfTest(Label* is_true,
Register input,
Register temp,
Register temp2) {
- Abort("EmitClassOfTest unimplemented.");
+ ASSERT(!input.is(temp));
+ ASSERT(!temp.is(temp2)); // But input and temp2 may be the same register.
+ __ tst(input, Operand(kSmiTagMask));
+ __ b(eq, is_false);
+ __ CompareObjectType(input, temp, temp2, FIRST_JS_OBJECT_TYPE);
+ __ b(lt, is_false);
+
+ // Map is now in temp.
+ // Functions have class 'Function'.
+ __ CompareInstanceType(temp, temp2, JS_FUNCTION_TYPE);
+ if (class_name->IsEqualTo(CStrVector("Function"))) {
+ __ b(eq, is_true);
+ } else {
+ __ b(eq, is_false);
+ }
+
+ // Check if the constructor in the map is a function.
+ __ ldr(temp, FieldMemOperand(temp, Map::kConstructorOffset));
+
+ // As long as JS_FUNCTION_TYPE is the last instance type and it is
+ // right after LAST_JS_OBJECT_TYPE, we can avoid checking for
+ // LAST_JS_OBJECT_TYPE.
+ ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
+ ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
+
+ // Objects with a non-function constructor have class 'Object'.
+ __ CompareObjectType(temp, temp2, temp2, JS_FUNCTION_TYPE);
+ if (class_name->IsEqualTo(CStrVector("Object"))) {
+ __ b(ne, is_true);
+ } else {
+ __ b(ne, is_false);
+ }
+
+ // temp now contains the constructor function. Grab the
+ // instance class name from there.
+ __ ldr(temp, FieldMemOperand(temp, JSFunction::kSharedFunctionInfoOffset));
+ __ ldr(temp, FieldMemOperand(temp,
+ SharedFunctionInfo::kInstanceClassNameOffset));
+ // The class name we are testing against is a symbol because it's a literal.
+ // The name in the constructor is a symbol because of the way the context is
+ // booted. This routine isn't expected to work for random API-created
+ // classes and it doesn't have to because you can't access it with natives
+ // syntax. Since both sides are symbols it is sufficient to use an identity
+ // comparison.
+ __ cmp(temp, Operand(class_name));
+ // End with the answer in flags.
}
void LCodeGen::DoClassOfTest(LClassOfTest* instr) {
- Abort("DoClassOfTest unimplemented.");
+ Register input = ToRegister(instr->input());
+ Register result = ToRegister(instr->result());
+ ASSERT(input.is(result));
+ Handle<String> class_name = instr->hydrogen()->class_name();
+
+ Label done, is_true, is_false;
+
+ EmitClassOfTest(&is_true, &is_false, class_name, input, scratch0(), input);
+ __ b(ne, &is_false);
+
+ __ bind(&is_true);
+ __ LoadRoot(result, Heap::kTrueValueRootIndex);
+ __ jmp(&done);
+
+ __ bind(&is_false);
+ __ LoadRoot(result, Heap::kFalseValueRootIndex);
+ __ bind(&done);
}
void LCodeGen::DoClassOfTestAndBranch(LClassOfTestAndBranch* instr) {
- Abort("DoClassOfTestAndBranch unimplemented.");
+ Register input = ToRegister(instr->input());
+ Register temp = scratch0();
+ Register temp2 = ToRegister(instr->temporary());
+ Handle<String> class_name = instr->hydrogen()->class_name();
+
+ int true_block = chunk_->LookupDestination(instr->true_block_id());
+ int false_block = chunk_->LookupDestination(instr->false_block_id());
+
+ Label* true_label = chunk_->GetAssemblyLabel(true_block);
+ Label* false_label = chunk_->GetAssemblyLabel(false_block);
+
+ EmitClassOfTest(true_label, false_label, class_name, input, temp, temp2);
+
+ EmitBranch(true_block, false_block, eq);
}
void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) {
- Abort("DoCmpMapAndBranch unimplemented.");
+ Register reg = ToRegister(instr->input());
+ Register temp = ToRegister(instr->temp());
+ int true_block = instr->true_block_id();
+ int false_block = instr->false_block_id();
+
+ __ ldr(temp, FieldMemOperand(reg, HeapObject::kMapOffset));
+ __ cmp(temp, Operand(instr->map()));
+ EmitBranch(true_block, false_block, eq);
}
void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
- // We expect object and function in registers r1 and r0.
+ ASSERT(ToRegister(instr->left()).is(r0)); // Object is in r0.
+ ASSERT(ToRegister(instr->right()).is(r1)); // Function is in r1.
+
InstanceofStub stub(InstanceofStub::kArgsInRegisters);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
Label true_value, done;
__ tst(r0, r0);
- __ mov(r0, Operand(Factory::false_value()), LeaveCC, eq);
- __ mov(r0, Operand(Factory::true_value()), LeaveCC, ne);
+ __ mov(r0, Operand(Factory::false_value()), LeaveCC, ne);
+ __ mov(r0, Operand(Factory::true_value()), LeaveCC, eq);
}
@@ -1353,6 +1904,10 @@ void LCodeGen::DoInstanceOfAndBranch(LInstanceOfAndBranch* instr) {
}
+void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) {
+ Abort("DoInstanceOfKnownGlobal unimplemented.");
+}
+
static Condition ComputeCompareCondition(Token::Value op) {
switch (op) {
@@ -1385,10 +1940,12 @@ void LCodeGen::DoCmpT(LCmpT* instr) {
condition = ReverseCondition(condition);
}
__ cmp(r0, Operand(0));
- __ LoadRoot(ToRegister(instr->result()), Heap::kTrueValueRootIndex,
- condition);
- __ LoadRoot(ToRegister(instr->result()), Heap::kFalseValueRootIndex,
- NegateCondition(condition));
+ __ LoadRoot(ToRegister(instr->result()),
+ Heap::kTrueValueRootIndex,
+ condition);
+ __ LoadRoot(ToRegister(instr->result()),
+ Heap::kFalseValueRootIndex,
+ NegateCondition(condition));
}
@@ -1431,8 +1988,23 @@ void LCodeGen::DoStoreGlobal(LStoreGlobal* instr) {
}
+void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
+ // TODO(antonm): load a context with a separate instruction.
+ Register result = ToRegister(instr->result());
+ __ LoadContext(result, instr->context_chain_length());
+ __ ldr(result, ContextOperand(result, instr->slot_index()));
+}
+
+
void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
- Abort("DoLoadNamedField unimplemented.");
+ Register object = ToRegister(instr->input());
+ Register result = ToRegister(instr->result());
+ if (instr->hydrogen()->is_in_object()) {
+ __ ldr(result, FieldMemOperand(object, instr->hydrogen()->offset()));
+ } else {
+ __ ldr(result, FieldMemOperand(object, JSObject::kPropertiesOffset));
+ __ ldr(result, FieldMemOperand(result, instr->hydrogen()->offset()));
+ }
}
@@ -1447,18 +2019,103 @@ void LCodeGen::DoLoadNamedGeneric(LLoadNamedGeneric* instr) {
}
+void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
+ Register scratch = scratch0();
+ Register function = ToRegister(instr->function());
+ Register result = ToRegister(instr->result());
+
+ // Check that the function really is a function. Load map into the
+ // result register.
+ __ CompareObjectType(function, result, scratch, JS_FUNCTION_TYPE);
+ DeoptimizeIf(ne, instr->environment());
+
+ // Make sure that the function has an instance prototype.
+ Label non_instance;
+ __ ldrb(scratch, FieldMemOperand(result, Map::kBitFieldOffset));
+ __ tst(scratch, Operand(1 << Map::kHasNonInstancePrototype));
+ __ b(ne, &non_instance);
+
+ // Get the prototype or initial map from the function.
+ __ ldr(result,
+ FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
+
+ // Check that the function has a prototype or an initial map.
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ cmp(result, ip);
+ DeoptimizeIf(eq, instr->environment());
+
+ // If the function does not have an initial map, we're done.
+ Label done;
+ __ CompareObjectType(result, scratch, scratch, MAP_TYPE);
+ __ b(ne, &done);
+
+ // Get the prototype from the initial map.
+ __ ldr(result, FieldMemOperand(result, Map::kPrototypeOffset));
+ __ jmp(&done);
+
+ // Non-instance prototype: Fetch prototype from constructor field
+ // in initial map.
+ __ bind(&non_instance);
+ __ ldr(result, FieldMemOperand(result, Map::kConstructorOffset));
+
+ // All done.
+ __ bind(&done);
+}
+
+
void LCodeGen::DoLoadElements(LLoadElements* instr) {
- Abort("DoLoadElements unimplemented.");
+ ASSERT(instr->result()->Equals(instr->input()));
+ Register reg = ToRegister(instr->input());
+ Register scratch = scratch0();
+
+ __ ldr(reg, FieldMemOperand(reg, JSObject::kElementsOffset));
+ if (FLAG_debug_code) {
+ Label done;
+ __ ldr(scratch, FieldMemOperand(reg, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
+ __ cmp(scratch, ip);
+ __ b(eq, &done);
+ __ LoadRoot(ip, Heap::kFixedCOWArrayMapRootIndex);
+ __ cmp(scratch, ip);
+ __ Check(eq, "Check for fast elements failed.");
+ __ bind(&done);
+ }
}
void LCodeGen::DoAccessArgumentsAt(LAccessArgumentsAt* instr) {
- Abort("DoAccessArgumentsAt unimplemented.");
+ Register arguments = ToRegister(instr->arguments());
+ Register length = ToRegister(instr->length());
+ Register index = ToRegister(instr->index());
+ Register result = ToRegister(instr->result());
+
+ // Bailout index is not a valid argument index. Use unsigned check to get
+ // negative check for free.
+ __ sub(length, length, index, SetCC);
+ DeoptimizeIf(ls, instr->environment());
+
+ // There are two words between the frame pointer and the last argument.
+ // Subtracting from length accounts for one of them add one more.
+ __ add(length, length, Operand(1));
+ __ ldr(result, MemOperand(arguments, length, LSL, kPointerSizeLog2));
}
void LCodeGen::DoLoadKeyedFastElement(LLoadKeyedFastElement* instr) {
- Abort("DoLoadKeyedFastElement unimplemented.");
+ Register elements = ToRegister(instr->elements());
+ Register key = EmitLoadRegister(instr->key(), scratch0());
+ Register result = ToRegister(instr->result());
+ Register scratch = scratch0();
+ ASSERT(result.is(elements));
+
+ // Load the result.
+ __ add(scratch, elements, Operand(key, LSL, kPointerSizeLog2));
+ __ ldr(result, FieldMemOperand(scratch, FixedArray::kHeaderSize));
+
+ // Check for the hole value.
+ __ LoadRoot(scratch, Heap::kTheHoleValueRootIndex);
+ __ cmp(result, scratch);
+ DeoptimizeIf(eq, instr->environment());
}
@@ -1472,17 +2129,104 @@ void LCodeGen::DoLoadKeyedGeneric(LLoadKeyedGeneric* instr) {
void LCodeGen::DoArgumentsElements(LArgumentsElements* instr) {
- Abort("DoArgumentsElements unimplemented.");
+ Register scratch = scratch0();
+ Register result = ToRegister(instr->result());
+
+ // Check if the calling frame is an arguments adaptor frame.
+ Label done, adapted;
+ __ ldr(scratch, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ ldr(result, MemOperand(scratch, StandardFrameConstants::kContextOffset));
+ __ cmp(result, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+
+ // Result is the frame pointer for the frame if not adapted and for the real
+ // frame below the adaptor frame if adapted.
+ __ mov(result, fp, LeaveCC, ne);
+ __ mov(result, scratch, LeaveCC, eq);
}
void LCodeGen::DoArgumentsLength(LArgumentsLength* instr) {
- Abort("DoArgumentsLength unimplemented.");
+ Register elem = ToRegister(instr->input());
+ Register result = ToRegister(instr->result());
+
+ Label done;
+
+ // If no arguments adaptor frame the number of arguments is fixed.
+ __ cmp(fp, elem);
+ __ mov(result, Operand(scope()->num_parameters()));
+ __ b(eq, &done);
+
+ // Arguments adaptor frame present. Get argument length from there.
+ __ ldr(result, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ ldr(result,
+ MemOperand(result, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ SmiUntag(result);
+
+ // Argument length is in result register.
+ __ bind(&done);
}
void LCodeGen::DoApplyArguments(LApplyArguments* instr) {
- Abort("DoApplyArguments unimplemented.");
+ Register receiver = ToRegister(instr->receiver());
+ Register function = ToRegister(instr->function());
+ Register scratch = scratch0();
+
+ ASSERT(receiver.is(r0));
+ ASSERT(function.is(r1));
+ ASSERT(ToRegister(instr->result()).is(r0));
+
+ // If the receiver is null or undefined, we have to pass the
+ // global object as a receiver.
+ Label global_receiver, receiver_ok;
+ __ LoadRoot(scratch, Heap::kNullValueRootIndex);
+ __ cmp(receiver, scratch);
+ __ b(eq, &global_receiver);
+ __ LoadRoot(scratch, Heap::kUndefinedValueRootIndex);
+ __ cmp(receiver, scratch);
+ __ b(ne, &receiver_ok);
+ __ bind(&global_receiver);
+ __ ldr(receiver, GlobalObjectOperand());
+ __ bind(&receiver_ok);
+
+ Register length = ToRegister(instr->length());
+ Register elements = ToRegister(instr->elements());
+
+ Label invoke;
+
+ // Copy the arguments to this function possibly from the
+ // adaptor frame below it.
+ const uint32_t kArgumentsLimit = 1 * KB;
+ __ cmp(length, Operand(kArgumentsLimit));
+ DeoptimizeIf(hi, instr->environment());
+
+ // Push the receiver and use the register to keep the original
+ // number of arguments.
+ __ push(receiver);
+ __ mov(receiver, length);
+ // The arguments are at a one pointer size offset from elements.
+ __ add(elements, elements, Operand(1 * kPointerSize));
+
+ // Loop through the arguments pushing them onto the execution
+ // stack.
+ Label loop;
+ // length is a small non-negative integer, due to the test above.
+ __ tst(length, Operand(length));
+ __ b(eq, &invoke);
+ __ bind(&loop);
+ __ ldr(scratch, MemOperand(elements, length, LSL, 2));
+ __ push(scratch);
+ __ sub(length, length, Operand(1), SetCC);
+ __ b(ne, &loop);
+
+ __ bind(&invoke);
+ // Invoke the function. The number of arguments is stored in receiver
+ // which is r0, as expected by InvokeFunction.
+ v8::internal::ParameterCount actual(receiver);
+ SafepointGenerator safepoint_generator(this,
+ instr->pointer_map(),
+ Safepoint::kNoDeoptimizationIndex);
+ __ InvokeFunction(function, actual, CALL_FUNCTION, &safepoint_generator);
}
@@ -1544,7 +2288,9 @@ void LCodeGen::CallKnownFunction(Handle<JSFunction> function,
void LCodeGen::DoCallConstantFunction(LCallConstantFunction* instr) {
- Abort("DoCallConstantFunction unimplemented.");
+ ASSERT(ToRegister(instr->result()).is(r0));
+ __ mov(r1, Operand(instr->function()));
+ CallKnownFunction(instr->function(), instr->arity(), instr);
}
@@ -1559,12 +2305,44 @@ void LCodeGen::DoMathAbs(LUnaryMathOperation* instr) {
void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) {
- Abort("DoMathFloor unimplemented.");
+ DoubleRegister input = ToDoubleRegister(instr->input());
+ Register result = ToRegister(instr->result());
+ Register prev_fpscr = ToRegister(instr->temp());
+ SwVfpRegister single_scratch = double_scratch0().low();
+ Register scratch = scratch0();
+
+ // Set custom FPCSR:
+ // - Set rounding mode to "Round towards Minus Infinity".
+ // - Clear vfp cumulative exception flags.
+ // - Make sure Flush-to-zero mode control bit is unset.
+ __ vmrs(prev_fpscr);
+ __ bic(scratch, prev_fpscr,
+ Operand(kVFPExceptionMask | kVFPRoundingModeMask | kVFPFlushToZeroMask));
+ __ orr(scratch, scratch, Operand(kVFPRoundToMinusInfinityBits));
+ __ vmsr(scratch);
+
+ // Convert the argument to an integer.
+ __ vcvt_s32_f64(single_scratch,
+ input,
+ Assembler::FPSCRRounding,
+ al);
+
+ // Retrieve FPSCR and check for vfp exceptions.
+ __ vmrs(scratch);
+ // Restore FPSCR
+ __ vmsr(prev_fpscr);
+ __ tst(scratch, Operand(kVFPExceptionMask));
+ DeoptimizeIf(ne, instr->environment());
+
+ // Move the result back to general purpose register r0.
+ __ vmov(result, single_scratch);
}
void LCodeGen::DoMathSqrt(LUnaryMathOperation* instr) {
- Abort("DoMathSqrt unimplemented.");
+ DoubleRegister input = ToDoubleRegister(instr->input());
+ ASSERT(ToDoubleRegister(instr->result()).is(input));
+ __ vsqrt(input, input);
}
@@ -1587,7 +2365,12 @@ void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) {
void LCodeGen::DoCallKeyed(LCallKeyed* instr) {
- Abort("DoCallKeyed unimplemented.");
+ ASSERT(ToRegister(instr->result()).is(r0));
+
+ int arity = instr->arity();
+ Handle<Code> ic = StubCache::ComputeKeyedCallInitialize(arity, NOT_IN_LOOP);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr);
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
}
@@ -1604,12 +2387,24 @@ void LCodeGen::DoCallNamed(LCallNamed* instr) {
void LCodeGen::DoCallFunction(LCallFunction* instr) {
- Abort("DoCallFunction unimplemented.");
+ ASSERT(ToRegister(instr->result()).is(r0));
+
+ int arity = instr->arity();
+ CallFunctionStub stub(arity, NOT_IN_LOOP, RECEIVER_MIGHT_BE_VALUE);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ __ Drop(1);
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
}
void LCodeGen::DoCallGlobal(LCallGlobal* instr) {
- Abort("DoCallGlobal unimplemented.");
+ ASSERT(ToRegister(instr->result()).is(r0));
+
+ int arity = instr->arity();
+ Handle<Code> ic = StubCache::ComputeCallInitialize(arity, NOT_IN_LOOP);
+ __ mov(r2, Operand(instr->name()));
+ CallCode(ic, RelocInfo::CODE_TARGET_CONTEXT, instr);
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
}
@@ -1636,7 +2431,34 @@ void LCodeGen::DoCallRuntime(LCallRuntime* instr) {
void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
- Abort("DoStoreNamedField unimplemented.");
+ Register object = ToRegister(instr->object());
+ Register value = ToRegister(instr->value());
+ Register scratch = scratch0();
+ int offset = instr->offset();
+
+ ASSERT(!object.is(value));
+
+ if (!instr->transition().is_null()) {
+ __ mov(scratch, Operand(instr->transition()));
+ __ str(scratch, FieldMemOperand(object, HeapObject::kMapOffset));
+ }
+
+ // Do the store.
+ if (instr->is_in_object()) {
+ __ str(value, FieldMemOperand(object, offset));
+ if (instr->needs_write_barrier()) {
+ // Update the write barrier for the object for in-object properties.
+ __ RecordWrite(object, Operand(offset), value, scratch);
+ }
+ } else {
+ __ ldr(scratch, FieldMemOperand(object, JSObject::kPropertiesOffset));
+ __ str(value, FieldMemOperand(scratch, offset));
+ if (instr->needs_write_barrier()) {
+ // Update the write barrier for the properties array.
+ // object is used as a scratch register.
+ __ RecordWrite(scratch, Operand(offset), value, object);
+ }
+ }
}
@@ -1652,12 +2474,34 @@ void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) {
void LCodeGen::DoBoundsCheck(LBoundsCheck* instr) {
- Abort("DoBoundsCheck unimplemented.");
+ __ cmp(ToRegister(instr->index()), ToRegister(instr->length()));
+ DeoptimizeIf(hs, instr->environment());
}
void LCodeGen::DoStoreKeyedFastElement(LStoreKeyedFastElement* instr) {
- Abort("DoStoreKeyedFastElement unimplemented.");
+ Register value = ToRegister(instr->value());
+ Register elements = ToRegister(instr->object());
+ Register key = instr->key()->IsRegister() ? ToRegister(instr->key()) : no_reg;
+ Register scratch = scratch0();
+
+ // Do the store.
+ if (instr->key()->IsConstantOperand()) {
+ ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
+ LConstantOperand* const_operand = LConstantOperand::cast(instr->key());
+ int offset =
+ ToInteger32(const_operand) * kPointerSize + FixedArray::kHeaderSize;
+ __ str(value, FieldMemOperand(elements, offset));
+ } else {
+ __ add(scratch, elements, Operand(key, LSL, kPointerSizeLog2));
+ __ str(value, FieldMemOperand(scratch, FixedArray::kHeaderSize));
+ }
+
+ if (instr->hydrogen()->NeedsWriteBarrier()) {
+ // Compute address of modified element and store it into key register.
+ __ add(key, scratch, Operand(FixedArray::kHeaderSize));
+ __ RecordWrite(elements, key, value);
+ }
}
@@ -1672,7 +2516,19 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) {
void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) {
- Abort("DoInteger32ToDouble unimplemented.");
+ LOperand* input = instr->input();
+ ASSERT(input->IsRegister() || input->IsStackSlot());
+ LOperand* output = instr->result();
+ ASSERT(output->IsDoubleRegister());
+ SwVfpRegister single_scratch = double_scratch0().low();
+ if (input->IsStackSlot()) {
+ Register scratch = scratch0();
+ __ ldr(scratch, ToMemOperand(input));
+ __ vmov(single_scratch, scratch);
+ } else {
+ __ vmov(single_scratch, ToRegister(input));
+ }
+ __ vcvt_f64_s32(ToDoubleRegister(output), single_scratch);
}
@@ -1757,10 +2613,10 @@ void LCodeGen::DoNumberTagD(LNumberTagD* instr) {
};
DoubleRegister input_reg = ToDoubleRegister(instr->input());
+ Register scratch = scratch0();
Register reg = ToRegister(instr->result());
Register temp1 = ToRegister(instr->temp1());
Register temp2 = ToRegister(instr->temp2());
- Register scratch = r9;
DeferredNumberTagD* deferred = new DeferredNumberTagD(this, instr);
if (FLAG_inline_new) {
@@ -1801,15 +2657,20 @@ void LCodeGen::DoSmiTag(LSmiTag* instr) {
void LCodeGen::DoSmiUntag(LSmiUntag* instr) {
- Abort("DoSmiUntag unimplemented.");
+ LOperand* input = instr->input();
+ ASSERT(input->IsRegister() && input->Equals(instr->result()));
+ if (instr->needs_check()) {
+ __ tst(ToRegister(input), Operand(kSmiTagMask));
+ DeoptimizeIf(ne, instr->environment());
+ }
+ __ SmiUntag(ToRegister(input));
}
void LCodeGen::EmitNumberUntagD(Register input_reg,
DoubleRegister result_reg,
LEnvironment* env) {
- Register core_scratch = r9;
- ASSERT(!input_reg.is(core_scratch));
+ Register scratch = scratch0();
SwVfpRegister flt_scratch = s0;
ASSERT(!result_reg.is(d0));
@@ -1820,9 +2681,9 @@ void LCodeGen::EmitNumberUntagD(Register input_reg,
__ b(eq, &load_smi);
// Heap number map check.
- __ ldr(core_scratch, FieldMemOperand(input_reg, HeapObject::kMapOffset));
+ __ ldr(scratch, FieldMemOperand(input_reg, HeapObject::kMapOffset));
__ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
- __ cmp(core_scratch, Operand(ip));
+ __ cmp(scratch, Operand(ip));
__ b(eq, &heap_number);
__ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
@@ -1864,16 +2725,15 @@ class DeferredTaggedToI: public LDeferredCode {
void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
Label done;
Register input_reg = ToRegister(instr->input());
- Register core_scratch = r9;
- ASSERT(!input_reg.is(core_scratch));
+ Register scratch = scratch0();
DoubleRegister dbl_scratch = d0;
SwVfpRegister flt_scratch = s0;
DoubleRegister dbl_tmp = ToDoubleRegister(instr->temp());
// Heap number map check.
- __ ldr(core_scratch, FieldMemOperand(input_reg, HeapObject::kMapOffset));
+ __ ldr(scratch, FieldMemOperand(input_reg, HeapObject::kMapOffset));
__ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
- __ cmp(core_scratch, Operand(ip));
+ __ cmp(scratch, Operand(ip));
if (instr->truncating()) {
Label heap_number;
@@ -1889,7 +2749,7 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
__ bind(&heap_number);
__ sub(ip, input_reg, Operand(kHeapObjectTag));
__ vldr(dbl_tmp, ip, HeapNumber::kValueOffset);
- __ vcmp(dbl_tmp, 0.0); // Sets overflow bit if NaN.
+ __ vcmp(dbl_tmp, 0.0); // Sets overflow bit in FPSCR flags if NaN.
__ vcvt_s32_f64(flt_scratch, dbl_tmp);
__ vmov(input_reg, flt_scratch); // 32-bit result of conversion.
__ vmrs(pc); // Move vector status bits to normal status bits.
@@ -1910,8 +2770,7 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
// back to check; note that using non-overlapping s and d regs would be
// slightly faster.
__ vcvt_f64_s32(dbl_scratch, flt_scratch);
- __ vcmp(dbl_scratch, dbl_tmp);
- __ vmrs(pc); // Move vector status bits to normal status bits.
+ __ VFPCompareAndSetFlags(dbl_scratch, dbl_tmp);
DeoptimizeIf(ne, instr->environment()); // Not equal or unordered.
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
__ tst(input_reg, Operand(input_reg));
@@ -1972,7 +2831,26 @@ void LCodeGen::DoCheckSmi(LCheckSmi* instr) {
void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
- Abort("DoCheckInstanceType unimplemented.");
+ Register input = ToRegister(instr->input());
+ Register scratch = scratch0();
+ InstanceType first = instr->hydrogen()->first();
+ InstanceType last = instr->hydrogen()->last();
+
+ __ ldr(scratch, FieldMemOperand(input, HeapObject::kMapOffset));
+ __ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
+ __ cmp(scratch, Operand(first));
+
+ // If there is only one type in the interval check for equality.
+ if (first == last) {
+ DeoptimizeIf(ne, instr->environment());
+ } else {
+ DeoptimizeIf(lo, instr->environment());
+ // Omit check for the last type.
+ if (last != LAST_TYPE) {
+ __ cmp(scratch, Operand(last));
+ DeoptimizeIf(hi, instr->environment());
+ }
+ }
}
@@ -1985,53 +2863,205 @@ void LCodeGen::DoCheckFunction(LCheckFunction* instr) {
void LCodeGen::DoCheckMap(LCheckMap* instr) {
+ Register scratch = scratch0();
LOperand* input = instr->input();
ASSERT(input->IsRegister());
Register reg = ToRegister(input);
- __ ldr(r9, FieldMemOperand(reg, HeapObject::kMapOffset));
- __ cmp(r9, Operand(instr->hydrogen()->map()));
+ __ ldr(scratch, FieldMemOperand(reg, HeapObject::kMapOffset));
+ __ cmp(scratch, Operand(instr->hydrogen()->map()));
DeoptimizeIf(ne, instr->environment());
}
-void LCodeGen::LoadPrototype(Register result,
- Handle<JSObject> prototype) {
- Abort("LoadPrototype unimplemented.");
+void LCodeGen::LoadHeapObject(Register result,
+ Handle<HeapObject> object) {
+ if (Heap::InNewSpace(*object)) {
+ Handle<JSGlobalPropertyCell> cell =
+ Factory::NewJSGlobalPropertyCell(object);
+ __ mov(result, Operand(cell));
+ __ ldr(result, FieldMemOperand(result, JSGlobalPropertyCell::kValueOffset));
+ } else {
+ __ mov(result, Operand(object));
+ }
}
void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) {
- Abort("DoCheckPrototypeMaps unimplemented.");
+ Register temp1 = ToRegister(instr->temp1());
+ Register temp2 = ToRegister(instr->temp2());
+
+ Handle<JSObject> holder = instr->holder();
+ Handle<JSObject> current_prototype = instr->prototype();
+
+ // Load prototype object.
+ LoadHeapObject(temp1, current_prototype);
+
+ // Check prototype maps up to the holder.
+ while (!current_prototype.is_identical_to(holder)) {
+ __ ldr(temp2, FieldMemOperand(temp1, HeapObject::kMapOffset));
+ __ cmp(temp2, Operand(Handle<Map>(current_prototype->map())));
+ DeoptimizeIf(ne, instr->environment());
+ current_prototype =
+ Handle<JSObject>(JSObject::cast(current_prototype->GetPrototype()));
+ // Load next prototype object.
+ LoadHeapObject(temp1, current_prototype);
+ }
+
+ // Check the holder map.
+ __ ldr(temp2, FieldMemOperand(temp1, HeapObject::kMapOffset));
+ __ cmp(temp2, Operand(Handle<Map>(current_prototype->map())));
+ DeoptimizeIf(ne, instr->environment());
}
void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) {
- Abort("DoArrayLiteral unimplemented.");
+ __ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ __ ldr(r3, FieldMemOperand(r3, JSFunction::kLiteralsOffset));
+ __ mov(r2, Operand(Smi::FromInt(instr->hydrogen()->literal_index())));
+ __ mov(r1, Operand(instr->hydrogen()->constant_elements()));
+ __ Push(r3, r2, r1);
+
+ // Pick the right runtime function or stub to call.
+ int length = instr->hydrogen()->length();
+ if (instr->hydrogen()->IsCopyOnWrite()) {
+ ASSERT(instr->hydrogen()->depth() == 1);
+ FastCloneShallowArrayStub::Mode mode =
+ FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS;
+ FastCloneShallowArrayStub stub(mode, length);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ } else if (instr->hydrogen()->depth() > 1) {
+ CallRuntime(Runtime::kCreateArrayLiteral, 3, instr);
+ } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) {
+ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3, instr);
+ } else {
+ FastCloneShallowArrayStub::Mode mode =
+ FastCloneShallowArrayStub::CLONE_ELEMENTS;
+ FastCloneShallowArrayStub stub(mode, length);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ }
}
void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) {
- Abort("DoObjectLiteral unimplemented.");
+ __ ldr(r4, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ __ ldr(r4, FieldMemOperand(r4, JSFunction::kLiteralsOffset));
+ __ mov(r3, Operand(Smi::FromInt(instr->hydrogen()->literal_index())));
+ __ mov(r2, Operand(instr->hydrogen()->constant_properties()));
+ __ mov(r1, Operand(Smi::FromInt(instr->hydrogen()->fast_elements() ? 1 : 0)));
+ __ Push(r4, r3, r2, r1);
+
+ // Pick the right runtime function to call.
+ if (instr->hydrogen()->depth() > 1) {
+ CallRuntime(Runtime::kCreateObjectLiteral, 4, instr);
+ } else {
+ CallRuntime(Runtime::kCreateObjectLiteralShallow, 4, instr);
+ }
}
void LCodeGen::DoRegExpLiteral(LRegExpLiteral* instr) {
- Abort("DoRegExpLiteral unimplemented.");
+ Label materialized;
+ // Registers will be used as follows:
+ // r3 = JS function.
+ // r7 = literals array.
+ // r1 = regexp literal.
+ // r0 = regexp literal clone.
+ // r2 and r4-r6 are used as temporaries.
+ __ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ __ ldr(r7, FieldMemOperand(r3, JSFunction::kLiteralsOffset));
+ int literal_offset = FixedArray::kHeaderSize +
+ instr->hydrogen()->literal_index() * kPointerSize;
+ __ ldr(r1, FieldMemOperand(r7, literal_offset));
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ __ cmp(r1, ip);
+ __ b(ne, &materialized);
+
+ // Create regexp literal using runtime function
+ // Result will be in r0.
+ __ mov(r6, Operand(Smi::FromInt(instr->hydrogen()->literal_index())));
+ __ mov(r5, Operand(instr->hydrogen()->pattern()));
+ __ mov(r4, Operand(instr->hydrogen()->flags()));
+ __ Push(r7, r6, r5, r4);
+ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4, instr);
+ __ mov(r1, r0);
+
+ __ bind(&materialized);
+ int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize;
+ Label allocated, runtime_allocate;
+
+ __ AllocateInNewSpace(size, r0, r2, r3, &runtime_allocate, TAG_OBJECT);
+ __ jmp(&allocated);
+
+ __ bind(&runtime_allocate);
+ __ mov(r0, Operand(Smi::FromInt(size)));
+ __ Push(r1, r0);
+ CallRuntime(Runtime::kAllocateInNewSpace, 1, instr);
+ __ pop(r1);
+
+ __ bind(&allocated);
+ // Copy the content into the newly allocated memory.
+ // (Unroll copy loop once for better throughput).
+ for (int i = 0; i < size - kPointerSize; i += 2 * kPointerSize) {
+ __ ldr(r3, FieldMemOperand(r1, i));
+ __ ldr(r2, FieldMemOperand(r1, i + kPointerSize));
+ __ str(r3, FieldMemOperand(r0, i));
+ __ str(r2, FieldMemOperand(r0, i + kPointerSize));
+ }
+ if ((size % (2 * kPointerSize)) != 0) {
+ __ ldr(r3, FieldMemOperand(r1, size - kPointerSize));
+ __ str(r3, FieldMemOperand(r0, size - kPointerSize));
+ }
}
void LCodeGen::DoFunctionLiteral(LFunctionLiteral* instr) {
- Abort("DoFunctionLiteral unimplemented.");
+ // Use the fast case closure allocation code that allocates in new
+ // space for nested functions that don't need literals cloning.
+ Handle<SharedFunctionInfo> shared_info = instr->shared_info();
+ bool pretenure = !instr->hydrogen()->pretenure();
+ if (shared_info->num_literals() == 0 && !pretenure) {
+ FastNewClosureStub stub;
+ __ mov(r1, Operand(shared_info));
+ __ push(r1);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ } else {
+ __ mov(r2, Operand(shared_info));
+ __ mov(r1, Operand(pretenure
+ ? Factory::true_value()
+ : Factory::false_value()));
+ __ Push(cp, r2, r1);
+ CallRuntime(Runtime::kNewClosure, 3, instr);
+ }
}
void LCodeGen::DoTypeof(LTypeof* instr) {
- Abort("DoTypeof unimplemented.");
+ Register input = ToRegister(instr->input());
+ __ push(input);
+ CallRuntime(Runtime::kTypeof, 1, instr);
}
void LCodeGen::DoTypeofIs(LTypeofIs* instr) {
- Abort("DoTypeofIs unimplemented.");
+ Register input = ToRegister(instr->input());
+ Register result = ToRegister(instr->result());
+ Label true_label;
+ Label false_label;
+ Label done;
+
+ Condition final_branch_condition = EmitTypeofIs(&true_label,
+ &false_label,
+ input,
+ instr->type_literal());
+ __ b(final_branch_condition, &true_label);
+ __ bind(&false_label);
+ __ LoadRoot(result, Heap::kFalseValueRootIndex);
+ __ b(&done);
+
+ __ bind(&true_label);
+ __ LoadRoot(result, Heap::kTrueValueRootIndex);
+
+ __ bind(&done);
}
@@ -2056,8 +3086,7 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
Register input,
Handle<String> type_name) {
Condition final_branch_condition = no_condition;
- Register core_scratch = r9;
- ASSERT(!input.is(core_scratch));
+ Register scratch = scratch0();
if (type_name->Equals(Heap::number_symbol())) {
__ tst(input, Operand(kSmiTagMask));
__ b(eq, true_label);
@@ -2073,7 +3102,7 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
__ ldrb(ip, FieldMemOperand(input, Map::kBitFieldOffset));
__ tst(ip, Operand(1 << Map::kIsUndetectable));
__ b(ne, false_label);
- __ CompareInstanceType(input, core_scratch, FIRST_NONSTRING_TYPE);
+ __ CompareInstanceType(input, scratch, FIRST_NONSTRING_TYPE);
final_branch_condition = lo;
} else if (type_name->Equals(Heap::boolean_symbol())) {
@@ -2099,10 +3128,10 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
} else if (type_name->Equals(Heap::function_symbol())) {
__ tst(input, Operand(kSmiTagMask));
__ b(eq, false_label);
- __ CompareObjectType(input, input, core_scratch, JS_FUNCTION_TYPE);
+ __ CompareObjectType(input, input, scratch, JS_FUNCTION_TYPE);
__ b(eq, true_label);
// Regular expressions => 'function' (they are callable).
- __ CompareInstanceType(input, core_scratch, JS_REGEXP_TYPE);
+ __ CompareInstanceType(input, scratch, JS_REGEXP_TYPE);
final_branch_condition = eq;
} else if (type_name->Equals(Heap::object_symbol())) {
@@ -2112,16 +3141,16 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
__ cmp(input, ip);
__ b(eq, true_label);
// Regular expressions => 'function', not 'object'.
- __ CompareObjectType(input, input, core_scratch, JS_REGEXP_TYPE);
+ __ CompareObjectType(input, input, scratch, JS_REGEXP_TYPE);
__ b(eq, false_label);
// Check for undetectable objects => false.
__ ldrb(ip, FieldMemOperand(input, Map::kBitFieldOffset));
__ tst(ip, Operand(1 << Map::kIsUndetectable));
__ b(ne, false_label);
// Check for JS objects => true.
- __ CompareInstanceType(input, core_scratch, FIRST_JS_OBJECT_TYPE);
+ __ CompareInstanceType(input, scratch, FIRST_JS_OBJECT_TYPE);
__ b(lo, false_label);
- __ CompareInstanceType(input, core_scratch, LAST_JS_OBJECT_TYPE);
+ __ CompareInstanceType(input, scratch, LAST_JS_OBJECT_TYPE);
final_branch_condition = ls;
} else {
@@ -2146,7 +3175,14 @@ void LCodeGen::DoDeoptimize(LDeoptimize* instr) {
void LCodeGen::DoDeleteProperty(LDeleteProperty* instr) {
- Abort("DoDeleteProperty unimplemented.");
+ Register object = ToRegister(instr->object());
+ Register key = ToRegister(instr->key());
+ __ Push(object, key);
+ RecordPosition(instr->pointer_map()->position());
+ SafepointGenerator safepoint_generator(this,
+ instr->pointer_map(),
+ Safepoint::kNoDeoptimizationIndex);
+ __ InvokeBuiltin(Builtins::DELETE, CALL_JS, &safepoint_generator);
}
diff --git a/deps/v8/src/arm/lithium-codegen-arm.h b/deps/v8/src/arm/lithium-codegen-arm.h
index 541a699615..9eed64b455 100644
--- a/deps/v8/src/arm/lithium-codegen-arm.h
+++ b/deps/v8/src/arm/lithium-codegen-arm.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -39,8 +39,30 @@ namespace internal {
// Forward declarations.
class LDeferredCode;
+class LGapNode;
class SafepointGenerator;
+class LGapResolver BASE_EMBEDDED {
+ public:
+ LGapResolver();
+ const ZoneList<LMoveOperands>* Resolve(const ZoneList<LMoveOperands>* moves,
+ LOperand* marker_operand);
+
+ private:
+ LGapNode* LookupNode(LOperand* operand);
+ bool CanReach(LGapNode* a, LGapNode* b, int visited_id);
+ bool CanReach(LGapNode* a, LGapNode* b);
+ void RegisterMove(LMoveOperands move);
+ void AddResultMove(LOperand* from, LOperand* to);
+ void AddResultMove(LGapNode* from, LGapNode* to);
+ void ResolveCycle(LGapNode* start, LOperand* marker_operand);
+
+ ZoneList<LGapNode*> nodes_;
+ ZoneList<LGapNode*> identified_cycles_;
+ ZoneList<LMoveOperands> result_;
+ int next_visited_id_;
+};
+
class LCodeGen BASE_EMBEDDED {
public:
@@ -71,6 +93,7 @@ class LCodeGen BASE_EMBEDDED {
void FinishCode(Handle<Code> code);
// Deferred code support.
+ void DoDeferredGenericBinaryStub(LBinaryOperation* instr, Token::Value op);
void DoDeferredNumberTagD(LNumberTagD* instr);
void DoDeferredNumberTagI(LNumberTagI* instr);
void DoDeferredTaggedToI(LTaggedToI* instr);
@@ -80,6 +103,9 @@ class LCodeGen BASE_EMBEDDED {
// Parallel move support.
void DoParallelMove(LParallelMove* move);
+ // Emit frame translation commands for an environment.
+ void WriteTranslation(LEnvironment* environment, Translation* translation);
+
// Declare methods that deal with the individual node types.
#define DECLARE_DO(type) void Do##type(L##type* node);
LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_DO)
@@ -103,6 +129,9 @@ class LCodeGen BASE_EMBEDDED {
HGraph* graph() const { return chunk_->graph(); }
MacroAssembler* masm() const { return masm_; }
+ Register scratch0() { return r9; }
+ DwVfpRegister double_scratch0() { return d0; }
+
int GetNextEmittedBlock(int block);
LInstruction* GetNextInstruction();
@@ -147,7 +176,7 @@ class LCodeGen BASE_EMBEDDED {
int arity,
LInstruction* instr);
- void LoadPrototype(Register result, Handle<JSObject> prototype);
+ void LoadHeapObject(Register result, Handle<HeapObject> object);
void RegisterLazyDeoptimization(LInstruction* instr);
void RegisterEnvironmentForDeoptimization(LEnvironment* environment);
@@ -192,6 +221,9 @@ class LCodeGen BASE_EMBEDDED {
void RecordSafepointWithRegisters(LPointerMap* pointers,
int arguments,
int deoptimization_index);
+ void RecordSafepointWithRegistersAndDoubles(LPointerMap* pointers,
+ int arguments,
+ int deoptimization_index);
void RecordPosition(int position);
static Condition TokenToCondition(Token::Value op, bool is_unsigned);
@@ -237,6 +269,9 @@ class LCodeGen BASE_EMBEDDED {
// itself is emitted at the end of the generated code.
SafepointTableBuilder safepoints_;
+ // Compiler from a set of parallel moves to a sequential list of moves.
+ LGapResolver resolver_;
+
friend class LDeferredCode;
friend class LEnvironment;
friend class SafepointGenerator;
diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc
index 4a13146590..1028b0e69f 100644
--- a/deps/v8/src/arm/macro-assembler-arm.cc
+++ b/deps/v8/src/arm/macro-assembler-arm.cc
@@ -466,6 +466,25 @@ void MacroAssembler::PopSafepointRegisters() {
}
+void MacroAssembler::PushSafepointRegistersAndDoubles() {
+ PushSafepointRegisters();
+ sub(sp, sp, Operand(DwVfpRegister::kNumAllocatableRegisters *
+ kDoubleSize));
+ for (int i = 0; i < DwVfpRegister::kNumAllocatableRegisters; i++) {
+ vstr(DwVfpRegister::FromAllocationIndex(i), sp, i * kDoubleSize);
+ }
+}
+
+
+void MacroAssembler::PopSafepointRegistersAndDoubles() {
+ for (int i = 0; i < DwVfpRegister::kNumAllocatableRegisters; i++) {
+ vldr(DwVfpRegister::FromAllocationIndex(i), sp, i * kDoubleSize);
+ }
+ add(sp, sp, Operand(DwVfpRegister::kNumAllocatableRegisters *
+ kDoubleSize));
+ PopSafepointRegisters();
+}
+
int MacroAssembler::SafepointRegisterStackIndex(int reg_code) {
// The registers are pushed starting with the highest encoding,
// which means that lowest encodings are closest to the stack pointer.
@@ -519,6 +538,49 @@ void MacroAssembler::Strd(Register src1, Register src2,
}
+void MacroAssembler::ClearFPSCRBits(const uint32_t bits_to_clear,
+ const Register scratch,
+ const Condition cond) {
+ vmrs(scratch, cond);
+ bic(scratch, scratch, Operand(bits_to_clear), LeaveCC, cond);
+ vmsr(scratch, cond);
+}
+
+
+void MacroAssembler::VFPCompareAndSetFlags(const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Condition cond) {
+ // Compare and move FPSCR flags to the normal condition flags.
+ VFPCompareAndLoadFlags(src1, src2, pc, cond);
+}
+
+void MacroAssembler::VFPCompareAndSetFlags(const DwVfpRegister src1,
+ const double src2,
+ const Condition cond) {
+ // Compare and move FPSCR flags to the normal condition flags.
+ VFPCompareAndLoadFlags(src1, src2, pc, cond);
+}
+
+
+void MacroAssembler::VFPCompareAndLoadFlags(const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Register fpscr_flags,
+ const Condition cond) {
+ // Compare and load FPSCR.
+ vcmp(src1, src2, cond);
+ vmrs(fpscr_flags, cond);
+}
+
+void MacroAssembler::VFPCompareAndLoadFlags(const DwVfpRegister src1,
+ const double src2,
+ const Register fpscr_flags,
+ const Condition cond) {
+ // Compare and load FPSCR.
+ vcmp(src1, src2, cond);
+ vmrs(fpscr_flags, cond);
+}
+
+
void MacroAssembler::EnterFrame(StackFrame::Type type) {
// r0-r3: preserved
stm(db_w, sp, cp.bit() | fp.bit() | lr.bit());
@@ -675,7 +737,8 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
Handle<Code> code_constant,
Register code_reg,
Label* done,
- InvokeFlag flag) {
+ InvokeFlag flag,
+ PostCallGenerator* post_call_generator) {
bool definitely_matches = false;
Label regular_invoke;
@@ -731,6 +794,7 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
Handle<Code>(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline));
if (flag == CALL_FUNCTION) {
Call(adaptor, RelocInfo::CODE_TARGET);
+ if (post_call_generator != NULL) post_call_generator->Generate();
b(done);
} else {
Jump(adaptor, RelocInfo::CODE_TARGET);
@@ -743,12 +807,15 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
void MacroAssembler::InvokeCode(Register code,
const ParameterCount& expected,
const ParameterCount& actual,
- InvokeFlag flag) {
+ InvokeFlag flag,
+ PostCallGenerator* post_call_generator) {
Label done;
- InvokePrologue(expected, actual, Handle<Code>::null(), code, &done, flag);
+ InvokePrologue(expected, actual, Handle<Code>::null(), code, &done, flag,
+ post_call_generator);
if (flag == CALL_FUNCTION) {
Call(code);
+ if (post_call_generator != NULL) post_call_generator->Generate();
} else {
ASSERT(flag == JUMP_FUNCTION);
Jump(code);
@@ -782,7 +849,8 @@ void MacroAssembler::InvokeCode(Handle<Code> code,
void MacroAssembler::InvokeFunction(Register fun,
const ParameterCount& actual,
- InvokeFlag flag) {
+ InvokeFlag flag,
+ PostCallGenerator* post_call_generator) {
// Contract with called JS functions requires that function is passed in r1.
ASSERT(fun.is(r1));
@@ -799,7 +867,7 @@ void MacroAssembler::InvokeFunction(Register fun,
FieldMemOperand(r1, JSFunction::kCodeEntryOffset));
ParameterCount expected(expected_reg);
- InvokeCode(code_reg, expected, actual, flag);
+ InvokeCode(code_reg, expected, actual, flag, post_call_generator);
}
@@ -1669,10 +1737,12 @@ void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin) {
void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id,
- InvokeJSFlags flags) {
+ InvokeJSFlags flags,
+ PostCallGenerator* post_call_generator) {
GetBuiltinEntry(r2, id);
if (flags == CALL_JS) {
Call(r2);
+ if (post_call_generator != NULL) post_call_generator->Generate();
} else {
ASSERT(flags == JUMP_JS);
Jump(r2);
@@ -1795,7 +1865,7 @@ void MacroAssembler::Abort(const char* msg) {
}
#endif
// Disable stub call restrictions to always allow calls to abort.
- set_allow_stub_calls(true);
+ AllowStubCallsScope allow_scope(this, true);
mov(r0, Operand(p0));
push(r0);
diff --git a/deps/v8/src/arm/macro-assembler-arm.h b/deps/v8/src/arm/macro-assembler-arm.h
index 97bbb2fb67..324fbb2dde 100644
--- a/deps/v8/src/arm/macro-assembler-arm.h
+++ b/deps/v8/src/arm/macro-assembler-arm.h
@@ -33,6 +33,9 @@
namespace v8 {
namespace internal {
+// Forward declaration.
+class PostCallGenerator;
+
// ----------------------------------------------------------------------------
// Static helper functions
@@ -229,6 +232,9 @@ class MacroAssembler: public Assembler {
// RegList constant kSafepointSavedRegisters.
void PushSafepointRegisters();
void PopSafepointRegisters();
+ void PushSafepointRegistersAndDoubles();
+ void PopSafepointRegistersAndDoubles();
+
static int SafepointRegisterStackIndex(int reg_code);
// Load two consecutive registers with two consecutive memory locations.
@@ -243,6 +249,30 @@ class MacroAssembler: public Assembler {
const MemOperand& dst,
Condition cond = al);
+ // Clear specified FPSCR bits.
+ void ClearFPSCRBits(const uint32_t bits_to_clear,
+ const Register scratch,
+ const Condition cond = al);
+
+ // Compare double values and move the result to the normal condition flags.
+ void VFPCompareAndSetFlags(const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Condition cond = al);
+ void VFPCompareAndSetFlags(const DwVfpRegister src1,
+ const double src2,
+ const Condition cond = al);
+
+ // Compare double values and then load the fpscr flags to a register.
+ void VFPCompareAndLoadFlags(const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Register fpscr_flags,
+ const Condition cond = al);
+ void VFPCompareAndLoadFlags(const DwVfpRegister src1,
+ const double src2,
+ const Register fpscr_flags,
+ const Condition cond = al);
+
+
// ---------------------------------------------------------------------------
// Activation frames
@@ -281,7 +311,8 @@ class MacroAssembler: public Assembler {
void InvokeCode(Register code,
const ParameterCount& expected,
const ParameterCount& actual,
- InvokeFlag flag);
+ InvokeFlag flag,
+ PostCallGenerator* post_call_generator = NULL);
void InvokeCode(Handle<Code> code,
const ParameterCount& expected,
@@ -293,7 +324,8 @@ class MacroAssembler: public Assembler {
// current context to the context in the function before invoking.
void InvokeFunction(Register function,
const ParameterCount& actual,
- InvokeFlag flag);
+ InvokeFlag flag,
+ PostCallGenerator* post_call_generator = NULL);
void InvokeFunction(JSFunction* function,
const ParameterCount& actual,
@@ -379,12 +411,13 @@ class MacroAssembler: public Assembler {
// ---------------------------------------------------------------------------
// Allocation support
- // Allocate an object in new space. The object_size is specified in words (not
- // bytes). If the new space is exhausted control continues at the gc_required
- // label. The allocated object is returned in result. If the flag
- // tag_allocated_object is true the result is tagged as as a heap object. All
- // registers are clobbered also when control continues at the gc_required
- // label.
+ // Allocate an object in new space. The object_size is specified
+ // either in bytes or in words if the allocation flag SIZE_IN_WORDS
+ // is passed. If the new space is exhausted control continues at the
+ // gc_required label. The allocated object is returned in result. If
+ // the flag tag_allocated_object is true the result is tagged as as
+ // a heap object. All registers are clobbered also when control
+ // continues at the gc_required label.
void AllocateInNewSpace(int object_size,
Register result,
Register scratch1,
@@ -633,7 +666,9 @@ class MacroAssembler: public Assembler {
// Invoke specified builtin JavaScript function. Adds an entry to
// the unresolved list if the name does not resolve.
- void InvokeBuiltin(Builtins::JavaScript id, InvokeJSFlags flags);
+ void InvokeBuiltin(Builtins::JavaScript id,
+ InvokeJSFlags flags,
+ PostCallGenerator* post_call_generator = NULL);
// Store the code object for the given builtin in the target register and
// setup the function in r1.
@@ -684,6 +719,16 @@ class MacroAssembler: public Assembler {
add(reg, reg, Operand(reg), s);
}
+ // Try to convert int32 to smi. If the value is to large, preserve
+ // the original value and jump to not_a_smi. Destroys scratch and
+ // sets flags.
+ void TrySmiTag(Register reg, Label* not_a_smi, Register scratch) {
+ mov(scratch, reg);
+ SmiTag(scratch, SetCC);
+ b(vs, not_a_smi);
+ mov(reg, scratch);
+ }
+
void SmiUntag(Register reg) {
mov(reg, Operand(reg, ASR, kSmiTagSize));
}
@@ -741,7 +786,8 @@ class MacroAssembler: public Assembler {
Handle<Code> code_constant,
Register code_reg,
Label* done,
- InvokeFlag flag);
+ InvokeFlag flag,
+ PostCallGenerator* post_call_generator = NULL);
// Activation support.
void EnterFrame(StackFrame::Type type);
diff --git a/deps/v8/src/arm/regexp-macro-assembler-arm.cc b/deps/v8/src/arm/regexp-macro-assembler-arm.cc
index fbcc9f7f66..94da04240d 100644
--- a/deps/v8/src/arm/regexp-macro-assembler-arm.cc
+++ b/deps/v8/src/arm/regexp-macro-assembler-arm.cc
@@ -417,8 +417,8 @@ void RegExpMacroAssemblerARM::CheckNotBackReference(
void RegExpMacroAssemblerARM::CheckNotRegistersEqual(int reg1,
- int reg2,
- Label* on_not_equal) {
+ int reg2,
+ Label* on_not_equal) {
__ ldr(r0, register_location(reg1));
__ ldr(r1, register_location(reg2));
__ cmp(r0, r1);
@@ -426,7 +426,7 @@ void RegExpMacroAssemblerARM::CheckNotRegistersEqual(int reg1,
}
-void RegExpMacroAssemblerARM::CheckNotCharacter(uint32_t c,
+void RegExpMacroAssemblerARM::CheckNotCharacter(unsigned c,
Label* on_not_equal) {
__ cmp(current_character(), Operand(c));
BranchOrBacktrack(ne, on_not_equal);
@@ -442,8 +442,8 @@ void RegExpMacroAssemblerARM::CheckCharacterAfterAnd(uint32_t c,
}
-void RegExpMacroAssemblerARM::CheckNotCharacterAfterAnd(uint32_t c,
- uint32_t mask,
+void RegExpMacroAssemblerARM::CheckNotCharacterAfterAnd(unsigned c,
+ unsigned mask,
Label* on_not_equal) {
__ and_(r0, current_character(), Operand(mask));
__ cmp(r0, Operand(c));
diff --git a/deps/v8/src/arm/regexp-macro-assembler-arm.h b/deps/v8/src/arm/regexp-macro-assembler-arm.h
index 4e09f671c1..b487ba59d1 100644
--- a/deps/v8/src/arm/regexp-macro-assembler-arm.h
+++ b/deps/v8/src/arm/regexp-macro-assembler-arm.h
@@ -50,9 +50,9 @@ class RegExpMacroAssemblerARM: public NativeRegExpMacroAssembler {
virtual void Backtrack();
virtual void Bind(Label* label);
virtual void CheckAtStart(Label* on_at_start);
- virtual void CheckCharacter(uint32_t c, Label* on_equal);
- virtual void CheckCharacterAfterAnd(uint32_t c,
- uint32_t mask,
+ virtual void CheckCharacter(unsigned c, Label* on_equal);
+ virtual void CheckCharacterAfterAnd(unsigned c,
+ unsigned mask,
Label* on_equal);
virtual void CheckCharacterGT(uc16 limit, Label* on_greater);
virtual void CheckCharacterLT(uc16 limit, Label* on_less);
@@ -68,9 +68,9 @@ class RegExpMacroAssemblerARM: public NativeRegExpMacroAssembler {
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
Label* on_no_match);
virtual void CheckNotRegistersEqual(int reg1, int reg2, Label* on_not_equal);
- virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal);
- virtual void CheckNotCharacterAfterAnd(uint32_t c,
- uint32_t mask,
+ virtual void CheckNotCharacter(unsigned c, Label* on_not_equal);
+ virtual void CheckNotCharacterAfterAnd(unsigned c,
+ unsigned mask,
Label* on_not_equal);
virtual void CheckNotCharacterAfterMinusAnd(uc16 c,
uc16 minus,
diff --git a/deps/v8/src/arm/simulator-arm.cc b/deps/v8/src/arm/simulator-arm.cc
index 143b839362..00650576c0 100644
--- a/deps/v8/src/arm/simulator-arm.cc
+++ b/deps/v8/src/arm/simulator-arm.cc
@@ -2600,11 +2600,6 @@ void Simulator::DecodeVCMP(Instr* instr) {
precision = kDoublePrecision;
}
- if (instr->Bit(7) != 0) {
- // Raising exceptions for quiet NaNs are not supported.
- UNIMPLEMENTED(); // Not used by V8.
- }
-
int d = instr->VFPDRegCode(precision);
int m = 0;
if (instr->Opc2Field() == 0x4) {
@@ -2618,6 +2613,13 @@ void Simulator::DecodeVCMP(Instr* instr) {
dm_value = get_double_from_d_register(m);
}
+ // Raise exceptions for quiet NaNs if necessary.
+ if (instr->Bit(7) == 1) {
+ if (isnan(dd_value)) {
+ inv_op_vfp_flag_ = true;
+ }
+ }
+
Compute_FPSCR_Flags(dd_value, dm_value);
} else {
UNIMPLEMENTED(); // Not used by V8.
diff --git a/deps/v8/src/arm/stub-cache-arm.cc b/deps/v8/src/arm/stub-cache-arm.cc
index c2a9796c87..20e2801826 100644
--- a/deps/v8/src/arm/stub-cache-arm.cc
+++ b/deps/v8/src/arm/stub-cache-arm.cc
@@ -1952,7 +1952,7 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object,
__ cmp(r7, Operand(HeapNumber::kMantissaBits));
// If greater or equal, the argument is already round and in r0.
__ b(&restore_fpscr_and_return, ge);
- __ b(&slow);
+ __ b(&wont_fit_smi);
__ bind(&no_vfp_exception);
// Move the result back to general purpose register r0.
@@ -1965,7 +1965,7 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object,
__ mov(r0, Operand(r0, LSL, kSmiTagSize));
// Check for -0.
- __ cmp(r0, Operand(0));
+ __ cmp(r0, Operand(0, RelocInfo::NONE));
__ b(&restore_fpscr_and_return, ne);
// r5 already holds the HeapNumber exponent.
__ tst(r5, Operand(HeapNumber::kSignMask));
@@ -1980,10 +1980,10 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object,
__ Ret();
__ bind(&wont_fit_smi);
- __ bind(&slow);
// Restore FPCSR and fall to slow case.
__ vmsr(r3);
+ __ bind(&slow);
// Tail call the full function. We do not have to patch the receiver
// because the function makes no use of it.
__ InvokeFunction(function, arguments(), JUMP_FUNCTION);
diff --git a/deps/v8/src/array.js b/deps/v8/src/array.js
index 0f1e969f98..0d7a7cbc85 100644
--- a/deps/v8/src/array.js
+++ b/deps/v8/src/array.js
@@ -117,41 +117,59 @@ function Join(array, length, separator, convert) {
// Fast case for one-element arrays.
if (length == 1) {
var e = array[0];
- if (!IS_UNDEFINED(e) || (0 in array)) {
- if (IS_STRING(e)) return e;
- return convert(e);
- }
+ if (IS_STRING(e)) return e;
+ return convert(e);
}
// Construct an array for the elements.
- var elements;
- var elements_length = 0;
+ var elements = new $Array(length);
// We pull the empty separator check outside the loop for speed!
if (separator.length == 0) {
- elements = new $Array(length);
+ var elements_length = 0;
for (var i = 0; i < length; i++) {
var e = array[i];
- if (!IS_UNDEFINED(e) || (i in array)) {
+ if (!IS_UNDEFINED(e)) {
if (!IS_STRING(e)) e = convert(e);
elements[elements_length++] = e;
}
}
- } else {
- elements = new $Array(length << 1);
+ elements.length = elements_length;
+ var result = %_FastAsciiArrayJoin(elements, '');
+ if (!IS_UNDEFINED(result)) return result;
+ return %StringBuilderConcat(elements, elements_length, '');
+ }
+ // Non-empty separator case.
+ // If the first element is a number then use the heuristic that the
+ // remaining elements are also likely to be numbers.
+ if (!IS_NUMBER(array[0])) {
for (var i = 0; i < length; i++) {
var e = array[i];
- if (i != 0) elements[elements_length++] = separator;
- if (!IS_UNDEFINED(e) || (i in array)) {
+ if (!IS_STRING(e)) e = convert(e);
+ elements[i] = e;
+ }
+ } else {
+ for (var i = 0; i < length; i++) {
+ var e = array[i];
+ if (IS_NUMBER(e)) elements[i] = %_NumberToString(e);
+ else {
if (!IS_STRING(e)) e = convert(e);
- elements[elements_length++] = e;
+ elements[i] = e;
}
}
+ }
+ var result = %_FastAsciiArrayJoin(elements, separator);
+ if (!IS_UNDEFINED(result)) return result;
+
+ var length2 = (length << 1) - 1;
+ var j = length2;
+ var i = length;
+ elements[--j] = elements[--i];
+ while (i > 0) {
+ elements[--j] = separator;
+ elements[--j] = elements[--i];
}
- elements.length = elements_length;
- var result = %_FastAsciiArrayJoin(elements, "");
- if (!IS_UNDEFINED(result)) return result;
- return %StringBuilderConcat(elements, elements_length, '');
+ return %StringBuilderConcat(elements, length2, '');
} finally {
// Make sure to pop the visited array no matter what happens.
if (is_array) visited_arrays.pop();
@@ -160,7 +178,7 @@ function Join(array, length, separator, convert) {
function ConvertToString(x) {
- if (IS_STRING(x)) return x;
+ // Assumes x is a non-string.
if (IS_NUMBER(x)) return %_NumberToString(x);
if (IS_BOOLEAN(x)) return x ? 'true' : 'false';
return (IS_NULL_OR_UNDEFINED(x)) ? '' : %ToString(%DefaultString(x));
diff --git a/deps/v8/src/assembler.cc b/deps/v8/src/assembler.cc
index eeb84128d7..cdcf481891 100644
--- a/deps/v8/src/assembler.cc
+++ b/deps/v8/src/assembler.cc
@@ -66,6 +66,7 @@ namespace internal {
const double DoubleConstant::min_int = kMinInt;
const double DoubleConstant::one_half = 0.5;
+const double DoubleConstant::minus_zero = -0.0;
const double DoubleConstant::negative_infinity = -V8_INFINITY;
@@ -647,6 +648,11 @@ ExternalReference ExternalReference::the_hole_value_location() {
}
+ExternalReference ExternalReference::arguments_marker_location() {
+ return ExternalReference(Factory::arguments_marker().location());
+}
+
+
ExternalReference ExternalReference::roots_address() {
return ExternalReference(Heap::roots_address());
}
@@ -724,6 +730,12 @@ ExternalReference ExternalReference::address_of_one_half() {
}
+ExternalReference ExternalReference::address_of_minus_zero() {
+ return ExternalReference(reinterpret_cast<void*>(
+ const_cast<double*>(&DoubleConstant::minus_zero)));
+}
+
+
ExternalReference ExternalReference::address_of_negative_infinity() {
return ExternalReference(reinterpret_cast<void*>(
const_cast<double*>(&DoubleConstant::negative_infinity)));
diff --git a/deps/v8/src/assembler.h b/deps/v8/src/assembler.h
index b68ad38970..5817a15b45 100644
--- a/deps/v8/src/assembler.h
+++ b/deps/v8/src/assembler.h
@@ -50,6 +50,7 @@ class DoubleConstant: public AllStatic {
public:
static const double min_int;
static const double one_half;
+ static const double minus_zero;
static const double negative_infinity;
};
@@ -512,6 +513,9 @@ class ExternalReference BASE_EMBEDDED {
// Static variable Factory::the_hole_value.location()
static ExternalReference the_hole_value_location();
+ // Static variable Factory::arguments_marker.location()
+ static ExternalReference arguments_marker_location();
+
// Static variable Heap::roots_address()
static ExternalReference roots_address();
@@ -552,6 +556,7 @@ class ExternalReference BASE_EMBEDDED {
// Static variables containing common double constants.
static ExternalReference address_of_min_int();
static ExternalReference address_of_one_half();
+ static ExternalReference address_of_minus_zero();
static ExternalReference address_of_negative_infinity();
Address address() const {return reinterpret_cast<Address>(address_);}
diff --git a/deps/v8/src/ast.cc b/deps/v8/src/ast.cc
index 895ab67713..4fe89be172 100644
--- a/deps/v8/src/ast.cc
+++ b/deps/v8/src/ast.cc
@@ -166,12 +166,6 @@ bool FunctionLiteral::AllowsLazyCompilation() {
}
-bool FunctionLiteral::AllowOptimize() {
- // We can't deal with heap-allocated locals.
- return scope()->num_heap_slots() == 0;
-}
-
-
ObjectLiteral::Property::Property(Literal* key, Expression* value) {
emit_store_ = true;
key_ = key;
@@ -215,12 +209,16 @@ bool ObjectLiteral::Property::emit_store() {
bool IsEqualString(void* first, void* second) {
+ ASSERT((*reinterpret_cast<String**>(first))->IsString());
+ ASSERT((*reinterpret_cast<String**>(second))->IsString());
Handle<String> h1(reinterpret_cast<String**>(first));
Handle<String> h2(reinterpret_cast<String**>(second));
return (*h1)->Equals(*h2);
}
bool IsEqualSmi(void* first, void* second) {
+ ASSERT((*reinterpret_cast<Smi**>(first))->IsSmi());
+ ASSERT((*reinterpret_cast<Smi**>(second))->IsSmi());
Handle<Smi> h1(reinterpret_cast<Smi**>(first));
Handle<Smi> h2(reinterpret_cast<Smi**>(second));
return (*h1)->value() == (*h2)->value();
@@ -266,12 +264,12 @@ void ObjectLiteral::CalculateEmitStore() {
// If the key of a computed property is in the table, do not emit
// a store for the property later.
if (property->kind() == ObjectLiteral::Property::COMPUTED) {
- if (table->Lookup(literal, hash, false) != NULL) {
+ if (table->Lookup(key, hash, false) != NULL) {
property->set_emit_store(false);
}
}
// Add key to the table.
- table->Lookup(literal, hash, true);
+ table->Lookup(key, hash, true);
}
}
@@ -517,6 +515,9 @@ void Property::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
if (key()->IsPropertyName()) {
if (oracle->LoadIsBuiltin(this, Builtins::LoadIC_ArrayLength)) {
is_array_length_ = true;
+ } else if (oracle->LoadIsBuiltin(this,
+ Builtins::LoadIC_FunctionPrototype)) {
+ is_function_prototype_ = true;
} else {
Literal* lit_key = key()->AsLiteral();
ASSERT(lit_key != NULL && lit_key->handle()->IsString());
@@ -638,10 +639,19 @@ void Call::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
}
}
#endif
- if (receiver_types_ != NULL && receiver_types_->length() > 0) {
- Handle<Map> type = receiver_types_->at(0);
- is_monomorphic_ = oracle->CallIsMonomorphic(this);
- if (is_monomorphic_) is_monomorphic_ = ComputeTarget(type, name);
+ is_monomorphic_ = oracle->CallIsMonomorphic(this);
+ check_type_ = oracle->GetCallCheckType(this);
+ if (is_monomorphic_) {
+ Handle<Map> map;
+ if (receiver_types_ != NULL && receiver_types_->length() > 0) {
+ ASSERT(check_type_ == RECEIVER_MAP_CHECK);
+ map = receiver_types_->at(0);
+ } else {
+ ASSERT(check_type_ != RECEIVER_MAP_CHECK);
+ map = Handle<Map>(
+ oracle->GetPrototypeForPrimitiveCheck(check_type_)->map());
+ }
+ is_monomorphic_ = ComputeTarget(map, name);
}
}
diff --git a/deps/v8/src/ast.h b/deps/v8/src/ast.h
index ed447e343a..f55ddcd56b 100644
--- a/deps/v8/src/ast.h
+++ b/deps/v8/src/ast.h
@@ -1208,6 +1208,7 @@ class Property: public Expression {
is_monomorphic_(false),
receiver_types_(NULL),
is_array_length_(false),
+ is_function_prototype_(false),
is_arguments_access_(false) { }
DECLARE_NODE_TYPE(Property)
@@ -1220,6 +1221,8 @@ class Property: public Expression {
int position() const { return pos_; }
bool is_synthetic() const { return type_ == SYNTHETIC; }
+ bool IsFunctionPrototype() const { return is_function_prototype_; }
+
// Marks that this is actually an argument rewritten to a keyed property
// accessing the argument through the arguments shadow object.
void set_is_arguments_access(bool is_arguments_access) {
@@ -1249,6 +1252,7 @@ class Property: public Expression {
bool is_monomorphic_;
ZoneMapList* receiver_types_;
bool is_array_length_;
+ bool is_function_prototype_;
bool is_arguments_access_;
Handle<Map> monomorphic_receiver_type_;
@@ -1264,6 +1268,7 @@ class Call: public Expression {
arguments_(arguments),
pos_(pos),
is_monomorphic_(false),
+ check_type_(RECEIVER_MAP_CHECK),
receiver_types_(NULL),
return_id_(GetNextId()) {
}
@@ -1279,6 +1284,7 @@ class Call: public Expression {
void RecordTypeFeedback(TypeFeedbackOracle* oracle);
virtual ZoneMapList* GetReceiverTypes() { return receiver_types_; }
virtual bool IsMonomorphic() { return is_monomorphic_; }
+ CheckType check_type() const { return check_type_; }
Handle<JSFunction> target() { return target_; }
Handle<JSObject> holder() { return holder_; }
Handle<JSGlobalPropertyCell> cell() { return cell_; }
@@ -1302,6 +1308,7 @@ class Call: public Expression {
int pos_;
bool is_monomorphic_;
+ CheckType check_type_;
ZoneMapList* receiver_types_;
Handle<JSFunction> target_;
Handle<JSObject> holder_;
@@ -1391,7 +1398,7 @@ class BinaryOperation: public Expression {
: op_(op), left_(left), right_(right), pos_(pos), is_smi_only_(false) {
ASSERT(Token::IsBinaryOp(op));
right_id_ = (op == Token::AND || op == Token::OR)
- ? GetNextId()
+ ? static_cast<int>(GetNextId())
: AstNode::kNoNumber;
}
@@ -1710,7 +1717,6 @@ class FunctionLiteral: public Expression {
int num_parameters() { return num_parameters_; }
bool AllowsLazyCompilation();
- bool AllowOptimize();
Handle<String> debug_name() const {
if (name_->length() > 0) return name_;
diff --git a/deps/v8/src/builtins.cc b/deps/v8/src/builtins.cc
index 21381f15d5..a659c461c0 100644
--- a/deps/v8/src/builtins.cc
+++ b/deps/v8/src/builtins.cc
@@ -380,7 +380,7 @@ static inline MaybeObject* EnsureJSArrayWithWritableFastElements(
Object* receiver) {
if (!receiver->IsJSArray()) return NULL;
JSArray* array = JSArray::cast(receiver);
- HeapObject* elms = HeapObject::cast(array->elements());
+ HeapObject* elms = array->elements();
if (elms->map() == Heap::fixed_array_map()) return elms;
if (elms->map() == Heap::fixed_cow_array_map()) {
return array->EnsureWritableFastElements();
@@ -613,41 +613,42 @@ BUILTIN(ArraySlice) {
Object* receiver = *args.receiver();
FixedArray* elms;
int len = -1;
- { MaybeObject* maybe_elms_obj =
- EnsureJSArrayWithWritableFastElements(receiver);
- Object* elms_obj;
- if (maybe_elms_obj != NULL && maybe_elms_obj->ToObject(&elms_obj)) {
- if (!IsJSArrayFastElementMovingAllowed(JSArray::cast(receiver))) {
- return CallJsBuiltin("ArraySlice", args);
- }
- elms = FixedArray::cast(elms_obj);
- JSArray* array = JSArray::cast(receiver);
- ASSERT(array->HasFastElements());
+ if (receiver->IsJSArray()) {
+ JSArray* array = JSArray::cast(receiver);
+ if (!array->HasFastElements() ||
+ !IsJSArrayFastElementMovingAllowed(array)) {
+ return CallJsBuiltin("ArraySlice", args);
+ }
- len = Smi::cast(array->length())->value();
- } else {
- // Array.slice(arguments, ...) is quite a common idiom (notably more
- // than 50% of invocations in Web apps). Treat it in C++ as well.
- Map* arguments_map =
- Top::context()->global_context()->arguments_boilerplate()->map();
-
- bool is_arguments_object_with_fast_elements =
- receiver->IsJSObject()
- && JSObject::cast(receiver)->map() == arguments_map
- && JSObject::cast(receiver)->HasFastElements();
- if (!is_arguments_object_with_fast_elements) {
+ elms = FixedArray::cast(array->elements());
+ len = Smi::cast(array->length())->value();
+ } else {
+ // Array.slice(arguments, ...) is quite a common idiom (notably more
+ // than 50% of invocations in Web apps). Treat it in C++ as well.
+ Map* arguments_map =
+ Top::context()->global_context()->arguments_boilerplate()->map();
+
+ bool is_arguments_object_with_fast_elements =
+ receiver->IsJSObject()
+ && JSObject::cast(receiver)->map() == arguments_map
+ && JSObject::cast(receiver)->HasFastElements();
+ if (!is_arguments_object_with_fast_elements) {
+ return CallJsBuiltin("ArraySlice", args);
+ }
+ elms = FixedArray::cast(JSObject::cast(receiver)->elements());
+ Object* len_obj = JSObject::cast(receiver)
+ ->InObjectPropertyAt(Heap::arguments_length_index);
+ if (!len_obj->IsSmi()) {
+ return CallJsBuiltin("ArraySlice", args);
+ }
+ len = Smi::cast(len_obj)->value();
+ if (len > elms->length()) {
+ return CallJsBuiltin("ArraySlice", args);
+ }
+ for (int i = 0; i < len; i++) {
+ if (elms->get(i) == Heap::the_hole_value()) {
return CallJsBuiltin("ArraySlice", args);
}
- elms = FixedArray::cast(JSObject::cast(receiver)->elements());
- len = elms->length();
-#ifdef DEBUG
- // Arguments object by construction should have no holes, check it.
- if (FLAG_enable_slow_asserts) {
- for (int i = 0; i < len; i++) {
- ASSERT(elms->get(i) != Heap::the_hole_value());
- }
- }
-#endif
}
}
ASSERT(len >= 0);
diff --git a/deps/v8/src/code-stubs.cc b/deps/v8/src/code-stubs.cc
index 1b0d8b0b6f..ba027e9332 100644
--- a/deps/v8/src/code-stubs.cc
+++ b/deps/v8/src/code-stubs.cc
@@ -49,8 +49,10 @@ bool CodeStub::FindCodeInCache(Code** code_out) {
void CodeStub::GenerateCode(MacroAssembler* masm) {
// Update the static counter each time a new code stub is generated.
Counters::code_stubs.Increment();
+
// Nested stubs are not allowed for leafs.
- masm->set_allow_stub_calls(AllowsStubCalls());
+ AllowStubCallsScope allow_scope(masm, AllowsStubCalls());
+
// Generate the code for the stub.
masm->set_generating_stub(true);
Generate(masm);
@@ -197,4 +199,34 @@ void ICCompareStub::Generate(MacroAssembler* masm) {
}
+const char* InstanceofStub::GetName() {
+ if (name_ != NULL) return name_;
+ const int kMaxNameLength = 100;
+ name_ = Bootstrapper::AllocateAutoDeletedArray(kMaxNameLength);
+ if (name_ == NULL) return "OOM";
+
+ const char* args = "";
+ if (HasArgsInRegisters()) {
+ args = "_REGS";
+ }
+
+ const char* inline_check = "";
+ if (HasCallSiteInlineCheck()) {
+ inline_check = "_INLINE";
+ }
+
+ const char* return_true_false_object = "";
+ if (ReturnTrueFalseObject()) {
+ return_true_false_object = "_TRUEFALSE";
+ }
+
+ OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
+ "InstanceofStub%s%s%s",
+ args,
+ inline_check,
+ return_true_false_object);
+ return name_;
+}
+
+
} } // namespace v8::internal
diff --git a/deps/v8/src/code-stubs.h b/deps/v8/src/code-stubs.h
index b7804b77f2..76f29f082f 100644
--- a/deps/v8/src/code-stubs.h
+++ b/deps/v8/src/code-stubs.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -34,7 +34,7 @@ namespace v8 {
namespace internal {
// List of code stubs used on all platforms. The order in this list is important
-// as only the stubs up to and including RecordWrite allows nested stub calls.
+// as only the stubs up to and including Instanceof allows nested stub calls.
#define CODE_STUB_LIST_ALL_PLATFORMS(V) \
V(CallFunction) \
V(GenericBinaryOp) \
@@ -48,7 +48,7 @@ namespace internal {
V(CompareIC) \
V(MathPow) \
V(TranscendentalCache) \
- V(RecordWrite) \
+ V(Instanceof) \
V(ConvertToDouble) \
V(WriteInt32ToHeapNumber) \
V(IntegerMod) \
@@ -59,7 +59,6 @@ namespace internal {
V(GenericUnaryOp) \
V(RevertToNumber) \
V(ToBoolean) \
- V(Instanceof) \
V(CounterOp) \
V(ArgumentsAccess) \
V(RegExpExec) \
@@ -180,7 +179,7 @@ class CodeStub BASE_EMBEDDED {
MajorKeyBits::encode(MajorKey());
}
- bool AllowsStubCalls() { return MajorKey() <= RecordWrite; }
+ bool AllowsStubCalls() { return MajorKey() <= Instanceof; }
class MajorKeyBits: public BitField<uint32_t, 0, kMajorBits> {};
class MinorKeyBits: public BitField<uint32_t, kMajorBits, kMinorBits> {};
@@ -327,22 +326,38 @@ class InstanceofStub: public CodeStub {
public:
enum Flags {
kNoFlags = 0,
- kArgsInRegisters = 1 << 0
+ kArgsInRegisters = 1 << 0,
+ kCallSiteInlineCheck = 1 << 1,
+ kReturnTrueFalseObject = 1 << 2
};
- explicit InstanceofStub(Flags flags) : flags_(flags) { }
+ explicit InstanceofStub(Flags flags) : flags_(flags), name_(NULL) { }
+
+ static Register left();
+ static Register right();
void Generate(MacroAssembler* masm);
private:
Major MajorKey() { return Instanceof; }
- int MinorKey() { return args_in_registers() ? 1 : 0; }
+ int MinorKey() { return static_cast<int>(flags_); }
- bool args_in_registers() {
+ bool HasArgsInRegisters() const {
return (flags_ & kArgsInRegisters) != 0;
}
+ bool HasCallSiteInlineCheck() const {
+ return (flags_ & kCallSiteInlineCheck) != 0;
+ }
+
+ bool ReturnTrueFalseObject() const {
+ return (flags_ & kReturnTrueFalseObject) != 0;
+ }
+
+ const char* GetName();
+
Flags flags_;
+ char* name_;
};
@@ -707,6 +722,10 @@ class CallFunctionStub: public CodeStub {
void Generate(MacroAssembler* masm);
+ static int ExtractArgcFromMinorKey(int minor_key) {
+ return ArgcBits::decode(minor_key);
+ }
+
private:
int argc_;
InLoopFlag in_loop_;
@@ -738,11 +757,6 @@ class CallFunctionStub: public CodeStub {
bool ReceiverMightBeValue() {
return (flags_ & RECEIVER_MIGHT_BE_VALUE) != 0;
}
-
- public:
- static int ExtractArgcFromMinorKey(int minor_key) {
- return ArgcBits::decode(minor_key);
- }
};
@@ -902,6 +916,24 @@ class StringCharAtGenerator {
DISALLOW_COPY_AND_ASSIGN(StringCharAtGenerator);
};
+
+class AllowStubCallsScope {
+ public:
+ AllowStubCallsScope(MacroAssembler* masm, bool allow)
+ : masm_(masm), previous_allow_(masm->allow_stub_calls()) {
+ masm_->set_allow_stub_calls(allow);
+ }
+ ~AllowStubCallsScope() {
+ masm_->set_allow_stub_calls(previous_allow_);
+ }
+
+ private:
+ MacroAssembler* masm_;
+ bool previous_allow_;
+
+ DISALLOW_COPY_AND_ASSIGN(AllowStubCallsScope);
+};
+
} } // namespace v8::internal
#endif // V8_CODE_STUBS_H_
diff --git a/deps/v8/src/compiler.cc b/deps/v8/src/compiler.cc
index e4864e4801..0bd973045a 100755
--- a/deps/v8/src/compiler.cc
+++ b/deps/v8/src/compiler.cc
@@ -92,6 +92,25 @@ CompilationInfo::CompilationInfo(Handle<JSFunction> closure)
}
+void CompilationInfo::DisableOptimization() {
+ if (FLAG_optimize_closures) {
+ // If we allow closures optimizations and it's an optimizable closure
+ // mark it correspondingly.
+ bool is_closure = closure_.is_null() && !scope_->HasTrivialOuterContext();
+ if (is_closure) {
+ bool is_optimizable_closure =
+ !scope_->outer_scope_calls_eval() && !scope_->inside_with();
+ if (is_optimizable_closure) {
+ SetMode(BASE);
+ return;
+ }
+ }
+ }
+
+ SetMode(NONOPT);
+}
+
+
// Determine whether to use the full compiler for all code. If the flag
// --always-full-compiler is specified this is the case. For the virtual frame
// based compiler the full compiler is also used if a debugger is connected, as
@@ -262,7 +281,9 @@ static bool MakeCrankshaftCode(CompilationInfo* info) {
HTracer::Instance()->TraceCompilation(info->function());
}
- TypeFeedbackOracle oracle(Handle<Code>(info->shared_info()->code()));
+ TypeFeedbackOracle oracle(
+ Handle<Code>(info->shared_info()->code()),
+ Handle<Context>(info->closure()->context()->global_context()));
HGraphBuilder builder(&oracle);
HPhase phase(HPhase::kTotal);
HGraph* graph = builder.CreateGraph(info);
diff --git a/deps/v8/src/compiler.h b/deps/v8/src/compiler.h
index 1176c6941a..68066aa67a 100644
--- a/deps/v8/src/compiler.h
+++ b/deps/v8/src/compiler.h
@@ -114,7 +114,7 @@ class CompilationInfo BASE_EMBEDDED {
SetMode(OPTIMIZE);
osr_ast_id_ = osr_ast_id;
}
- void DisableOptimization() { SetMode(NONOPT); }
+ void DisableOptimization();
// Deoptimization support.
bool HasDeoptimizationSupport() const { return supports_deoptimization_; }
@@ -125,9 +125,7 @@ class CompilationInfo BASE_EMBEDDED {
// Determine whether or not we can adaptively optimize.
bool AllowOptimize() {
- return V8::UseCrankshaft() &&
- !closure_.is_null() &&
- function_->AllowOptimize();
+ return V8::UseCrankshaft() && !closure_.is_null();
}
private:
diff --git a/deps/v8/src/cpu-profiler.cc b/deps/v8/src/cpu-profiler.cc
index f13c0eefab..fcf539f3bd 100644
--- a/deps/v8/src/cpu-profiler.cc
+++ b/deps/v8/src/cpu-profiler.cc
@@ -47,7 +47,8 @@ static const int kTickSamplesBufferChunksCount = 16;
ProfilerEventsProcessor::ProfilerEventsProcessor(ProfileGenerator* generator)
- : generator_(generator),
+ : Thread("v8:ProfEvntProc"),
+ generator_(generator),
running_(true),
ticks_buffer_(sizeof(TickSampleEventRecord),
kTickSamplesBufferChunkSize,
diff --git a/deps/v8/src/d8-debug.cc b/deps/v8/src/d8-debug.cc
index 5f3ed766ab..8a3886c676 100644
--- a/deps/v8/src/d8-debug.cc
+++ b/deps/v8/src/d8-debug.cc
@@ -34,12 +34,21 @@
namespace v8 {
-void PrintPrompt() {
- printf("dbg> ");
+static bool was_running = true;
+
+void PrintPrompt(bool is_running) {
+ const char* prompt = is_running? "> " : "dbg> ";
+ was_running = is_running;
+ printf("%s", prompt);
fflush(stdout);
}
+void PrintPrompt() {
+ PrintPrompt(was_running);
+}
+
+
void HandleDebugEvent(DebugEvent event,
Handle<Object> exec_state,
Handle<Object> event_data,
@@ -91,7 +100,7 @@ void HandleDebugEvent(DebugEvent event,
bool running = false;
while (!running) {
char command[kBufferSize];
- PrintPrompt();
+ PrintPrompt(running);
char* str = fgets(command, kBufferSize, stdin);
if (str == NULL) break;
@@ -284,7 +293,9 @@ void RemoteDebugger::HandleMessageReceived(char* message) {
} else {
printf("???\n");
}
- PrintPrompt();
+
+ bool is_running = details->Get(String::New("running"))->ToBoolean()->Value();
+ PrintPrompt(is_running);
}
diff --git a/deps/v8/src/d8-debug.h b/deps/v8/src/d8-debug.h
index c7acc2f79f..4e33e6f4c4 100644
--- a/deps/v8/src/d8-debug.h
+++ b/deps/v8/src/d8-debug.h
@@ -98,7 +98,8 @@ class RemoteDebugger {
class ReceiverThread: public i::Thread {
public:
explicit ReceiverThread(RemoteDebugger* remote_debugger)
- : remote_debugger_(remote_debugger) {}
+ : Thread("d8:ReceiverThrd"),
+ remote_debugger_(remote_debugger) {}
~ReceiverThread() {}
void Run();
@@ -112,7 +113,8 @@ class ReceiverThread: public i::Thread {
class KeyboardThread: public i::Thread {
public:
explicit KeyboardThread(RemoteDebugger* remote_debugger)
- : remote_debugger_(remote_debugger) {}
+ : Thread("d8:KeyboardThrd"),
+ remote_debugger_(remote_debugger) {}
~KeyboardThread() {}
void Run();
diff --git a/deps/v8/src/d8.cc b/deps/v8/src/d8.cc
index 5a1e63a763..f0da7ac8bc 100644
--- a/deps/v8/src/d8.cc
+++ b/deps/v8/src/d8.cc
@@ -599,7 +599,8 @@ void Shell::RunShell() {
class ShellThread : public i::Thread {
public:
ShellThread(int no, i::Vector<const char> files)
- : no_(no), files_(files) { }
+ : Thread("d8:ShellThread"),
+ no_(no), files_(files) { }
virtual void Run();
private:
int no_;
diff --git a/deps/v8/src/d8.js b/deps/v8/src/d8.js
index a758e09c14..b0edb706ad 100644
--- a/deps/v8/src/d8.js
+++ b/deps/v8/src/d8.js
@@ -110,17 +110,32 @@ Debug.ScopeType = { Global: 0,
const kNoFrame = -1;
Debug.State = {
currentFrame: kNoFrame,
+ displaySourceStartLine: -1,
+ displaySourceEndLine: -1,
currentSourceLine: -1
}
var trace_compile = false; // Tracing all compile events?
+var trace_debug_json = false; // Tracing all debug json packets?
+var last_cmd_line = '';
+var repeat_cmd_line = '';
+var is_running = true;
+
+// Copied from debug-delay.js. This is needed below:
+function ScriptTypeFlag(type) {
+ return (1 << type);
+}
// Process a debugger JSON message into a display text and a running status.
// This function returns an object with properties "text" and "running" holding
// this information.
function DebugMessageDetails(message) {
+ if (trace_debug_json) {
+ print("received: '" + message + "'");
+ }
// Convert the JSON string to an object.
var response = new ProtocolPackage(message);
+ is_running = response.running();
if (response.type() == 'event') {
return DebugEventDetails(response);
@@ -161,6 +176,8 @@ function DebugEventDetails(response) {
result += '\n';
result += SourceUnderline(body.sourceLineText, body.sourceColumn);
Debug.State.currentSourceLine = body.sourceLine;
+ Debug.State.displaySourceStartLine = -1;
+ Debug.State.displaySourceEndLine = -1;
Debug.State.currentFrame = 0;
details.text = result;
break;
@@ -180,10 +197,14 @@ function DebugEventDetails(response) {
result += '\n';
result += SourceUnderline(body.sourceLineText, body.sourceColumn);
Debug.State.currentSourceLine = body.sourceLine;
+ Debug.State.displaySourceStartLine = -1;
+ Debug.State.displaySourceEndLine = -1;
Debug.State.currentFrame = 0;
} else {
result += ' (empty stack)';
Debug.State.currentSourceLine = -1;
+ Debug.State.displaySourceStartLine = -1;
+ Debug.State.displaySourceEndLine = -1;
Debug.State.currentFrame = kNoFrame;
}
details.text = result;
@@ -202,6 +223,10 @@ function DebugEventDetails(response) {
details.text = result;
break;
+ case 'scriptCollected':
+ details.text = result;
+ break;
+
default:
details.text = 'Unknown debug event ' + response.event();
}
@@ -254,7 +279,11 @@ function SourceUnderline(source_text, position) {
// Converts a text command to a JSON request.
function DebugCommandToJSONRequest(cmd_line) {
- return new DebugRequest(cmd_line).JSONRequest();
+ var result = new DebugRequest(cmd_line).JSONRequest();
+ if (trace_debug_json && result) {
+ print("sending: '" + result + "'");
+ }
+ return result;
};
@@ -266,6 +295,20 @@ function DebugRequest(cmd_line) {
return;
}
+ // Check for a simple carriage return to repeat the last command:
+ var is_repeating = false;
+ if (cmd_line == '\n') {
+ if (is_running) {
+ cmd_line = 'break'; // Not in debugger mode, break with a frame request.
+ } else {
+ cmd_line = repeat_cmd_line; // use command to repeat.
+ is_repeating = true;
+ }
+ }
+ if (!is_running) { // Only save the command if in debugger mode.
+ repeat_cmd_line = cmd_line; // save last command.
+ }
+
// Trim string for leading and trailing whitespace.
cmd_line = cmd_line.replace(/^\s+|\s+$/g, '');
@@ -281,6 +324,13 @@ function DebugRequest(cmd_line) {
args = cmd_line.slice(pos).replace(/^\s+|\s+$/g, '');
}
+ if ((cmd === undefined) || !cmd) {
+ this.request_ = void 0;
+ return;
+ }
+
+ last_cmd = cmd;
+
// Switch on command.
switch (cmd) {
case 'continue':
@@ -290,7 +340,22 @@ function DebugRequest(cmd_line) {
case 'step':
case 's':
- this.request_ = this.stepCommandToJSONRequest_(args);
+ this.request_ = this.stepCommandToJSONRequest_(args, 'in');
+ break;
+
+ case 'stepi':
+ case 'si':
+ this.request_ = this.stepCommandToJSONRequest_(args, 'min');
+ break;
+
+ case 'next':
+ case 'n':
+ this.request_ = this.stepCommandToJSONRequest_(args, 'next');
+ break;
+
+ case 'finish':
+ case 'fin':
+ this.request_ = this.stepCommandToJSONRequest_(args, 'out');
break;
case 'backtrace':
@@ -311,6 +376,26 @@ function DebugRequest(cmd_line) {
this.request_ = this.scopeCommandToJSONRequest_(args);
break;
+ case 'disconnect':
+ case 'exit':
+ case 'quit':
+ this.request_ = this.disconnectCommandToJSONRequest_(args);
+ break;
+
+ case 'up':
+ this.request_ =
+ this.frameCommandToJSONRequest_('' +
+ (Debug.State.currentFrame + 1));
+ break;
+
+ case 'down':
+ case 'do':
+ this.request_ =
+ this.frameCommandToJSONRequest_('' +
+ (Debug.State.currentFrame - 1));
+ break;
+
+ case 'set':
case 'print':
case 'p':
this.request_ = this.printCommandToJSONRequest_(args);
@@ -328,11 +413,17 @@ function DebugRequest(cmd_line) {
this.request_ = this.instancesCommandToJSONRequest_(args);
break;
+ case 'list':
+ case 'l':
+ this.request_ = this.listCommandToJSONRequest_(args);
+ break;
case 'source':
this.request_ = this.sourceCommandToJSONRequest_(args);
break;
case 'scripts':
+ case 'script':
+ case 'scr':
this.request_ = this.scriptsCommandToJSONRequest_(args);
break;
@@ -347,6 +438,8 @@ function DebugRequest(cmd_line) {
break;
case 'clear':
+ case 'delete':
+ case 'd':
this.request_ = this.clearCommandToJSONRequest_(args);
break;
@@ -354,7 +447,42 @@ function DebugRequest(cmd_line) {
this.request_ = this.threadsCommandToJSONRequest_(args);
break;
+ case 'cond':
+ this.request_ = this.changeBreakpointCommandToJSONRequest_(args, 'cond');
+ break;
+
+ case 'enable':
+ case 'en':
+ this.request_ =
+ this.changeBreakpointCommandToJSONRequest_(args, 'enable');
+ break;
+
+ case 'disable':
+ case 'dis':
+ this.request_ =
+ this.changeBreakpointCommandToJSONRequest_(args, 'disable');
+ break;
+
+ case 'ignore':
+ this.request_ =
+ this.changeBreakpointCommandToJSONRequest_(args, 'ignore');
+ break;
+
+ case 'info':
+ case 'inf':
+ this.request_ = this.infoCommandToJSONRequest_(args);
+ break;
+
+ case 'flags':
+ this.request_ = this.v8FlagsToJSONRequest_(args);
+ break;
+
+ case 'gc':
+ this.request_ = this.gcToJSONRequest_(args);
+ break;
+
case 'trace':
+ case 'tr':
// Return undefined to indicate command handled internally (no JSON).
this.request_ = void 0;
this.traceCommand_(args);
@@ -370,8 +498,6 @@ function DebugRequest(cmd_line) {
default:
throw new Error('Unknown command "' + cmd + '"');
}
-
- last_cmd = cmd;
}
DebugRequest.prototype.JSONRequest = function() {
@@ -465,59 +591,73 @@ DebugRequest.prototype.continueCommandToJSONRequest_ = function(args) {
// Create a JSON request for the step command.
-DebugRequest.prototype.stepCommandToJSONRequest_ = function(args) {
+DebugRequest.prototype.stepCommandToJSONRequest_ = function(args, type) {
// Requesting a step is through the continue command with additional
// arguments.
var request = this.createRequest('continue');
request.arguments = {};
// Process arguments if any.
+
+ // Only process args if the command is 'step' which is indicated by type being
+ // set to 'in'. For all other commands, ignore the args.
if (args && args.length > 0) {
- args = args.split(/\s*[ ]+\s*/g);
+ args = args.split(/\s+/g);
if (args.length > 2) {
throw new Error('Invalid step arguments.');
}
if (args.length > 0) {
- // Get step count argument if any.
- if (args.length == 2) {
- var stepcount = parseInt(args[1]);
- if (isNaN(stepcount) || stepcount <= 0) {
- throw new Error('Invalid step count argument "' + args[0] + '".');
+ // Check if we have a gdb stype step command. If so, the 1st arg would
+ // be the step count. If it's not a number, then assume that we're
+ // parsing for the legacy v8 step command.
+ var stepcount = Number(args[0]);
+ if (stepcount == Number.NaN) {
+ // No step count at arg 1. Process as legacy d8 step command:
+ if (args.length == 2) {
+ var stepcount = parseInt(args[1]);
+ if (isNaN(stepcount) || stepcount <= 0) {
+ throw new Error('Invalid step count argument "' + args[0] + '".');
+ }
+ request.arguments.stepcount = stepcount;
}
- request.arguments.stepcount = stepcount;
- }
- // Get the step action.
- switch (args[0]) {
- case 'in':
- case 'i':
- request.arguments.stepaction = 'in';
- break;
+ // Get the step action.
+ switch (args[0]) {
+ case 'in':
+ case 'i':
+ request.arguments.stepaction = 'in';
+ break;
- case 'min':
- case 'm':
- request.arguments.stepaction = 'min';
- break;
+ case 'min':
+ case 'm':
+ request.arguments.stepaction = 'min';
+ break;
- case 'next':
- case 'n':
- request.arguments.stepaction = 'next';
- break;
+ case 'next':
+ case 'n':
+ request.arguments.stepaction = 'next';
+ break;
- case 'out':
- case 'o':
- request.arguments.stepaction = 'out';
- break;
+ case 'out':
+ case 'o':
+ request.arguments.stepaction = 'out';
+ break;
- default:
- throw new Error('Invalid step argument "' + args[0] + '".');
+ default:
+ throw new Error('Invalid step argument "' + args[0] + '".');
+ }
+
+ } else {
+ // gdb style step commands:
+ request.arguments.stepaction = type;
+ request.arguments.stepcount = stepcount;
}
}
} else {
- // Default is step next.
- request.arguments.stepaction = 'next';
+ // Default is step of the specified type.
+ request.arguments.stepaction = type;
}
return request.toJSONProtocol();
@@ -648,6 +788,41 @@ DebugRequest.prototype.instancesCommandToJSONRequest_ = function(args) {
};
+// Create a JSON request for the list command.
+DebugRequest.prototype.listCommandToJSONRequest_ = function(args) {
+
+ // Default is ten lines starting five lines before the current location.
+ if (Debug.State.displaySourceEndLine == -1) {
+ // If we list forwards, we will start listing after the last source end
+ // line. Set it to start from 5 lines before the current location.
+ Debug.State.displaySourceEndLine = Debug.State.currentSourceLine - 5;
+ // If we list backwards, we will start listing backwards from the last
+ // source start line. Set it to start from 1 lines before the current
+ // location.
+ Debug.State.displaySourceStartLine = Debug.State.currentSourceLine + 1;
+ }
+
+ var from = Debug.State.displaySourceEndLine + 1;
+ var lines = 10;
+
+ // Parse the arguments.
+ args = args.split(/\s*,\s*/g);
+ if (args == '') {
+ } else if ((args.length == 1) && (args[0] == '-')) {
+ from = Debug.State.displaySourceStartLine - lines;
+ } else if (args.length == 2) {
+ from = parseInt(args[0]);
+ lines = parseInt(args[1]) - from + 1; // inclusive of the ending line.
+ } else {
+ throw new Error('Invalid list arguments.');
+ }
+ Debug.State.displaySourceStartLine = from;
+ Debug.State.displaySourceEndLine = from + lines - 1;
+ var sourceArgs = '' + from + ' ' + lines;
+ return this.sourceCommandToJSONRequest_(sourceArgs);
+};
+
+
// Create a JSON request for the source command.
DebugRequest.prototype.sourceCommandToJSONRequest_ = function(args) {
// Build a evaluate request from the text command.
@@ -709,7 +884,10 @@ DebugRequest.prototype.scriptsCommandToJSONRequest_ = function(args) {
break;
default:
- throw new Error('Invalid argument "' + args[0] + '".');
+ // If the arg is not one of the know one aboves, then it must be a
+ // filter used for filtering the results:
+ request.arguments.filter = args[0];
+ break;
}
}
@@ -731,6 +909,8 @@ DebugRequest.prototype.breakCommandToJSONRequest_ = function(args) {
var request = this.createRequest('setbreakpoint');
+ // Break the args into target spec and condition if appropriate.
+
// Check for breakpoint condition.
pos = args.indexOf(' ');
if (pos > 0) {
@@ -801,6 +981,178 @@ DebugRequest.prototype.clearCommandToJSONRequest_ = function(args) {
};
+// Create a JSON request for the change breakpoint command.
+DebugRequest.prototype.changeBreakpointCommandToJSONRequest_ =
+ function(args, command) {
+
+ var request;
+
+ // Check for exception breaks first:
+ // en[able] exc[eptions] [all|unc[aught]]
+ // en[able] [all|unc[aught]] exc[eptions]
+ // dis[able] exc[eptions] [all|unc[aught]]
+ // dis[able] [all|unc[aught]] exc[eptions]
+ if ((command == 'enable' || command == 'disable') &&
+ args && args.length > 1) {
+ var nextPos = args.indexOf(' ');
+ var arg1 = (nextPos > 0) ? args.substring(0, nextPos) : args;
+ var excType = null;
+
+ // Check for:
+ // en[able] exc[eptions] [all|unc[aught]]
+ // dis[able] exc[eptions] [all|unc[aught]]
+ if (arg1 == 'exc' || arg1 == 'exception' || arg1 == 'exceptions') {
+
+ var arg2 = (nextPos > 0) ?
+ args.substring(nextPos + 1, args.length) : 'all';
+ if (!arg2) {
+ arg2 = 'all'; // if unspecified, set for all.
+ } if (arg2 == 'unc') { // check for short cut.
+ arg2 = 'uncaught';
+ }
+ excType = arg2;
+
+ // Check for:
+ // en[able] [all|unc[aught]] exc[eptions]
+ // dis[able] [all|unc[aught]] exc[eptions]
+ } else if (arg1 == 'all' || arg1 == 'unc' || arg1 == 'uncaught') {
+
+ var arg2 = (nextPos > 0) ?
+ args.substring(nextPos + 1, args.length) : null;
+ if (arg2 == 'exc' || arg1 == 'exception' || arg1 == 'exceptions') {
+ excType = arg1;
+ if (excType == 'unc') {
+ excType = 'uncaught';
+ }
+ }
+ }
+
+ // If we matched one of the command formats, then excType will be non-null:
+ if (excType) {
+ // Build a evaluate request from the text command.
+ request = this.createRequest('setexceptionbreak');
+
+ request.arguments = {};
+ request.arguments.type = excType;
+ request.arguments.enabled = (command == 'enable');
+
+ return request.toJSONProtocol();
+ }
+ }
+
+ // Build a evaluate request from the text command.
+ request = this.createRequest('changebreakpoint');
+
+ // Process arguments if any.
+ if (args && args.length > 0) {
+ request.arguments = {};
+ var pos = args.indexOf(' ');
+ var breakpointArg = args;
+ var otherArgs;
+ if (pos > 0) {
+ breakpointArg = args.substring(0, pos);
+ otherArgs = args.substring(pos + 1, args.length);
+ }
+
+ request.arguments.breakpoint = parseInt(breakpointArg);
+
+ switch(command) {
+ case 'cond':
+ request.arguments.condition = otherArgs ? otherArgs : null;
+ break;
+ case 'enable':
+ request.arguments.enabled = true;
+ break;
+ case 'disable':
+ request.arguments.enabled = false;
+ break;
+ case 'ignore':
+ request.arguments.ignoreCount = parseInt(otherArgs);
+ break;
+ default:
+ throw new Error('Invalid arguments.');
+ }
+ } else {
+ throw new Error('Invalid arguments.');
+ }
+
+ return request.toJSONProtocol();
+};
+
+
+// Create a JSON request for the disconnect command.
+DebugRequest.prototype.disconnectCommandToJSONRequest_ = function(args) {
+ var request;
+ request = this.createRequest('disconnect');
+ return request.toJSONProtocol();
+};
+
+
+// Create a JSON request for the info command.
+DebugRequest.prototype.infoCommandToJSONRequest_ = function(args) {
+ var request;
+ if (args && (args == 'break' || args == 'br')) {
+ // Build a evaluate request from the text command.
+ request = this.createRequest('listbreakpoints');
+ last_cmd = 'info break';
+ } else if (args && (args == 'locals' || args == 'lo')) {
+ // Build a evaluate request from the text command.
+ request = this.createRequest('frame');
+ last_cmd = 'info locals';
+ } else if (args && (args == 'args' || args == 'ar')) {
+ // Build a evaluate request from the text command.
+ request = this.createRequest('frame');
+ last_cmd = 'info args';
+ } else {
+ throw new Error('Invalid info arguments.');
+ }
+
+ return request.toJSONProtocol();
+};
+
+
+DebugRequest.prototype.v8FlagsToJSONRequest_ = function(args) {
+ var request;
+ request = this.createRequest('v8flags');
+ request.arguments = {};
+ request.arguments.flags = args;
+ return request.toJSONProtocol();
+};
+
+
+DebugRequest.prototype.gcToJSONRequest_ = function(args) {
+ var request;
+ if (!args) {
+ args = 'all';
+ }
+ var args = args.split(/\s+/g);
+ var cmd = args[0];
+
+ switch(cmd) {
+ case 'all':
+ case 'quick':
+ case 'full':
+ case 'young':
+ case 'old':
+ case 'compact':
+ case 'sweep':
+ case 'scavenge': {
+ if (cmd == 'young') { cmd = 'quick'; }
+ else if (cmd == 'old') { cmd = 'full'; }
+
+ request = this.createRequest('gc');
+ request.arguments = {};
+ request.arguments.type = cmd;
+ break;
+ }
+ // Else fall thru to the default case below to report the error.
+ default:
+ throw new Error('Missing arguments after ' + cmd + '.');
+ }
+ return request.toJSONProtocol();
+};
+
+
// Create a JSON request for the threads command.
DebugRequest.prototype.threadsCommandToJSONRequest_ = function(args) {
// Build a threads request from the text command.
@@ -816,6 +1168,10 @@ DebugRequest.prototype.traceCommand_ = function(args) {
if (args == 'compile') {
trace_compile = !trace_compile;
print('Tracing of compiled scripts ' + (trace_compile ? 'on' : 'off'));
+ } else if (args === 'debug json' || args === 'json' || args === 'packets') {
+ trace_debug_json = !trace_debug_json;
+ print('Tracing of debug json packets ' +
+ (trace_debug_json ? 'on' : 'off'));
} else {
throw new Error('Invalid trace arguments.');
}
@@ -831,24 +1187,63 @@ DebugRequest.prototype.helpCommand_ = function(args) {
print('warning: arguments to \'help\' are ignored');
}
- print('break');
- print('break location [condition]');
- print(' break on named function: location is a function name');
- print(' break on function: location is #<id>#');
- print(' break on script position: location is name:line[:column]');
- print('clear <breakpoint #>');
- print('backtrace [n] | [-n] | [from to]');
- print('frame <frame #>');
+ print('Note: <> denotes symbollic values to be replaced with real values.');
+ print('Note: [] denotes optional parts of commands, or optional options / arguments.');
+ print(' e.g. d[elete] - you get the same command if you type d or delete.');
+ print('');
+ print('[break] - break as soon as possible');
+ print('b[reak] location [condition]');
+ print(' - break on named function: location is a function name');
+ print(' - break on function: location is #<id>#');
+ print(' - break on script position: location is name:line[:column]');
+ print('');
+ print('clear <breakpoint #> - deletes the specified user defined breakpoint');
+ print('d[elete] <breakpoint #> - deletes the specified user defined breakpoint');
+ print('dis[able] <breakpoint #> - disables the specified user defined breakpoint');
+ print('dis[able] exc[eptions] [[all] | unc[aught]]');
+ print(' - disables breaking on exceptions');
+ print('en[able] <breakpoint #> - enables the specified user defined breakpoint');
+ print('en[able] exc[eptions] [[all] | unc[aught]]');
+ print(' - enables breaking on exceptions');
+ print('');
+ print('b[ack]t[race] [n] | [-n] | [from to]');
+ print(' - prints the stack back trace');
+ print('f[rame] - prints info about the current frame context');
+ print('f[rame] <frame #> - set context to specified frame #');
print('scopes');
print('scope <scope #>');
+ print('');
+ print('up - set context to caller of current frame');
+ print('do[wn] - set context to callee of current frame');
+ print('inf[o] br[eak] - prints info about breakpoints in use');
+ print('inf[o] ar[gs] - prints info about arguments of the current function');
+ print('inf[o] lo[cals] - prints info about locals in the current function');
+ print('inf[o] liveobjectlist|lol - same as \'lol info\'');
+ print('');
print('step [in | next | out| min [step count]]');
- print('print <expression>');
- print('dir <expression>');
+ print('c[ontinue] - continue executing after a breakpoint');
+ print('s[tep] [<N>] - step into the next N callees (default N is 1)');
+ print('s[tep]i [<N>] - step into the next N callees (default N is 1)');
+ print('n[ext] [<N>] - step over the next N callees (default N is 1)');
+ print('fin[ish] [<N>] - step out of N frames (default N is 1)');
+ print('');
+ print('p[rint] <expression> - prints the result of the specified expression');
+ print('dir <expression> - prints the object structure of the result');
+ print('set <var> = <expression> - executes the specified statement');
+ print('');
+ print('l[ist] - list the source code around for the current pc');
+ print('l[ist] [- | <start>,<end>] - list the specified range of source code');
print('source [from line [num lines]]');
- print('scripts');
- print('continue');
+ print('scr[ipts] [native|extensions|all]');
+ print('scr[ipts] [<filter text>] - list scripts with the specified text in its description');
+ print('');
+ print('gc - runs the garbage collector');
+ print('');
print('trace compile');
- print('help');
+ // hidden command: trace debug json - toggles tracing of debug json packets
+ print('');
+ print('disconnect|exit|quit - disconnects and quits the debugger');
+ print('help - prints this help information');
}
@@ -930,6 +1325,27 @@ function formatScope_(scope) {
}
+function refObjectToString_(protocolPackage, handle) {
+ var value = protocolPackage.lookup(handle);
+ var result = '';
+ if (value.isString()) {
+ result = '"' + value.value() + '"';
+ } else if (value.isPrimitive()) {
+ result = value.valueString();
+ } else if (value.isObject()) {
+ result += formatObject_(value, true);
+ }
+ return result;
+}
+
+
+// Rounds number 'num' to 'length' decimal places.
+function roundNumber(num, length) {
+ var factor = Math.pow(10, length);
+ return Math.round(num * factor) / factor;
+}
+
+
// Convert a JSON response to text for display in a text based debugger.
function DebugResponseDetails(response) {
details = {text:'', running:false}
@@ -962,6 +1378,11 @@ function DebugResponseDetails(response) {
details.text = result;
break;
+ case 'changebreakpoint':
+ result = 'successfully changed breakpoint';
+ details.text = result;
+ break;
+
case 'listbreakpoints':
result = 'breakpoints: (' + body.breakpoints.length + ')';
for (var i = 0; i < body.breakpoints.length; i++) {
@@ -974,9 +1395,9 @@ function DebugResponseDetails(response) {
if (breakpoint.script_name) {
result += ' script_name=' + breakpoint.script_name;
}
- result += ' line=' + breakpoint.line;
+ result += ' line=' + (breakpoint.line + 1);
if (breakpoint.column != null) {
- result += ' column=' + breakpoint.column;
+ result += ' column=' + (breakpoint.column + 1);
}
if (breakpoint.groupId) {
result += ' groupId=' + breakpoint.groupId;
@@ -992,6 +1413,24 @@ function DebugResponseDetails(response) {
}
result += ' hit_count=' + breakpoint.hit_count;
}
+ if (body.breakpoints.length === 0) {
+ result = "No user defined breakpoints\n";
+ } else {
+ result += '\n';
+ }
+ if (body.breakOnExceptions) {
+ result += '* breaking on ALL exceptions is enabled\n';
+ } else if (body.breakOnUncaughtExceptions) {
+ result += '* breaking on UNCAUGHT exceptions is enabled\n';
+ } else {
+ result += '* all exception breakpoints are disabled\n';
+ }
+ details.text = result;
+ break;
+
+ case 'setexceptionbreak':
+ result = 'Break on ' + body.type + ' exceptions: ';
+ result += body.enabled ? 'enabled' : 'disabled';
details.text = result;
break;
@@ -1010,10 +1449,39 @@ function DebugResponseDetails(response) {
break;
case 'frame':
- details.text = SourceUnderline(body.sourceLineText,
- body.column);
- Debug.State.currentSourceLine = body.line;
- Debug.State.currentFrame = body.index;
+ if (last_cmd === 'info locals') {
+ var locals = body.locals;
+ if (locals.length === 0) {
+ result = 'No locals';
+ } else {
+ for (var i = 0; i < locals.length; i++) {
+ var local = locals[i];
+ result += local.name + ' = ';
+ result += refObjectToString_(response, local.value.ref);
+ result += '\n';
+ }
+ }
+ } else if (last_cmd === 'info args') {
+ var args = body.arguments;
+ if (args.length === 0) {
+ result = 'No arguments';
+ } else {
+ for (var i = 0; i < args.length; i++) {
+ var arg = args[i];
+ result += arg.name + ' = ';
+ result += refObjectToString_(response, arg.value.ref);
+ result += '\n';
+ }
+ }
+ } else {
+ result = SourceUnderline(body.sourceLineText,
+ body.column);
+ Debug.State.currentSourceLine = body.line;
+ Debug.State.currentFrame = body.index;
+ Debug.State.displaySourceStartLine = -1;
+ Debug.State.displaySourceEndLine = -1;
+ }
+ details.text = result;
break;
case 'scopes':
@@ -1132,7 +1600,9 @@ function DebugResponseDetails(response) {
if (body[i].name) {
result += body[i].name;
} else {
- if (body[i].compilationType == Debug.ScriptCompilationType.Eval) {
+ if (body[i].compilationType == Debug.ScriptCompilationType.Eval
+ && body[i].evalFromScript
+ ) {
result += 'eval from ';
var script_value = response.lookup(body[i].evalFromScript.ref);
result += ' ' + script_value.field('name');
@@ -1162,6 +1632,9 @@ function DebugResponseDetails(response) {
result += sourceStart;
result += ']';
}
+ if (body.length == 0) {
+ result = "no matching scripts found";
+ }
details.text = result;
break;
@@ -1181,6 +1654,23 @@ function DebugResponseDetails(response) {
details.text = "(running)";
break;
+ case 'v8flags':
+ details.text = "flags set";
+ break;
+
+ case 'gc':
+ details.text = "GC " + body.before + " => " + body.after;
+ if (body.after > (1024*1024)) {
+ details.text +=
+ " (" + roundNumber(body.before/(1024*1024), 1) + "M => " +
+ roundNumber(body.after/(1024*1024), 1) + "M)";
+ } else if (body.after > 1024) {
+ details.text +=
+ " (" + roundNumber(body.before/1024, 1) + "K => " +
+ roundNumber(body.after/1024, 1) + "K)";
+ }
+ break;
+
default:
details.text =
'Response for unknown command \'' + response.command() + '\'' +
@@ -1467,6 +1957,11 @@ ProtocolValue.prototype.value = function() {
}
+ProtocolValue.prototype.valueString = function() {
+ return this.value_.text;
+}
+
+
function ProtocolReference(handle) {
this.handle_ = handle;
}
@@ -1613,7 +2108,9 @@ function SimpleObjectToJSON_(object) {
var property_value_json;
switch (typeof property_value) {
case 'object':
- if (typeof property_value.toJSONProtocol == 'function') {
+ if (property_value === null) {
+ property_value_json = 'null';
+ } else if (typeof property_value.toJSONProtocol == 'function') {
property_value_json = property_value.toJSONProtocol(true)
} else if (property_value.constructor.name == 'Array'){
property_value_json = SimpleArrayToJSON_(property_value);
diff --git a/deps/v8/src/data-flow.h b/deps/v8/src/data-flow.h
index 6e2230c65e..79d760f5a4 100644
--- a/deps/v8/src/data-flow.h
+++ b/deps/v8/src/data-flow.h
@@ -112,10 +112,13 @@ class BitVector: public ZoneObject {
}
void CopyFrom(const BitVector& other) {
- ASSERT(other.length() == length());
- for (int i = 0; i < data_length_; i++) {
+ ASSERT(other.length() <= length());
+ for (int i = 0; i < other.data_length_; i++) {
data_[i] = other.data_[i];
}
+ for (int i = other.data_length_; i < data_length_; i++) {
+ data_[i] = 0;
+ }
}
bool Contains(int i) const {
diff --git a/deps/v8/src/date.js b/deps/v8/src/date.js
index bc70327c41..9eb607c7e5 100644
--- a/deps/v8/src/date.js
+++ b/deps/v8/src/date.js
@@ -1000,7 +1000,7 @@ function DateToISOString() {
function DateToJSON(key) {
var o = ToObject(this);
var tv = DefaultNumber(o);
- if (IS_NUMBER(tv) && !$isFinite(tv)) {
+ if (IS_NUMBER(tv) && !NUMBER_IS_FINITE(tv)) {
return null;
}
return o.toISOString();
diff --git a/deps/v8/src/debug-agent.cc b/deps/v8/src/debug-agent.cc
index e2d9304338..6901079b9b 100644
--- a/deps/v8/src/debug-agent.cc
+++ b/deps/v8/src/debug-agent.cc
@@ -27,9 +27,11 @@
#include "v8.h"
+#include "debug.h"
#include "debug-agent.h"
#ifdef ENABLE_DEBUGGER_SUPPORT
+
namespace v8 {
namespace internal {
@@ -167,22 +169,33 @@ void DebuggerAgentSession::Run() {
while (true) {
// Read data from the debugger front end.
SmartPointer<char> message = DebuggerAgentUtil::ReceiveMessage(client_);
- if (*message == NULL) {
- // Session is closed.
- agent_->OnSessionClosed(this);
- return;
+
+ const char* msg = *message;
+ bool is_closing_session = (msg == NULL);
+
+ if (msg == NULL) {
+ // If we lost the connection, then simulate a disconnect msg:
+ msg = "{\"seq\":1,\"type\":\"request\",\"command\":\"disconnect\"}";
+
+ } else {
+ // Check if we're getting a disconnect request:
+ const char* disconnectRequestStr =
+ "\"type\":\"request\",\"command\":\"disconnect\"}";
+ const char* result = strstr(msg, disconnectRequestStr);
+ if (result != NULL) {
+ is_closing_session = true;
+ }
}
// Convert UTF-8 to UTF-16.
- unibrow::Utf8InputBuffer<> buf(*message,
- StrLength(*message));
+ unibrow::Utf8InputBuffer<> buf(msg, StrLength(msg));
int len = 0;
while (buf.has_more()) {
buf.GetNext();
len++;
}
ScopedVector<int16_t> temp(len + 1);
- buf.Reset(*message, StrLength(*message));
+ buf.Reset(msg, StrLength(msg));
for (int i = 0; i < len; i++) {
temp[i] = buf.GetNext();
}
@@ -190,6 +203,12 @@ void DebuggerAgentSession::Run() {
// Send the request received to the debugger.
v8::Debug::SendCommand(reinterpret_cast<const uint16_t *>(temp.start()),
len);
+
+ if (is_closing_session) {
+ // Session is closed.
+ agent_->OnSessionClosed(this);
+ return;
+ }
}
}
diff --git a/deps/v8/src/debug-agent.h b/deps/v8/src/debug-agent.h
index 3647994364..4cedb83187 100644
--- a/deps/v8/src/debug-agent.h
+++ b/deps/v8/src/debug-agent.h
@@ -44,7 +44,8 @@ class DebuggerAgentSession;
class DebuggerAgent: public Thread {
public:
explicit DebuggerAgent(const char* name, int port)
- : name_(StrDup(name)), port_(port),
+ : Thread(name),
+ name_(StrDup(name)), port_(port),
server_(OS::CreateSocket()), terminate_(false),
session_access_(OS::CreateMutex()), session_(NULL),
terminate_now_(OS::CreateSemaphore(0)),
@@ -90,7 +91,8 @@ class DebuggerAgent: public Thread {
class DebuggerAgentSession: public Thread {
public:
DebuggerAgentSession(DebuggerAgent* agent, Socket* client)
- : agent_(agent), client_(client) {}
+ : Thread("v8:DbgAgntSessn"),
+ agent_(agent), client_(client) {}
void DebuggerMessage(Vector<uint16_t> message);
void Shutdown();
diff --git a/deps/v8/src/debug-debugger.js b/deps/v8/src/debug-debugger.js
index 090c661dd3..1adf73ac71 100644
--- a/deps/v8/src/debug-debugger.js
+++ b/deps/v8/src/debug-debugger.js
@@ -112,8 +112,8 @@ var debugger_flags = {
// Create a new break point object and add it to the list of break points.
-function MakeBreakPoint(source_position, opt_line, opt_column, opt_script_break_point) {
- var break_point = new BreakPoint(source_position, opt_line, opt_column, opt_script_break_point);
+function MakeBreakPoint(source_position, opt_script_break_point) {
+ var break_point = new BreakPoint(source_position, opt_script_break_point);
break_points.push(break_point);
return break_point;
}
@@ -123,10 +123,8 @@ function MakeBreakPoint(source_position, opt_line, opt_column, opt_script_break_
// NOTE: This object does not have a reference to the function having break
// point as this would cause function not to be garbage collected when it is
// not used any more. We do not want break points to keep functions alive.
-function BreakPoint(source_position, opt_line, opt_column, opt_script_break_point) {
+function BreakPoint(source_position, opt_script_break_point) {
this.source_position_ = source_position;
- this.source_line_ = opt_line;
- this.source_column_ = opt_column;
if (opt_script_break_point) {
this.script_break_point_ = opt_script_break_point;
} else {
@@ -424,7 +422,7 @@ ScriptBreakPoint.prototype.set = function (script) {
if (position === null) return;
// Create a break point object and set the break point.
- break_point = MakeBreakPoint(position, this.line(), this.column(), this);
+ break_point = MakeBreakPoint(position, this);
break_point.setIgnoreCount(this.ignoreCount());
var actual_position = %SetScriptBreakPoint(script, position, break_point);
if (IS_UNDEFINED(actual_position)) {
@@ -639,7 +637,7 @@ Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) {
opt_condition);
} else {
// Set a break point directly on the function.
- var break_point = MakeBreakPoint(source_position, opt_line, opt_column);
+ var break_point = MakeBreakPoint(source_position);
var actual_position =
%SetFunctionBreakPoint(func, source_position, break_point);
actual_position += this.sourcePosition(func);
@@ -652,15 +650,40 @@ Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) {
};
+Debug.setBreakPointByScriptIdAndPosition = function(script_id, position,
+ condition, enabled)
+{
+ break_point = MakeBreakPoint(position);
+ break_point.setCondition(condition);
+ if (!enabled)
+ break_point.disable();
+ var scripts = this.scripts();
+ for (var i = 0; i < scripts.length; i++) {
+ if (script_id == scripts[i].id) {
+ break_point.actual_position = %SetScriptBreakPoint(scripts[i], position,
+ break_point);
+ break;
+ }
+ }
+ return break_point;
+};
+
+
Debug.enableBreakPoint = function(break_point_number) {
var break_point = this.findBreakPoint(break_point_number, false);
- break_point.enable();
+ // Only enable if the breakpoint hasn't been deleted:
+ if (break_point) {
+ break_point.enable();
+ }
};
Debug.disableBreakPoint = function(break_point_number) {
var break_point = this.findBreakPoint(break_point_number, false);
- break_point.disable();
+ // Only enable if the breakpoint hasn't been deleted:
+ if (break_point) {
+ break_point.disable();
+ }
};
@@ -701,6 +724,17 @@ Debug.clearAllBreakPoints = function() {
};
+Debug.disableAllBreakPoints = function() {
+ // Disable all user defined breakpoints:
+ for (var i = 1; i < next_break_point_number; i++) {
+ Debug.disableBreakPoint(i);
+ }
+ // Disable all exception breakpoints:
+ %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false);
+ %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
+};
+
+
Debug.findScriptBreakPoint = function(break_point_number, remove) {
var script_break_point;
for (var i = 0; i < script_break_points.length; i++) {
@@ -1341,6 +1375,10 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request)
this.clearBreakPointRequest_(request, response);
} else if (request.command == 'clearbreakpointgroup') {
this.clearBreakPointGroupRequest_(request, response);
+ } else if (request.command == 'disconnect') {
+ this.disconnectRequest_(request, response);
+ } else if (request.command == 'setexceptionbreak') {
+ this.setExceptionBreakRequest_(request, response);
} else if (request.command == 'listbreakpoints') {
this.listBreakpointsRequest_(request, response);
} else if (request.command == 'backtrace') {
@@ -1373,6 +1411,13 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request)
this.changeLiveRequest_(request, response);
} else if (request.command == 'flags') {
this.debuggerFlagsRequest_(request, response);
+ } else if (request.command == 'v8flags') {
+ this.v8FlagsRequest_(request, response);
+
+ // GC tools:
+ } else if (request.command == 'gc') {
+ this.gcRequest_(request, response);
+
} else {
throw new Error('Unknown command "' + request.command + '" in request');
}
@@ -1690,7 +1735,63 @@ DebugCommandProcessor.prototype.listBreakpointsRequest_ = function(request, resp
array.push(description);
}
- response.body = { breakpoints: array }
+ response.body = {
+ breakpoints: array,
+ breakOnExceptions: Debug.isBreakOnException(),
+ breakOnUncaughtExceptions: Debug.isBreakOnUncaughtException()
+ }
+}
+
+
+DebugCommandProcessor.prototype.disconnectRequest_ =
+ function(request, response) {
+ Debug.disableAllBreakPoints();
+ this.continueRequest_(request, response);
+}
+
+
+DebugCommandProcessor.prototype.setExceptionBreakRequest_ =
+ function(request, response) {
+ // Check for legal request.
+ if (!request.arguments) {
+ response.failed('Missing arguments');
+ return;
+ }
+
+ // Pull out and check the 'type' argument:
+ var type = request.arguments.type;
+ if (!type) {
+ response.failed('Missing argument "type"');
+ return;
+ }
+
+ // Initialize the default value of enable:
+ var enabled;
+ if (type == 'all') {
+ enabled = !Debug.isBreakOnException();
+ } else if (type == 'uncaught') {
+ enabled = !Debug.isBreakOnUncaughtException();
+ }
+
+ // Pull out and check the 'enabled' argument if present:
+ if (!IS_UNDEFINED(request.arguments.enabled)) {
+ enabled = request.arguments.enabled;
+ if ((enabled != true) && (enabled != false)) {
+ response.failed('Illegal value for "enabled":"' + enabled + '"');
+ }
+ }
+
+ // Now set the exception break state:
+ if (type == 'all') {
+ %ChangeBreakOnException(Debug.ExceptionBreak.Caught, enabled);
+ } else if (type == 'uncaught') {
+ %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, enabled);
+ } else {
+ response.failed('Unknown "type":"' + type + '"');
+ }
+
+ // Add the cleared break point number to the response.
+ response.body = { 'type': type, 'enabled': enabled };
}
@@ -2047,6 +2148,16 @@ DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) {
idsToInclude[ids[i]] = true;
}
}
+
+ var filterStr = null;
+ var filterNum = null;
+ if (!IS_UNDEFINED(request.arguments.filter)) {
+ var num = %ToNumber(request.arguments.filter);
+ if (!isNaN(num)) {
+ filterNum = num;
+ }
+ filterStr = request.arguments.filter;
+ }
}
// Collect all scripts in the heap.
@@ -2058,6 +2169,21 @@ DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) {
if (idsToInclude && !idsToInclude[scripts[i].id]) {
continue;
}
+ if (filterStr || filterNum) {
+ var script = scripts[i];
+ var found = false;
+ if (filterNum && !found) {
+ if (script.id && script.id === filterNum) {
+ found = true;
+ }
+ }
+ if (filterStr && !found) {
+ if (script.name && script.name.indexOf(filterStr) >= 0) {
+ found = true;
+ }
+ }
+ if (!found) continue;
+ }
if (types & ScriptTypeFlag(scripts[i].type)) {
response.body.push(MakeMirror(scripts[i]));
}
@@ -2196,6 +2322,27 @@ DebugCommandProcessor.prototype.debuggerFlagsRequest_ = function(request,
}
+DebugCommandProcessor.prototype.v8FlagsRequest_ = function(request, response) {
+ var flags = request.arguments.flags;
+ if (!flags) flags = '';
+ %SetFlags(flags);
+};
+
+
+DebugCommandProcessor.prototype.gcRequest_ = function(request, response) {
+ var type = request.arguments.type;
+ if (!type) type = 'all';
+
+ var before = %GetHeapUsage();
+ %CollectGarbage(type);
+ var after = %GetHeapUsage();
+
+ response.body = { "before": before, "after": after };
+};
+
+
+
+
// 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 ca3c1db790..8ec77e77e5 100644
--- a/deps/v8/src/debug.cc
+++ b/deps/v8/src/debug.cc
@@ -622,7 +622,7 @@ bool Debug::disable_break_ = false;
// Default call debugger on uncaught exception.
bool Debug::break_on_exception_ = false;
-bool Debug::break_on_uncaught_exception_ = true;
+bool Debug::break_on_uncaught_exception_ = false;
Handle<Context> Debug::debug_context_ = Handle<Context>();
Code* Debug::debug_break_return_ = NULL;
@@ -2740,8 +2740,10 @@ bool Debugger::StartAgent(const char* name, int port,
}
if (Socket::Setup()) {
- agent_ = new DebuggerAgent(name, port);
- agent_->Start();
+ if (agent_ == NULL) {
+ agent_ = new DebuggerAgent(name, port);
+ agent_->Start();
+ }
return true;
}
@@ -3037,7 +3039,8 @@ void LockingCommandMessageQueue::Clear() {
MessageDispatchHelperThread::MessageDispatchHelperThread()
- : sem_(OS::CreateSemaphore(0)), mutex_(OS::CreateMutex()),
+ : Thread("v8:MsgDispHelpr"),
+ sem_(OS::CreateSemaphore(0)), mutex_(OS::CreateMutex()),
already_signalled_(false) {
}
diff --git a/deps/v8/src/debug.h b/deps/v8/src/debug.h
index 0d63085f15..85c4d534ff 100644
--- a/deps/v8/src/debug.h
+++ b/deps/v8/src/debug.h
@@ -32,6 +32,7 @@
#include "debug-agent.h"
#include "execution.h"
#include "factory.h"
+#include "flags.h"
#include "hashmap.h"
#include "platform.h"
#include "string-stream.h"
@@ -772,6 +773,15 @@ class Debugger {
}
}
+ if (((event == v8::BeforeCompile) || (event == v8::AfterCompile)) &&
+ !FLAG_debug_compile_events) {
+ return false;
+
+ } else if ((event == v8::ScriptCollected) &&
+ !FLAG_debug_script_collected_events) {
+ return false;
+ }
+
// Currently argument event is not used.
return !compiling_natives_ && Debugger::IsDebuggerActive();
}
diff --git a/deps/v8/src/deoptimizer.cc b/deps/v8/src/deoptimizer.cc
index dd70baaa16..a3d2002178 100644
--- a/deps/v8/src/deoptimizer.cc
+++ b/deps/v8/src/deoptimizer.cc
@@ -309,9 +309,9 @@ void Deoptimizer::TearDown() {
}
-unsigned Deoptimizer::GetOutputInfo(DeoptimizationOutputData* data,
- unsigned id,
- SharedFunctionInfo* shared) {
+int Deoptimizer::GetOutputInfo(DeoptimizationOutputData* data,
+ unsigned id,
+ SharedFunctionInfo* shared) {
// TODO(kasperl): For now, we do a simple linear search for the PC
// offset associated with the given node id. This should probably be
// changed to a binary search.
@@ -618,17 +618,17 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator,
}
case Translation::ARGUMENTS_OBJECT: {
- // Use the hole value as a sentinel and fill in the arguments object
- // after the deoptimized frame is built.
+ // Use the arguments marker value as a sentinel and fill in the arguments
+ // object after the deoptimized frame is built.
ASSERT(frame_index == 0); // Only supported for first frame.
if (FLAG_trace_deopt) {
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- ",
output_[frame_index]->GetTop() + output_offset,
output_offset);
- Heap::the_hole_value()->ShortPrint();
+ Heap::arguments_marker()->ShortPrint();
PrintF(" ; arguments object\n");
}
- intptr_t value = reinterpret_cast<intptr_t>(Heap::the_hole_value());
+ intptr_t value = reinterpret_cast<intptr_t>(Heap::arguments_marker());
output_[frame_index]->SetFrameSlot(output_offset, value);
return;
}
diff --git a/deps/v8/src/deoptimizer.h b/deps/v8/src/deoptimizer.h
index 2d7dfc895c..f9bf280ea8 100644
--- a/deps/v8/src/deoptimizer.h
+++ b/deps/v8/src/deoptimizer.h
@@ -145,9 +145,9 @@ class Deoptimizer : public Malloced {
static Address GetDeoptimizationEntry(int id, BailoutType type);
static int GetDeoptimizationId(Address addr, BailoutType type);
- static unsigned GetOutputInfo(DeoptimizationOutputData* data,
- unsigned node_id,
- SharedFunctionInfo* shared);
+ static int GetOutputInfo(DeoptimizationOutputData* data,
+ unsigned node_id,
+ SharedFunctionInfo* shared);
static void Setup();
static void TearDown();
diff --git a/deps/v8/src/disassembler.cc b/deps/v8/src/disassembler.cc
index bb0a07229a..194a299f02 100644
--- a/deps/v8/src/disassembler.cc
+++ b/deps/v8/src/disassembler.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -268,10 +268,13 @@ static int DecodeIt(FILE* f,
Code::Kind2String(kind),
CodeStub::MajorName(major_key, false));
switch (major_key) {
- case CodeStub::CallFunction:
- out.AddFormatted("argc = %d", minor_key);
+ case CodeStub::CallFunction: {
+ int argc =
+ CallFunctionStub::ExtractArgcFromMinorKey(minor_key);
+ out.AddFormatted("argc = %d", argc);
break;
- default:
+ }
+ default:
out.AddFormatted("minor: %d", minor_key);
}
}
diff --git a/deps/v8/src/extensions/experimental/experimental.gyp b/deps/v8/src/extensions/experimental/experimental.gyp
new file mode 100644
index 0000000000..73888fc045
--- /dev/null
+++ b/deps/v8/src/extensions/experimental/experimental.gyp
@@ -0,0 +1,50 @@
+# Copyright 2011 the V8 project authors. All rights reserved.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+{
+ 'variables': {
+ 'icu_src_dir%': '',
+ },
+ 'targets': [
+ {
+ 'target_name': 'i18n_api',
+ 'type': 'static_library',
+ 'sources': [
+ 'i18n-extension.cc',
+ 'i18n-extension.h',
+ ],
+ 'include_dirs': [
+ '<(icu_src_dir)/public/common',
+ '../..',
+ ],
+ 'dependencies': [
+ '<(icu_src_dir)/icu.gyp:*',
+ '../../../tools/gyp/v8.gyp:v8',
+ ],
+ },
+ ], # targets
+}
diff --git a/deps/v8/src/factory.cc b/deps/v8/src/factory.cc
index 83af447d91..2bc878cc4d 100644
--- a/deps/v8/src/factory.cc
+++ b/deps/v8/src/factory.cc
@@ -99,6 +99,14 @@ Handle<String> Factory::LookupSymbol(Vector<const char> string) {
CALL_HEAP_FUNCTION(Heap::LookupSymbol(string), String);
}
+Handle<String> Factory::LookupAsciiSymbol(Vector<const char> string) {
+ CALL_HEAP_FUNCTION(Heap::LookupAsciiSymbol(string), String);
+}
+
+Handle<String> Factory::LookupTwoByteSymbol(Vector<const uc16> string) {
+ CALL_HEAP_FUNCTION(Heap::LookupTwoByteSymbol(string), String);
+}
+
Handle<String> Factory::NewStringFromAscii(Vector<const char> string,
PretenureFlag pretenure) {
diff --git a/deps/v8/src/factory.h b/deps/v8/src/factory.h
index b7a2882e83..a5e1591238 100644
--- a/deps/v8/src/factory.h
+++ b/deps/v8/src/factory.h
@@ -61,6 +61,8 @@ class Factory : public AllStatic {
PretenureFlag pretenure);
static Handle<String> LookupSymbol(Vector<const char> str);
+ static Handle<String> LookupAsciiSymbol(Vector<const char> str);
+ static Handle<String> LookupTwoByteSymbol(Vector<const uc16> str);
static Handle<String> LookupAsciiSymbol(const char* str) {
return LookupSymbol(CStrVector(str));
}
diff --git a/deps/v8/src/flag-definitions.h b/deps/v8/src/flag-definitions.h
index f160a85afc..daadef69fd 100644
--- a/deps/v8/src/flag-definitions.h
+++ b/deps/v8/src/flag-definitions.h
@@ -122,7 +122,6 @@ DEFINE_bool(trace_inlining, false, "trace inlining decisions")
DEFINE_bool(trace_alloc, false, "trace register allocator")
DEFINE_bool(trace_range, false, "trace range analysis")
DEFINE_bool(trace_gvn, false, "trace global value numbering")
-DEFINE_bool(trace_environment, false, "trace lithium environments")
DEFINE_bool(trace_representation, false, "trace representation types")
DEFINE_bool(stress_pointer_maps, false, "pointer map for every instruction")
DEFINE_bool(stress_environments, false, "environment for every instruction")
@@ -142,6 +141,7 @@ DEFINE_bool(use_osr, false, "use on-stack replacement")
#endif
DEFINE_bool(trace_osr, false, "trace on-stack replacement")
DEFINE_int(stress_runs, 0, "number of stress runs")
+DEFINE_bool(optimize_closures, true, "optimize closures")
// assembler-ia32.cc / assembler-arm.cc / assembler-x64.cc
DEFINE_bool(debug_code, false,
@@ -356,6 +356,16 @@ DEFINE_string(map_counters, NULL, "Map counters to a file")
DEFINE_args(js_arguments, JSArguments(),
"Pass all remaining arguments to the script. Alias for \"--\".")
+#if defined(WEBOS__)
+DEFINE_bool(debug_compile_events, false, "Enable debugger compile events")
+DEFINE_bool(debug_script_collected_events, false,
+ "Enable debugger script collected events")
+#else
+DEFINE_bool(debug_compile_events, true, "Enable debugger compile events")
+DEFINE_bool(debug_script_collected_events, true,
+ "Enable debugger script collected events")
+#endif
+
//
// Debug only flags
//
diff --git a/deps/v8/src/frames.cc b/deps/v8/src/frames.cc
index 3af72887e9..16ffbf5a94 100644
--- a/deps/v8/src/frames.cc
+++ b/deps/v8/src/frames.cc
@@ -329,21 +329,20 @@ void SafeStackTraceFrameIterator::Advance() {
Code* StackFrame::GetSafepointData(Address pc,
- uint8_t** safepoint_entry,
+ SafepointEntry* safepoint_entry,
unsigned* stack_slots) {
PcToCodeCache::PcToCodeCacheEntry* entry = PcToCodeCache::GetCacheEntry(pc);
- uint8_t* cached_safepoint_entry = entry->safepoint_entry;
- if (cached_safepoint_entry == NULL) {
- cached_safepoint_entry = entry->code->GetSafepointEntry(pc);
- ASSERT(cached_safepoint_entry != NULL); // No safepoint found.
- entry->safepoint_entry = cached_safepoint_entry;
+ SafepointEntry cached_safepoint_entry = entry->safepoint_entry;
+ if (!entry->safepoint_entry.is_valid()) {
+ entry->safepoint_entry = entry->code->GetSafepointEntry(pc);
+ ASSERT(entry->safepoint_entry.is_valid());
} else {
- ASSERT(cached_safepoint_entry == entry->code->GetSafepointEntry(pc));
+ ASSERT(entry->safepoint_entry.Equals(entry->code->GetSafepointEntry(pc)));
}
// Fill in the results and return the code.
Code* code = entry->code;
- *safepoint_entry = cached_safepoint_entry;
+ *safepoint_entry = entry->safepoint_entry;
*stack_slots = code->stack_slots();
return code;
}
@@ -536,7 +535,7 @@ void OptimizedFrame::Iterate(ObjectVisitor* v) const {
// Compute the safepoint information.
unsigned stack_slots = 0;
- uint8_t* safepoint_entry = NULL;
+ SafepointEntry safepoint_entry;
Code* code = StackFrame::GetSafepointData(
pc(), &safepoint_entry, &stack_slots);
unsigned slot_space = stack_slots * kPointerSize;
@@ -548,10 +547,22 @@ void OptimizedFrame::Iterate(ObjectVisitor* v) const {
Object** parameters_limit = &Memory::Object_at(
fp() + JavaScriptFrameConstants::kFunctionOffset - slot_space);
+ // Visit the parameters that may be on top of the saved registers.
+ if (safepoint_entry.argument_count() > 0) {
+ v->VisitPointers(parameters_base,
+ parameters_base + safepoint_entry.argument_count());
+ parameters_base += safepoint_entry.argument_count();
+ }
+
+ if (safepoint_entry.has_doubles()) {
+ parameters_base += DoubleRegister::kNumAllocatableRegisters *
+ kDoubleSize / kPointerSize;
+ }
+
// Visit the registers that contain pointers if any.
- if (SafepointTable::HasRegisters(safepoint_entry)) {
+ if (safepoint_entry.HasRegisters()) {
for (int i = kNumSafepointRegisters - 1; i >=0; i--) {
- if (SafepointTable::HasRegisterAt(safepoint_entry, i)) {
+ if (safepoint_entry.HasRegisterAt(i)) {
int reg_stack_index = MacroAssembler::SafepointRegisterStackIndex(i);
v->VisitPointer(parameters_base + reg_stack_index);
}
@@ -561,7 +572,8 @@ void OptimizedFrame::Iterate(ObjectVisitor* v) const {
}
// We're done dealing with the register bits.
- safepoint_entry += kNumSafepointRegisters >> kBitsPerByteLog2;
+ uint8_t* safepoint_bits = safepoint_entry.bits();
+ safepoint_bits += kNumSafepointRegisters >> kBitsPerByteLog2;
// Visit the rest of the parameters.
v->VisitPointers(parameters_base, parameters_limit);
@@ -570,7 +582,7 @@ void OptimizedFrame::Iterate(ObjectVisitor* v) const {
for (unsigned index = 0; index < stack_slots; index++) {
int byte_index = index >> kBitsPerByteLog2;
int bit_index = index & (kBitsPerByte - 1);
- if ((safepoint_entry[byte_index] & (1U << bit_index)) != 0) {
+ if ((safepoint_bits[byte_index] & (1U << bit_index)) != 0) {
v->VisitPointer(parameters_limit + index);
}
}
@@ -778,14 +790,8 @@ DeoptimizationInputData* OptimizedFrame::GetDeoptimizationData(
ASSERT(code != NULL);
ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION);
- SafepointTable table(code);
- unsigned pc_offset = static_cast<unsigned>(pc() - code->instruction_start());
- for (unsigned i = 0; i < table.length(); i++) {
- if (table.GetPcOffset(i) == pc_offset) {
- *deopt_index = table.GetDeoptimizationIndex(i);
- break;
- }
- }
+ SafepointEntry safepoint_entry = code->GetSafepointEntry(pc());
+ *deopt_index = safepoint_entry.deoptimization_index();
ASSERT(*deopt_index != AstNode::kNoNumber);
return DeoptimizationInputData::cast(code->deoptimization_data());
@@ -1150,7 +1156,7 @@ PcToCodeCache::PcToCodeCacheEntry* PcToCodeCache::GetCacheEntry(Address pc) {
// been set. Otherwise, we risk trying to use a cache entry before
// the code has been computed.
entry->code = GcSafeFindCodeForPc(pc);
- entry->safepoint_entry = NULL;
+ entry->safepoint_entry.Reset();
entry->pc = pc;
}
return entry;
diff --git a/deps/v8/src/frames.h b/deps/v8/src/frames.h
index 778f9d2435..5378709062 100644
--- a/deps/v8/src/frames.h
+++ b/deps/v8/src/frames.h
@@ -28,6 +28,8 @@
#ifndef V8_FRAMES_H_
#define V8_FRAMES_H_
+#include "safepoint-table.h"
+
namespace v8 {
namespace internal {
@@ -51,7 +53,7 @@ class PcToCodeCache : AllStatic {
struct PcToCodeCacheEntry {
Address pc;
Code* code;
- uint8_t* safepoint_entry;
+ SafepointEntry safepoint_entry;
};
static PcToCodeCacheEntry* cache(int index) {
@@ -208,7 +210,7 @@ class StackFrame BASE_EMBEDDED {
// safepoint entry and the number of stack slots. The pc must be at
// a safepoint.
static Code* GetSafepointData(Address pc,
- uint8_t** safepoint_entry,
+ SafepointEntry* safepoint_entry,
unsigned* stack_slots);
virtual void Iterate(ObjectVisitor* v) const = 0;
diff --git a/deps/v8/src/globals.h b/deps/v8/src/globals.h
index 35156ae606..9b24bf6350 100644
--- a/deps/v8/src/globals.h
+++ b/deps/v8/src/globals.h
@@ -181,10 +181,6 @@ typedef byte* Address;
#define USING_BSD_ABI
#endif
-// Code-point values in Unicode 4.0 are 21 bits wide.
-typedef uint16_t uc16;
-typedef int32_t uc32;
-
// -----------------------------------------------------------------------------
// Constants
@@ -228,6 +224,15 @@ const int kBinary32MinExponent = 0x01;
const int kBinary32MantissaBits = 23;
const int kBinary32ExponentShift = 23;
+// ASCII/UC16 constants
+// Code-point values in Unicode 4.0 are 21 bits wide.
+typedef uint16_t uc16;
+typedef int32_t uc32;
+const int kASCIISize = kCharSize;
+const int kUC16Size = sizeof(uc16); // NOLINT
+const uc32 kMaxAsciiCharCode = 0x7f;
+const uint32_t kMaxAsciiCharCodeU = 0x7fu;
+
// The expression OFFSET_OF(type, field) computes the byte-offset
// of the specified field relative to the containing type. This
diff --git a/deps/v8/src/handles.cc b/deps/v8/src/handles.cc
index 68c61b5cdb..461c3f5fab 100644
--- a/deps/v8/src/handles.cc
+++ b/deps/v8/src/handles.cc
@@ -280,13 +280,13 @@ Handle<Object> ForceDeleteProperty(Handle<JSObject> object,
}
-Handle<Object> IgnoreAttributesAndSetLocalProperty(
+Handle<Object> SetLocalPropertyIgnoreAttributes(
Handle<JSObject> object,
Handle<String> key,
Handle<Object> value,
PropertyAttributes attributes) {
CALL_HEAP_FUNCTION(object->
- IgnoreAttributesAndSetLocalProperty(*key, *value, attributes), Object);
+ SetLocalPropertyIgnoreAttributes(*key, *value, attributes), Object);
}
@@ -422,6 +422,15 @@ Handle<Object> SetElement(Handle<JSObject> object,
}
+Handle<Object> SetOwnElement(Handle<JSObject> object,
+ uint32_t index,
+ Handle<Object> value) {
+ ASSERT(!object->HasPixelElements());
+ ASSERT(!object->HasExternalArrayElements());
+ CALL_HEAP_FUNCTION(object->SetElement(index, *value, false), Object);
+}
+
+
Handle<JSObject> Copy(Handle<JSObject> obj) {
CALL_HEAP_FUNCTION(Heap::CopyJSObject(*obj), JSObject);
}
diff --git a/deps/v8/src/handles.h b/deps/v8/src/handles.h
index 8fd25dc9ef..aa9d8b9999 100644
--- a/deps/v8/src/handles.h
+++ b/deps/v8/src/handles.h
@@ -217,9 +217,10 @@ Handle<Object> SetNormalizedProperty(Handle<JSObject> object,
Handle<Object> ForceDeleteProperty(Handle<JSObject> object,
Handle<Object> key);
-Handle<Object> IgnoreAttributesAndSetLocalProperty(Handle<JSObject> object,
- Handle<String> key,
- Handle<Object> value,
+Handle<Object> SetLocalPropertyIgnoreAttributes(
+ Handle<JSObject> object,
+ Handle<String> key,
+ Handle<Object> value,
PropertyAttributes attributes);
Handle<Object> SetPropertyWithInterceptor(Handle<JSObject> object,
@@ -231,6 +232,10 @@ Handle<Object> SetElement(Handle<JSObject> object,
uint32_t index,
Handle<Object> value);
+Handle<Object> SetOwnElement(Handle<JSObject> object,
+ uint32_t index,
+ Handle<Object> value);
+
Handle<Object> GetProperty(Handle<JSObject> obj,
const char* name);
diff --git a/deps/v8/src/heap-inl.h b/deps/v8/src/heap-inl.h
index ef839988da..7b91e8715a 100644
--- a/deps/v8/src/heap-inl.h
+++ b/deps/v8/src/heap-inl.h
@@ -40,6 +40,19 @@ int Heap::MaxObjectSizeInPagedSpace() {
}
+MaybeObject* Heap::AllocateStringFromUtf8(Vector<const char> str,
+ PretenureFlag pretenure) {
+ // Check for ASCII first since this is the common case.
+ if (String::IsAscii(str.start(), str.length())) {
+ // If the string is ASCII, we do not need to convert the characters
+ // since UTF8 is backwards compatible with ASCII.
+ return AllocateStringFromAscii(str, pretenure);
+ }
+ // Non-ASCII and we need to decode.
+ return AllocateStringFromUtf8Slow(str, pretenure);
+}
+
+
MaybeObject* Heap::AllocateSymbol(Vector<const char> str,
int chars,
uint32_t hash_field) {
@@ -49,6 +62,71 @@ MaybeObject* Heap::AllocateSymbol(Vector<const char> str,
}
+MaybeObject* Heap::AllocateAsciiSymbol(Vector<const char> str,
+ uint32_t hash_field) {
+ if (str.length() > SeqAsciiString::kMaxLength) {
+ return Failure::OutOfMemoryException();
+ }
+ // Compute map and object size.
+ Map* map = ascii_symbol_map();
+ int size = SeqAsciiString::SizeFor(str.length());
+
+ // Allocate string.
+ Object* result;
+ { MaybeObject* maybe_result = (size > MaxObjectSizeInPagedSpace())
+ ? lo_space_->AllocateRaw(size)
+ : old_data_space_->AllocateRaw(size);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ reinterpret_cast<HeapObject*>(result)->set_map(map);
+ // Set length and hash fields of the allocated string.
+ String* answer = String::cast(result);
+ answer->set_length(str.length());
+ answer->set_hash_field(hash_field);
+
+ ASSERT_EQ(size, answer->Size());
+
+ // Fill in the characters.
+ memcpy(answer->address() + SeqAsciiString::kHeaderSize,
+ str.start(), str.length());
+
+ return answer;
+}
+
+
+MaybeObject* Heap::AllocateTwoByteSymbol(Vector<const uc16> str,
+ uint32_t hash_field) {
+ if (str.length() > SeqTwoByteString::kMaxLength) {
+ return Failure::OutOfMemoryException();
+ }
+ // Compute map and object size.
+ Map* map = symbol_map();
+ int size = SeqTwoByteString::SizeFor(str.length());
+
+ // Allocate string.
+ Object* result;
+ { MaybeObject* maybe_result = (size > MaxObjectSizeInPagedSpace())
+ ? lo_space_->AllocateRaw(size)
+ : old_data_space_->AllocateRaw(size);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ reinterpret_cast<HeapObject*>(result)->set_map(map);
+ // Set length and hash fields of the allocated string.
+ String* answer = String::cast(result);
+ answer->set_length(str.length());
+ answer->set_hash_field(hash_field);
+
+ ASSERT_EQ(size, answer->Size());
+
+ // Fill in the characters.
+ memcpy(answer->address() + SeqTwoByteString::kHeaderSize,
+ str.start(), str.length() * kUC16Size);
+
+ return answer;
+}
+
MaybeObject* Heap::CopyFixedArray(FixedArray* src) {
return CopyFixedArrayWithMap(src, src->map());
}
diff --git a/deps/v8/src/heap.cc b/deps/v8/src/heap.cc
index 1e9999164c..32d751a392 100644
--- a/deps/v8/src/heap.cc
+++ b/deps/v8/src/heap.cc
@@ -2011,6 +2011,12 @@ bool Heap::CreateInitialObjects() {
}
set_the_hole_value(obj);
+ { MaybeObject* maybe_obj = CreateOddball("arguments_marker",
+ Smi::FromInt(-4));
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_arguments_marker(obj);
+
{ MaybeObject* maybe_obj =
CreateOddball("no_interceptor_result_sentinel", Smi::FromInt(-2));
if (!maybe_obj->ToObject(&obj)) return false;
@@ -2549,20 +2555,10 @@ MaybeObject* Heap::AllocateExternalStringFromTwoByte(
}
// For small strings we check whether the resource contains only
- // ascii characters. If yes, we use a different string map.
- bool is_ascii = true;
- if (length >= static_cast<size_t>(String::kMinNonFlatLength)) {
- is_ascii = false;
- } else {
- const uc16* data = resource->data();
- for (size_t i = 0; i < length; i++) {
- if (data[i] > String::kMaxAsciiCharCode) {
- is_ascii = false;
- break;
- }
- }
- }
-
+ // ASCII characters. If yes, we use a different string map.
+ static const size_t kAsciiCheckLengthLimit = 32;
+ bool is_ascii = length <= kAsciiCheckLengthLimit &&
+ String::IsAscii(resource->data(), static_cast<int>(length));
Map* map = is_ascii ?
Heap::external_string_with_ascii_data_map() : Heap::external_string_map();
Object* result;
@@ -2728,6 +2724,9 @@ MaybeObject* Heap::CreateCode(const CodeDesc& desc,
code->set_instruction_size(desc.instr_size);
code->set_relocation_info(ByteArray::cast(reloc_info));
code->set_flags(flags);
+ if (code->is_call_stub() || code->is_keyed_call_stub()) {
+ code->set_check_type(RECEIVER_MAP_CHECK);
+ }
code->set_deoptimization_data(empty_fixed_array());
// Allow self references to created code object by patching the handle to
// point to the newly allocated Code object.
@@ -3307,8 +3306,8 @@ MaybeObject* Heap::AllocateStringFromAscii(Vector<const char> string,
}
-MaybeObject* Heap::AllocateStringFromUtf8(Vector<const char> string,
- PretenureFlag pretenure) {
+MaybeObject* Heap::AllocateStringFromUtf8Slow(Vector<const char> string,
+ PretenureFlag pretenure) {
// V8 only supports characters in the Basic Multilingual Plane.
const uc32 kMaxSupportedChar = 0xFFFF;
// Count the number of characters in the UTF-8 string and check if
@@ -3317,17 +3316,11 @@ MaybeObject* Heap::AllocateStringFromUtf8(Vector<const char> string,
decoder(ScannerConstants::utf8_decoder());
decoder->Reset(string.start(), string.length());
int chars = 0;
- bool is_ascii = true;
while (decoder->has_more()) {
- uc32 r = decoder->GetNext();
- if (r > String::kMaxAsciiCharCode) is_ascii = false;
+ decoder->GetNext();
chars++;
}
- // If the string is ascii, we do not need to convert the characters
- // since UTF8 is backwards compatible with ascii.
- if (is_ascii) return AllocateStringFromAscii(string, pretenure);
-
Object* result;
{ MaybeObject* maybe_result = AllocateRawTwoByteString(chars, pretenure);
if (!maybe_result->ToObject(&result)) return maybe_result;
@@ -3348,11 +3341,8 @@ MaybeObject* Heap::AllocateStringFromUtf8(Vector<const char> string,
MaybeObject* Heap::AllocateStringFromTwoByte(Vector<const uc16> string,
PretenureFlag pretenure) {
// Check if the string is an ASCII string.
- int i = 0;
- while (i < string.length() && string[i] <= String::kMaxAsciiCharCode) i++;
-
MaybeObject* maybe_result;
- if (i == string.length()) { // It's an ASCII string.
+ if (String::IsAscii(string.start(), string.length())) {
maybe_result = AllocateRawAsciiString(string.length(), pretenure);
} else { // It's not an ASCII string.
maybe_result = AllocateRawTwoByteString(string.length(), pretenure);
@@ -4032,6 +4022,36 @@ MaybeObject* Heap::LookupSymbol(Vector<const char> string) {
}
+MaybeObject* Heap::LookupAsciiSymbol(Vector<const char> string) {
+ Object* symbol = NULL;
+ Object* new_table;
+ { MaybeObject* maybe_new_table =
+ symbol_table()->LookupAsciiSymbol(string, &symbol);
+ if (!maybe_new_table->ToObject(&new_table)) return maybe_new_table;
+ }
+ // Can't use set_symbol_table because SymbolTable::cast knows that
+ // SymbolTable is a singleton and checks for identity.
+ roots_[kSymbolTableRootIndex] = new_table;
+ ASSERT(symbol != NULL);
+ return symbol;
+}
+
+
+MaybeObject* Heap::LookupTwoByteSymbol(Vector<const uc16> string) {
+ Object* symbol = NULL;
+ Object* new_table;
+ { MaybeObject* maybe_new_table =
+ symbol_table()->LookupTwoByteSymbol(string, &symbol);
+ if (!maybe_new_table->ToObject(&new_table)) return maybe_new_table;
+ }
+ // Can't use set_symbol_table because SymbolTable::cast knows that
+ // SymbolTable is a singleton and checks for identity.
+ roots_[kSymbolTableRootIndex] = new_table;
+ ASSERT(symbol != NULL);
+ return symbol;
+}
+
+
MaybeObject* Heap::LookupSymbol(String* string) {
if (string->IsSymbol()) return string;
Object* symbol = NULL;
@@ -5012,7 +5032,7 @@ class UnreachableObjectsFilter : public HeapObjectsFilter {
obj->SetMark();
}
UnmarkingVisitor visitor;
- Heap::IterateRoots(&visitor, VISIT_ONLY_STRONG);
+ Heap::IterateRoots(&visitor, VISIT_ALL);
while (visitor.can_process())
visitor.ProcessNext();
}
diff --git a/deps/v8/src/heap.h b/deps/v8/src/heap.h
index 18a4afbe46..0d79081a23 100644
--- a/deps/v8/src/heap.h
+++ b/deps/v8/src/heap.h
@@ -53,6 +53,7 @@ namespace internal {
V(Object, null_value, NullValue) \
V(Object, true_value, TrueValue) \
V(Object, false_value, FalseValue) \
+ V(Object, arguments_marker, ArgumentsMarker) \
V(Map, heap_number_map, HeapNumberMap) \
V(Map, global_context_map, GlobalContextMap) \
V(Map, fixed_array_map, FixedArrayMap) \
@@ -412,7 +413,10 @@ class Heap : public AllStatic {
MUST_USE_RESULT static MaybeObject* AllocateStringFromAscii(
Vector<const char> str,
PretenureFlag pretenure = NOT_TENURED);
- MUST_USE_RESULT static MaybeObject* AllocateStringFromUtf8(
+ MUST_USE_RESULT static inline MaybeObject* AllocateStringFromUtf8(
+ Vector<const char> str,
+ PretenureFlag pretenure = NOT_TENURED);
+ MUST_USE_RESULT static MaybeObject* AllocateStringFromUtf8Slow(
Vector<const char> str,
PretenureFlag pretenure = NOT_TENURED);
MUST_USE_RESULT static MaybeObject* AllocateStringFromTwoByte(
@@ -428,6 +432,14 @@ class Heap : public AllStatic {
int chars,
uint32_t hash_field);
+ MUST_USE_RESULT static inline MaybeObject* AllocateAsciiSymbol(
+ Vector<const char> str,
+ uint32_t hash_field);
+
+ MUST_USE_RESULT static inline MaybeObject* AllocateTwoByteSymbol(
+ Vector<const uc16> str,
+ uint32_t hash_field);
+
MUST_USE_RESULT static MaybeObject* AllocateInternalSymbol(
unibrow::CharacterStream* buffer, int chars, uint32_t hash_field);
@@ -683,6 +695,9 @@ class Heap : public AllStatic {
// failed.
// Please note this function does not perform a garbage collection.
MUST_USE_RESULT static MaybeObject* LookupSymbol(Vector<const char> str);
+ MUST_USE_RESULT static MaybeObject* LookupAsciiSymbol(Vector<const char> str);
+ MUST_USE_RESULT static MaybeObject* LookupTwoByteSymbol(
+ Vector<const uc16> str);
MUST_USE_RESULT static MaybeObject* LookupAsciiSymbol(const char* str) {
return LookupSymbol(CStrVector(str));
}
@@ -1850,7 +1865,7 @@ class GCTracer BASE_EMBEDDED {
}
~Scope() {
- ASSERT((0 <= scope_) && (scope_ < kNumberOfScopes));
+ ASSERT(scope_ < kNumberOfScopes); // scope_ is unsigned.
tracer_->scopes_[scope_] += OS::TimeCurrentMillis() - start_time_;
}
diff --git a/deps/v8/src/hydrogen-instructions.cc b/deps/v8/src/hydrogen-instructions.cc
index 3f39888e9b..89478f5e33 100644
--- a/deps/v8/src/hydrogen-instructions.cc
+++ b/deps/v8/src/hydrogen-instructions.cc
@@ -1190,6 +1190,11 @@ void HStoreGlobal::PrintDataTo(StringStream* stream) const {
}
+void HLoadContextSlot::PrintDataTo(StringStream* stream) const {
+ stream->Add("(%d, %d)", context_chain_length(), slot_index());
+}
+
+
// Implementation of type inference and type conversions. Calculates
// the inferred type of this instruction based on the input operands.
diff --git a/deps/v8/src/hydrogen-instructions.h b/deps/v8/src/hydrogen-instructions.h
index cbbe8fcc88..4a23f2a327 100644
--- a/deps/v8/src/hydrogen-instructions.h
+++ b/deps/v8/src/hydrogen-instructions.h
@@ -73,10 +73,10 @@ class LChunkBuilder;
// HCompare
// HCompareJSObjectEq
// HInstanceOf
+// HInstanceOfKnownGlobal
// HLoadKeyed
// HLoadKeyedFastElement
// HLoadKeyedGeneric
-// HLoadNamedGeneric
// HPower
// HStoreNamed
// HStoreNamedField
@@ -92,6 +92,7 @@ class LChunkBuilder;
// HCallNew
// HCallRuntime
// HCallStub
+// HCheckPrototypeMaps
// HConstant
// HControlInstruction
// HDeoptimize
@@ -106,6 +107,7 @@ class LChunkBuilder;
// HGlobalObject
// HGlobalReceiver
// HLeaveInlined
+// HLoadContextSlot
// HLoadGlobal
// HMaterializedLiteral
// HArrayLiteral
@@ -119,19 +121,21 @@ class LChunkBuilder;
// HStoreKeyedFastElement
// HStoreKeyedGeneric
// HUnaryOperation
-// HArrayLength
// HBitNot
// HChange
// HCheckFunction
// HCheckInstanceType
// HCheckMap
// HCheckNonSmi
-// HCheckPrototypeMaps
// HCheckSmi
// HDeleteProperty
+// HFixedArrayLength
+// HJSArrayLength
// HLoadElements
// HTypeofIs
// HLoadNamedField
+// HLoadNamedGeneric
+// HLoadFunctionPrototype
// HPushArgument
// HTypeof
// HUnaryMathOperation
@@ -170,7 +174,6 @@ class LChunkBuilder;
V(ArgumentsElements) \
V(ArgumentsLength) \
V(ArgumentsObject) \
- V(ArrayLength) \
V(ArrayLiteral) \
V(BitAnd) \
V(BitNot) \
@@ -203,24 +206,29 @@ class LChunkBuilder;
V(Deoptimize) \
V(Div) \
V(EnterInlined) \
+ V(FixedArrayLength) \
V(FunctionLiteral) \
V(GlobalObject) \
V(GlobalReceiver) \
V(Goto) \
V(InstanceOf) \
+ V(InstanceOfKnownGlobal) \
V(IsNull) \
V(IsObject) \
V(IsSmi) \
V(HasInstanceType) \
V(HasCachedArrayIndex) \
+ V(JSArrayLength) \
V(ClassOfTest) \
V(LeaveInlined) \
+ V(LoadContextSlot) \
V(LoadElements) \
V(LoadGlobal) \
V(LoadKeyedFastElement) \
V(LoadKeyedGeneric) \
V(LoadNamedField) \
V(LoadNamedGeneric) \
+ V(LoadFunctionPrototype) \
V(Mod) \
V(Mul) \
V(ObjectLiteral) \
@@ -256,6 +264,7 @@ class LChunkBuilder;
V(GlobalVars) \
V(Maps) \
V(ArrayLengths) \
+ V(FunctionPrototypes) \
V(OsrEntries)
#define DECLARE_INSTRUCTION(type) \
@@ -905,6 +914,9 @@ class HCompareMapAndBranch: public HUnaryControlInstruction {
virtual HBasicBlock* FirstSuccessor() const { return true_destination_; }
virtual HBasicBlock* SecondSuccessor() const { return false_destination_; }
+ HBasicBlock* true_destination() const { return true_destination_; }
+ HBasicBlock* false_destination() const { return false_destination_; }
+
virtual void PrintDataTo(StringStream* stream) const;
Handle<Map> map() const { return map_; }
@@ -1015,10 +1027,10 @@ class HChange: public HUnaryOperation {
class HSimulate: public HInstruction {
public:
- HSimulate(int ast_id, int pop_count, int environment_height)
+ HSimulate(int ast_id, int pop_count, int environment_length)
: ast_id_(ast_id),
pop_count_(pop_count),
- environment_height_(environment_height),
+ environment_length_(environment_length),
values_(2),
assigned_indexes_(2) {}
virtual ~HSimulate() {}
@@ -1032,7 +1044,7 @@ class HSimulate: public HInstruction {
ast_id_ = id;
}
- int environment_height() const { return environment_height_; }
+ int environment_length() const { return environment_length_; }
int pop_count() const { return pop_count_; }
const ZoneList<HValue*>* values() const { return &values_; }
int GetAssignedIndexAt(int index) const {
@@ -1074,7 +1086,7 @@ class HSimulate: public HInstruction {
}
int ast_id_;
int pop_count_;
- int environment_height_;
+ int environment_length_;
ZoneList<HValue*> values_;
ZoneList<int> assigned_indexes_;
};
@@ -1336,9 +1348,9 @@ class HCallRuntime: public HCall {
};
-class HArrayLength: public HUnaryOperation {
+class HJSArrayLength: public HUnaryOperation {
public:
- explicit HArrayLength(HValue* value) : HUnaryOperation(value) {
+ explicit HJSArrayLength(HValue* value) : HUnaryOperation(value) {
// The length of an array is stored as a tagged value in the array
// object. It is guaranteed to be 32 bit integer, but it can be
// represented as either a smi or heap number.
@@ -1351,7 +1363,23 @@ class HArrayLength: public HUnaryOperation {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(ArrayLength, "array_length")
+ DECLARE_CONCRETE_INSTRUCTION(JSArrayLength, "js_array_length")
+};
+
+
+class HFixedArrayLength: public HUnaryOperation {
+ public:
+ explicit HFixedArrayLength(HValue* value) : HUnaryOperation(value) {
+ set_representation(Representation::Tagged());
+ SetFlag(kDependsOnArrayLengths);
+ SetFlag(kUseGVN);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) const {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(FixedArrayLength, "fixed_array_length")
};
@@ -1596,42 +1624,40 @@ class HCheckNonSmi: public HUnaryOperation {
};
-class HCheckPrototypeMaps: public HUnaryOperation {
+class HCheckPrototypeMaps: public HInstruction {
public:
- HCheckPrototypeMaps(HValue* value,
- Handle<JSObject> holder,
- Handle<Map> receiver_map)
- : HUnaryOperation(value),
- holder_(holder),
- receiver_map_(receiver_map) {
- set_representation(Representation::Tagged());
+ HCheckPrototypeMaps(Handle<JSObject> prototype, Handle<JSObject> holder)
+ : prototype_(prototype), holder_(holder) {
SetFlag(kUseGVN);
SetFlag(kDependsOnMaps);
}
- virtual Representation RequiredInputRepresentation(int index) const {
- return Representation::Tagged();
- }
-
#ifdef DEBUG
virtual void Verify() const;
#endif
+ Handle<JSObject> prototype() const { return prototype_; }
Handle<JSObject> holder() const { return holder_; }
- Handle<Map> receiver_map() const { return receiver_map_; }
DECLARE_CONCRETE_INSTRUCTION(CheckPrototypeMaps, "check_prototype_maps")
+ virtual intptr_t Hashcode() const {
+ ASSERT(!Heap::IsAllocationAllowed());
+ intptr_t hash = reinterpret_cast<intptr_t>(*prototype());
+ hash = 17 * hash + reinterpret_cast<intptr_t>(*holder());
+ return hash;
+ }
+
protected:
virtual bool DataEquals(HValue* other) const {
HCheckPrototypeMaps* b = HCheckPrototypeMaps::cast(other);
- return holder_.is_identical_to(b->holder()) &&
- receiver_map_.is_identical_to(b->receiver_map());
+ return prototype_.is_identical_to(b->prototype()) &&
+ holder_.is_identical_to(b->holder());
}
private:
+ Handle<JSObject> prototype_;
Handle<JSObject> holder_;
- Handle<Map> receiver_map_;
};
@@ -1766,6 +1792,8 @@ class HConstant: public HInstruction {
Handle<Object> handle() const { return handle_; }
+ bool InOldSpace() const { return !Heap::InNewSpace(*handle_); }
+
virtual bool EmitAtUses() const { return !representation().IsDouble(); }
virtual void PrintDataTo(StringStream* stream) const;
virtual HType CalculateInferredType() const;
@@ -2236,6 +2264,28 @@ class HInstanceOf: public HBinaryOperation {
};
+class HInstanceOfKnownGlobal: public HUnaryOperation {
+ public:
+ HInstanceOfKnownGlobal(HValue* left, Handle<JSFunction> right)
+ : HUnaryOperation(left), function_(right) {
+ set_representation(Representation::Tagged());
+ SetFlagMask(AllSideEffects());
+ }
+
+ Handle<JSFunction> function() { return function_; }
+
+ virtual Representation RequiredInputRepresentation(int index) const {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal,
+ "instance_of_known_global")
+
+ private:
+ Handle<JSFunction> function_;
+};
+
+
class HPower: public HBinaryOperation {
public:
HPower(HValue* left, HValue* right)
@@ -2551,6 +2601,39 @@ class HStoreGlobal: public HUnaryOperation {
};
+class HLoadContextSlot: public HInstruction {
+ public:
+ HLoadContextSlot(int context_chain_length , int slot_index)
+ : context_chain_length_(context_chain_length), slot_index_(slot_index) {
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ SetFlag(kDependsOnCalls);
+ }
+
+ int context_chain_length() const { return context_chain_length_; }
+ int slot_index() const { return slot_index_; }
+
+ virtual void PrintDataTo(StringStream* stream) const;
+
+ virtual intptr_t Hashcode() const {
+ return context_chain_length() * 29 + slot_index();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadContextSlot, "load_context_slot")
+
+ protected:
+ virtual bool DataEquals(HValue* other) const {
+ HLoadContextSlot* b = HLoadContextSlot::cast(other);
+ return (context_chain_length() == b->context_chain_length())
+ && (slot_index() == b->slot_index());
+ }
+
+ private:
+ int context_chain_length_;
+ int slot_index_;
+};
+
+
class HLoadNamedField: public HUnaryOperation {
public:
HLoadNamedField(HValue* object, bool is_in_object, int offset)
@@ -2617,6 +2700,27 @@ class HLoadNamedGeneric: public HUnaryOperation {
};
+class HLoadFunctionPrototype: public HUnaryOperation {
+ public:
+ explicit HLoadFunctionPrototype(HValue* function)
+ : HUnaryOperation(function) {
+ set_representation(Representation::Tagged());
+ SetFlagMask(kDependsOnFunctionPrototypes);
+ }
+
+ HValue* function() const { return OperandAt(0); }
+
+ virtual Representation RequiredInputRepresentation(int index) const {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadFunctionPrototype, "load_function_prototype")
+
+ protected:
+ virtual bool DataEquals(HValue* other) const { return true; }
+};
+
+
class HLoadKeyed: public HBinaryOperation {
public:
HLoadKeyed(HValue* obj, HValue* key) : HBinaryOperation(obj, key) {
@@ -2663,6 +2767,12 @@ class HLoadKeyedGeneric: public HLoadKeyed {
};
+static inline bool StoringValueNeedsWriteBarrier(HValue* value) {
+ return !value->type().IsSmi() &&
+ !(value->IsConstant() && HConstant::cast(value)->InOldSpace());
+}
+
+
class HStoreNamed: public HBinaryOperation {
public:
HStoreNamed(HValue* obj, Handle<Object> name, HValue* val)
@@ -2680,6 +2790,10 @@ class HStoreNamed: public HBinaryOperation {
HValue* value() const { return OperandAt(1); }
void set_value(HValue* value) { SetOperandAt(1, value); }
+ bool NeedsWriteBarrier() const {
+ return StoringValueNeedsWriteBarrier(value());
+ }
+
DECLARE_INSTRUCTION(StoreNamed)
protected:
@@ -2760,6 +2874,10 @@ class HStoreKeyed: public HInstruction {
HValue* key() const { return OperandAt(1); }
HValue* value() const { return OperandAt(2); }
+ bool NeedsWriteBarrier() const {
+ return StoringValueNeedsWriteBarrier(value());
+ }
+
DECLARE_INSTRUCTION(StoreKeyed)
protected:
@@ -2779,10 +2897,6 @@ class HStoreKeyedFastElement: public HStoreKeyed {
SetFlag(kChangesArrayElements);
}
- bool NeedsWriteBarrier() const {
- return !value()->type().IsSmi();
- }
-
virtual Representation RequiredInputRepresentation(int index) const {
// The key is supposed to be Integer32.
return (index == 1) ? Representation::Integer32()
diff --git a/deps/v8/src/hydrogen.cc b/deps/v8/src/hydrogen.cc
index e34acd67d4..7aa66fd619 100644
--- a/deps/v8/src/hydrogen.cc
+++ b/deps/v8/src/hydrogen.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -128,7 +128,7 @@ HSimulate* HBasicBlock::CreateSimulate(int id) {
int push_count = environment->push_count();
int pop_count = environment->pop_count();
- int length = environment->values()->length();
+ int length = environment->length();
HSimulate* instr = new HSimulate(id, pop_count, length);
for (int i = push_count - 1; i >= 0; --i) {
instr->AddPushedValue(environment->ExpressionStackAt(i));
@@ -222,7 +222,7 @@ void HBasicBlock::RegisterPredecessor(HBasicBlock* pred) {
ASSERT(IsLoopHeader() || first_ == NULL);
HEnvironment* incoming_env = pred->last_environment();
if (IsLoopHeader()) {
- ASSERT(phis()->length() == incoming_env->values()->length());
+ ASSERT(phis()->length() == incoming_env->length());
for (int i = 0; i < phis_.length(); ++i) {
phis_[i]->AddInput(incoming_env->values()->at(i));
}
@@ -1982,7 +1982,7 @@ AstContext::AstContext(HGraphBuilder* owner, Expression::Context kind)
: owner_(owner), kind_(kind), outer_(owner->ast_context()) {
owner->set_ast_context(this); // Push.
#ifdef DEBUG
- original_count_ = owner->environment()->total_count();
+ original_length_ = owner->environment()->length();
#endif
}
@@ -1995,14 +1995,14 @@ AstContext::~AstContext() {
EffectContext::~EffectContext() {
ASSERT(owner()->HasStackOverflow() ||
!owner()->subgraph()->HasExit() ||
- owner()->environment()->total_count() == original_count_);
+ owner()->environment()->length() == original_length_);
}
ValueContext::~ValueContext() {
ASSERT(owner()->HasStackOverflow() ||
!owner()->subgraph()->HasExit() ||
- owner()->environment()->total_count() == original_count_ + 1);
+ owner()->environment()->length() == original_length_ + 1);
}
@@ -2343,7 +2343,7 @@ void HGraphBuilder::SetupScope(Scope* scope) {
}
// Set the initial values of stack-allocated locals.
- for (int i = count; i < environment()->values()->length(); ++i) {
+ for (int i = count; i < environment()->length(); ++i) {
environment()->Bind(i, undefined_constant);
}
@@ -2702,7 +2702,7 @@ void HSubgraph::PreProcessOsrEntry(IterationStatement* statement) {
int osr_entry_id = statement->OsrEntryId();
// We want the correct environment at the OsrEntry instruction. Build
// it explicitly. The expression stack should be empty.
- int count = osr_entry->last_environment()->total_count();
+ int count = osr_entry->last_environment()->length();
ASSERT(count == (osr_entry->last_environment()->parameter_count() +
osr_entry->last_environment()->local_count()));
for (int i = 0; i < count; ++i) {
@@ -2940,6 +2940,21 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
BAILOUT("unsupported context for arguments object");
}
ast_context()->ReturnValue(environment()->Lookup(variable));
+ } else if (variable->IsContextSlot()) {
+ if (variable->mode() == Variable::CONST) {
+ BAILOUT("reference to const context slot");
+ }
+ Slot* slot = variable->AsSlot();
+ CompilationInfo* info = graph()->info();
+ int context_chain_length = info->function()->scope()->
+ ContextChainLength(slot->var()->scope());
+ ASSERT(context_chain_length >= 0);
+ // TODO(antonm): if slot's value is not modified by closures, instead
+ // of reading it out of context, we could just embed the value as
+ // a constant.
+ HLoadContextSlot* instr =
+ new HLoadContextSlot(context_chain_length, slot->index());
+ ast_context()->ReturnInstruction(instr, expr->id());
} else if (variable->is_global()) {
LookupResult lookup;
LookupGlobalPropertyCell(variable, &lookup, false);
@@ -2956,7 +2971,7 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
HLoadGlobal* instr = new HLoadGlobal(cell, check_hole);
ast_context()->ReturnInstruction(instr, expr->id());
} else {
- BAILOUT("reference to non-stack-allocated/non-global variable");
+ BAILOUT("reference to a variable which requires dynamic lookup");
}
}
@@ -3103,8 +3118,15 @@ HBasicBlock* HGraphBuilder::BuildTypeSwitch(ZoneMapList* maps,
// this basic block the current basic block.
HBasicBlock* join_block = graph_->CreateBasicBlock();
for (int i = 0; i < subgraphs->length(); ++i) {
- if (subgraphs->at(i)->HasExit()) {
- subgraphs->at(i)->exit_block()->Goto(join_block);
+ HSubgraph* subgraph = subgraphs->at(i);
+ if (subgraph->HasExit()) {
+ // In an effect context the value of the type switch is not needed.
+ // There is no need to merge it at the join block only to discard it.
+ HBasicBlock* subgraph_exit = subgraph->exit_block();
+ if (ast_context()->IsEffect()) {
+ subgraph_exit->last_environment()->Drop(1);
+ }
+ subgraph_exit->Goto(join_block);
}
}
@@ -3242,7 +3264,8 @@ void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr,
Push(value);
instr->set_position(expr->position());
AddInstruction(instr);
- if (instr->HasSideEffects()) AddSimulate(expr->id());
+ if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId());
+ ast_context()->ReturnValue(Pop());
} else {
// Build subgraph for generic store through IC.
{
@@ -3260,11 +3283,14 @@ void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr,
}
HBasicBlock* new_exit_block =
- BuildTypeSwitch(&maps, &subgraphs, object, expr->AssignmentId());
+ BuildTypeSwitch(&maps, &subgraphs, object, expr->id());
subgraph()->set_exit_block(new_exit_block);
+ // In an effect context, we did not materialized the value in the
+ // predecessor environments so there's no need to handle it here.
+ if (subgraph()->HasExit() && !ast_context()->IsEffect()) {
+ ast_context()->ReturnValue(Pop());
+ }
}
-
- if (subgraph()->HasExit()) ast_context()->ReturnValue(Pop());
}
@@ -3471,7 +3497,7 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) {
Top(),
expr->position(),
expr->AssignmentId());
- } else {
+ } else if (var->IsStackAllocated()) {
// We allow reference to the arguments object only in assignemtns
// to local variables to make sure that the arguments object does
// not escape and is not modified.
@@ -3484,6 +3510,8 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) {
VISIT_FOR_VALUE(expr->value());
}
Bind(proxy->var(), Top());
+ } else {
+ BAILOUT("Assigning to no non-stack-allocated/non-global variable");
}
// Return the value.
ast_context()->ReturnValue(Pop());
@@ -3548,8 +3576,7 @@ void HGraphBuilder::HandlePolymorphicLoadNamedField(Property* expr,
if (maps.length() == 0) {
HInstruction* instr = BuildLoadNamedGeneric(object, expr);
instr->set_position(expr->position());
- PushAndAdd(instr);
- if (instr->HasSideEffects()) AddSimulate(expr->id());
+ ast_context()->ReturnInstruction(instr, expr->id());
} else {
// Build subgraph for generic load through IC.
{
@@ -3568,9 +3595,12 @@ void HGraphBuilder::HandlePolymorphicLoadNamedField(Property* expr,
HBasicBlock* new_exit_block =
BuildTypeSwitch(&maps, &subgraphs, object, expr->id());
subgraph()->set_exit_block(new_exit_block);
+ // In an effect context, we did not materialized the value in the
+ // predecessor environments so there's no need to handle it here.
+ if (subgraph()->HasExit() && !ast_context()->IsEffect()) {
+ ast_context()->ReturnValue(Pop());
+ }
}
-
- if (subgraph()->HasExit()) ast_context()->ReturnValue(Pop());
}
@@ -3643,9 +3673,18 @@ HInstruction* HGraphBuilder::BuildLoadKeyedFastElement(HValue* object,
Handle<Map> map = expr->GetMonomorphicReceiverType();
ASSERT(map->has_fast_elements());
AddInstruction(new HCheckMap(object, map));
- HInstruction* elements = AddInstruction(new HLoadElements(object));
- HInstruction* length = AddInstruction(new HArrayLength(elements));
- AddInstruction(new HBoundsCheck(key, length));
+ bool is_array = (map->instance_type() == JS_ARRAY_TYPE);
+ HLoadElements* elements = new HLoadElements(object);
+ HInstruction* length = NULL;
+ if (is_array) {
+ length = AddInstruction(new HJSArrayLength(object));
+ AddInstruction(new HBoundsCheck(key, length));
+ AddInstruction(elements);
+ } else {
+ AddInstruction(elements);
+ length = AddInstruction(new HFixedArrayLength(elements));
+ AddInstruction(new HBoundsCheck(key, length));
+ }
return new HLoadKeyedFastElement(elements, key);
}
@@ -3671,9 +3710,9 @@ HInstruction* HGraphBuilder::BuildStoreKeyedFastElement(HValue* object,
bool is_array = (map->instance_type() == JS_ARRAY_TYPE);
HInstruction* length = NULL;
if (is_array) {
- length = AddInstruction(new HArrayLength(object));
+ length = AddInstruction(new HJSArrayLength(object));
} else {
- length = AddInstruction(new HArrayLength(elements));
+ length = AddInstruction(new HFixedArrayLength(elements));
}
AddInstruction(new HBoundsCheck(key, length));
return new HStoreKeyedFastElement(elements, key, val);
@@ -3720,7 +3759,13 @@ void HGraphBuilder::VisitProperty(Property* expr) {
if (expr->IsArrayLength()) {
HValue* array = Pop();
AddInstruction(new HCheckNonSmi(array));
- instr = new HArrayLength(array);
+ AddInstruction(new HCheckInstanceType(array, JS_ARRAY_TYPE, JS_ARRAY_TYPE));
+ instr = new HJSArrayLength(array);
+
+ } else if (expr->IsFunctionPrototype()) {
+ HValue* function = Pop();
+ AddInstruction(new HCheckNonSmi(function));
+ instr = new HLoadFunctionPrototype(function);
} else if (expr->key()->IsPropertyName()) {
Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
@@ -3767,9 +3812,9 @@ void HGraphBuilder::AddCheckConstantFunction(Call* expr,
AddInstruction(new HCheckMap(receiver, receiver_map));
}
if (!expr->holder().is_null()) {
- AddInstruction(new HCheckPrototypeMaps(receiver,
- expr->holder(),
- receiver_map));
+ AddInstruction(new HCheckPrototypeMaps(
+ Handle<JSObject>(JSObject::cast(receiver_map->prototype())),
+ expr->holder()));
}
}
@@ -3841,7 +3886,11 @@ void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr,
HBasicBlock* new_exit_block =
BuildTypeSwitch(&maps, &subgraphs, receiver, expr->id());
subgraph()->set_exit_block(new_exit_block);
- if (new_exit_block != NULL) ast_context()->ReturnValue(Pop());
+ // In an effect context, we did not materialized the value in the
+ // predecessor environments so there's no need to handle it here.
+ if (new_exit_block != NULL && !ast_context()->IsEffect()) {
+ ast_context()->ReturnValue(Pop());
+ }
}
}
@@ -3977,7 +4026,9 @@ bool HGraphBuilder::TryInline(Call* expr) {
function_return_->MarkAsInlineReturnTarget();
}
call_context_ = ast_context();
- TypeFeedbackOracle new_oracle(Handle<Code>(shared->code()));
+ TypeFeedbackOracle new_oracle(
+ Handle<Code>(shared->code()),
+ Handle<Context>(target->context()->global_context()));
oracle_ = &new_oracle;
graph()->info()->SetOsrAstId(AstNode::kNoNumber);
@@ -4179,7 +4230,8 @@ bool HGraphBuilder::TryCallApply(Call* expr) {
HValue* arg_two_value = environment()->Lookup(arg_two->var());
if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false;
- if (!expr->IsMonomorphic()) return false;
+ if (!expr->IsMonomorphic() ||
+ expr->check_type() != RECEIVER_MAP_CHECK) return false;
// Found pattern f.apply(receiver, arguments).
VisitForValue(prop->obj());
@@ -4248,7 +4300,7 @@ void HGraphBuilder::VisitCall(Call* expr) {
expr->RecordTypeFeedback(oracle());
ZoneMapList* types = expr->GetReceiverTypes();
- if (expr->IsMonomorphic()) {
+ if (expr->IsMonomorphic() && expr->check_type() == RECEIVER_MAP_CHECK) {
AddCheckConstantFunction(expr, receiver, types->first(), true);
if (TryMathFunctionInline(expr)) {
@@ -4273,6 +4325,7 @@ void HGraphBuilder::VisitCall(Call* expr) {
}
} else if (types != NULL && types->length() > 1) {
+ ASSERT(expr->check_type() == RECEIVER_MAP_CHECK);
HandlePolymorphicCallNamed(expr, receiver, types, name);
return;
@@ -4847,14 +4900,49 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
TypeInfo info = oracle()->CompareType(expr, TypeFeedbackOracle::RESULT);
HInstruction* instr = NULL;
if (op == Token::INSTANCEOF) {
- instr = new HInstanceOf(left, right);
+ // Check to see if the rhs of the instanceof is a global function not
+ // residing in new space. If it is we assume that the function will stay the
+ // same.
+ Handle<JSFunction> target = Handle<JSFunction>::null();
+ Variable* var = expr->right()->AsVariableProxy()->AsVariable();
+ bool global_function = (var != NULL) && var->is_global() && !var->is_this();
+ CompilationInfo* info = graph()->info();
+ if (global_function &&
+ info->has_global_object() &&
+ !info->global_object()->IsAccessCheckNeeded()) {
+ Handle<String> name = var->name();
+ Handle<GlobalObject> global(info->global_object());
+ LookupResult lookup;
+ global->Lookup(*name, &lookup);
+ if (lookup.IsProperty() &&
+ lookup.type() == NORMAL &&
+ lookup.GetValue()->IsJSFunction()) {
+ Handle<JSFunction> candidate(JSFunction::cast(lookup.GetValue()));
+ // If the function is in new space we assume it's more likely to
+ // change and thus prefer the general IC code.
+ if (!Heap::InNewSpace(*candidate)) {
+ target = candidate;
+ }
+ }
+ }
+
+ // If the target is not null we have found a known global function that is
+ // assumed to stay the same for this instanceof.
+ if (target.is_null()) {
+ instr = new HInstanceOf(left, right);
+ } else {
+ AddInstruction(new HCheckFunction(right, target));
+ instr = new HInstanceOfKnownGlobal(left, target);
+ }
} else if (op == Token::IN) {
BAILOUT("Unsupported comparison: in");
} else if (info.IsNonPrimitive()) {
switch (op) {
case Token::EQ:
case Token::EQ_STRICT: {
+ AddInstruction(new HCheckNonSmi(left));
AddInstruction(HCheckInstanceType::NewIsJSObjectOrJSFunction(left));
+ AddInstruction(new HCheckNonSmi(right));
AddInstruction(HCheckInstanceType::NewIsJSObjectOrJSFunction(right));
instr = new HCompareJSObjectEq(left, right);
break;
@@ -5262,6 +5350,19 @@ void HEnvironment::Initialize(int parameter_count,
}
+void HEnvironment::Initialize(const HEnvironment* other) {
+ closure_ = other->closure();
+ values_.AddAll(other->values_);
+ assigned_variables_.AddAll(other->assigned_variables_);
+ parameter_count_ = other->parameter_count_;
+ local_count_ = other->local_count_;
+ if (other->outer_ != NULL) outer_ = other->outer_->Copy(); // Deep copy.
+ pop_count_ = other->pop_count_;
+ push_count_ = other->push_count_;
+ ast_id_ = other->ast_id_;
+}
+
+
void HEnvironment::AddIncomingEdge(HBasicBlock* block, HEnvironment* other) {
ASSERT(!block->IsLoopHeader());
ASSERT(values_.length() == other->values_.length());
@@ -5292,26 +5393,45 @@ void HEnvironment::AddIncomingEdge(HBasicBlock* block, HEnvironment* other) {
}
-void HEnvironment::Initialize(const HEnvironment* other) {
- closure_ = other->closure();
- values_.AddAll(other->values_);
- assigned_variables_.AddAll(other->assigned_variables_);
- parameter_count_ = other->parameter_count_;
- local_count_ = other->local_count_;
- if (other->outer_ != NULL) outer_ = other->outer_->Copy(); // Deep copy.
- pop_count_ = other->pop_count_;
- push_count_ = other->push_count_;
- ast_id_ = other->ast_id_;
+void HEnvironment::Bind(int index, HValue* value) {
+ ASSERT(value != NULL);
+ if (!assigned_variables_.Contains(index)) {
+ assigned_variables_.Add(index);
+ }
+ values_[index] = value;
}
-int HEnvironment::IndexFor(Variable* variable) const {
- Slot* slot = variable->AsSlot();
- ASSERT(slot != NULL && slot->IsStackAllocated());
- if (slot->type() == Slot::PARAMETER) {
- return slot->index() + 1;
- } else {
- return parameter_count_ + slot->index();
+bool HEnvironment::HasExpressionAt(int index) const {
+ return index >= parameter_count_ + local_count_;
+}
+
+
+bool HEnvironment::ExpressionStackIsEmpty() const {
+ int first_expression = parameter_count() + local_count();
+ ASSERT(length() >= first_expression);
+ return length() == first_expression;
+}
+
+
+void HEnvironment::SetExpressionStackAt(int index_from_top, HValue* value) {
+ int count = index_from_top + 1;
+ int index = values_.length() - count;
+ ASSERT(HasExpressionAt(index));
+ // The push count must include at least the element in question or else
+ // the new value will not be included in this environment's history.
+ if (push_count_ < count) {
+ // This is the same effect as popping then re-pushing 'count' elements.
+ pop_count_ += (count - push_count_);
+ push_count_ = count;
+ }
+ values_[index] = value;
+}
+
+
+void HEnvironment::Drop(int count) {
+ for (int i = 0; i < count; ++i) {
+ Pop();
}
}
@@ -5376,7 +5496,7 @@ HEnvironment* HEnvironment::CopyForInlining(Handle<JSFunction> target,
void HEnvironment::PrintTo(StringStream* stream) {
- for (int i = 0; i < total_count(); i++) {
+ for (int i = 0; i < length(); i++) {
if (i == 0) stream->Add("parameters\n");
if (i == parameter_count()) stream->Add("locals\n");
if (i == parameter_count() + local_count()) stream->Add("expressions");
@@ -5614,31 +5734,40 @@ void HStatistics::Print() {
PrintF("%30s", names_[i]);
double ms = static_cast<double>(timing_[i]) / 1000;
double percent = static_cast<double>(timing_[i]) * 100 / sum;
- PrintF(" - %0.3f ms / %0.3f %% \n", ms, percent);
+ PrintF(" - %7.3f ms / %4.1f %% ", ms, percent);
+
+ unsigned size = sizes_[i];
+ double size_percent = static_cast<double>(size) * 100 / total_size_;
+ PrintF(" %8u bytes / %4.1f %%\n", size, size_percent);
}
- PrintF("%30s - %0.3f ms \n", "Sum", static_cast<double>(sum) / 1000);
+ PrintF("%30s - %7.3f ms %8u bytes\n", "Sum",
+ static_cast<double>(sum) / 1000,
+ total_size_);
PrintF("---------------------------------------------------------------\n");
- PrintF("%30s - %0.3f ms (%0.1f times slower than full code gen)\n",
+ PrintF("%30s - %7.3f ms (%.1f times slower than full code gen)\n",
"Total",
static_cast<double>(total_) / 1000,
static_cast<double>(total_) / full_code_gen_);
}
-void HStatistics::SaveTiming(const char* name, int64_t ticks) {
+void HStatistics::SaveTiming(const char* name, int64_t ticks, unsigned size) {
if (name == HPhase::kFullCodeGen) {
full_code_gen_ += ticks;
} else if (name == HPhase::kTotal) {
total_ += ticks;
} else {
+ total_size_ += size;
for (int i = 0; i < names_.length(); ++i) {
if (names_[i] == name) {
timing_[i] += ticks;
+ sizes_[i] += size;
return;
}
}
names_.Add(name);
timing_.Add(ticks);
+ sizes_.Add(size);
}
}
@@ -5659,13 +5788,15 @@ void HPhase::Begin(const char* name,
chunk_ = allocator->chunk();
}
if (FLAG_time_hydrogen) start_ = OS::Ticks();
+ start_allocation_size_ = Zone::allocation_size_;
}
void HPhase::End() const {
if (FLAG_time_hydrogen) {
int64_t end = OS::Ticks();
- HStatistics::Instance()->SaveTiming(name_, end - start_);
+ unsigned size = Zone::allocation_size_ - start_allocation_size_;
+ HStatistics::Instance()->SaveTiming(name_, end - start_, size);
}
if (FLAG_trace_hydrogen) {
@@ -5678,7 +5809,6 @@ void HPhase::End() const {
#ifdef DEBUG
if (graph_ != NULL) graph_->Verify();
- if (chunk_ != NULL) chunk_->Verify();
if (allocator_ != NULL) allocator_->Verify();
#endif
}
diff --git a/deps/v8/src/hydrogen.h b/deps/v8/src/hydrogen.h
index ebabf3d292..35165ae97a 100644
--- a/deps/v8/src/hydrogen.h
+++ b/deps/v8/src/hydrogen.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -401,27 +401,33 @@ class HEnvironment: public ZoneObject {
Scope* scope,
Handle<JSFunction> closure);
+ // Simple accessors.
+ Handle<JSFunction> closure() const { return closure_; }
+ const ZoneList<HValue*>* values() const { return &values_; }
+ const ZoneList<int>* assigned_variables() const {
+ return &assigned_variables_;
+ }
+ int parameter_count() const { return parameter_count_; }
+ int local_count() const { return local_count_; }
+ HEnvironment* outer() const { return outer_; }
+ int pop_count() const { return pop_count_; }
+ int push_count() const { return push_count_; }
+
+ int ast_id() const { return ast_id_; }
+ void set_ast_id(int id) { ast_id_ = id; }
+
+ int length() const { return values_.length(); }
+
void Bind(Variable* variable, HValue* value) {
Bind(IndexFor(variable), value);
-
- if (FLAG_trace_environment) {
- PrintF("Slot index=%d name=%s\n",
- variable->AsSlot()->index(),
- *variable->name()->ToCString());
- }
}
- void Bind(int index, HValue* value) {
- ASSERT(value != NULL);
- if (!assigned_variables_.Contains(index)) {
- assigned_variables_.Add(index);
- }
- values_[index] = value;
- }
+ void Bind(int index, HValue* value);
HValue* Lookup(Variable* variable) const {
return Lookup(IndexFor(variable));
}
+
HValue* Lookup(int index) const {
HValue* result = values_[index];
ASSERT(result != NULL);
@@ -434,53 +440,28 @@ class HEnvironment: public ZoneObject {
values_.Add(value);
}
- HValue* Top() const { return ExpressionStackAt(0); }
-
- HValue* ExpressionStackAt(int index_from_top) const {
- int index = values_.length() - index_from_top - 1;
- ASSERT(IsExpressionStackIndex(index));
- return values_[index];
- }
-
- void SetExpressionStackAt(int index_from_top, HValue* value) {
- int index = values_.length() - index_from_top - 1;
- ASSERT(IsExpressionStackIndex(index));
- values_[index] = value;
- }
-
HValue* Pop() {
- ASSERT(!IsExpressionStackEmpty());
+ ASSERT(!ExpressionStackIsEmpty());
if (push_count_ > 0) {
--push_count_;
- ASSERT(push_count_ >= 0);
} else {
++pop_count_;
}
return values_.RemoveLast();
}
- void Drop(int count) {
- for (int i = 0; i < count; ++i) {
- Pop();
- }
- }
-
- Handle<JSFunction> closure() const { return closure_; }
+ void Drop(int count);
- // ID of the original AST node to identify deoptimization points.
- int ast_id() const { return ast_id_; }
- void set_ast_id(int id) { ast_id_ = id; }
+ HValue* Top() const { return ExpressionStackAt(0); }
- const ZoneList<HValue*>* values() const { return &values_; }
- const ZoneList<int>* assigned_variables() const {
- return &assigned_variables_;
+ HValue* ExpressionStackAt(int index_from_top) const {
+ int index = length() - index_from_top - 1;
+ ASSERT(HasExpressionAt(index));
+ return values_[index];
}
- int parameter_count() const { return parameter_count_; }
- int local_count() const { return local_count_; }
- int push_count() const { return push_count_; }
- int pop_count() const { return pop_count_; }
- int total_count() const { return values_.length(); }
- HEnvironment* outer() const { return outer_; }
+
+ void SetExpressionStackAt(int index_from_top, HValue* value);
+
HEnvironment* Copy() const;
HEnvironment* CopyWithoutHistory() const;
HEnvironment* CopyAsLoopHeader(HBasicBlock* block) const;
@@ -496,13 +477,15 @@ class HEnvironment: public ZoneObject {
HConstant* undefined) const;
void AddIncomingEdge(HBasicBlock* block, HEnvironment* other);
+
void ClearHistory() {
pop_count_ = 0;
push_count_ = 0;
assigned_variables_.Clear();
}
+
void SetValueAt(int index, HValue* value) {
- ASSERT(index < total_count());
+ ASSERT(index < length());
values_[index] = value;
}
@@ -512,19 +495,23 @@ class HEnvironment: public ZoneObject {
private:
explicit HEnvironment(const HEnvironment* other);
- bool IsExpressionStackIndex(int index) const {
- return index >= parameter_count_ + local_count_;
- }
- bool IsExpressionStackEmpty() const {
- int length = values_.length();
- int first_expression = parameter_count() + local_count();
- ASSERT(length >= first_expression);
- return length == first_expression;
- }
+ // True if index is included in the expression stack part of the environment.
+ bool HasExpressionAt(int index) const;
+
+ bool ExpressionStackIsEmpty() const;
+
void Initialize(int parameter_count, int local_count, int stack_height);
void Initialize(const HEnvironment* other);
- int VariableToIndex(Variable* var);
- int IndexFor(Variable* variable) const;
+
+ // Map a variable to an environment index. Parameter indices are shifted
+ // by 1 (receiver is parameter index -1 but environment index 0).
+ // Stack-allocated local indices are shifted by the number of parameters.
+ int IndexFor(Variable* variable) const {
+ Slot* slot = variable->AsSlot();
+ ASSERT(slot != NULL && slot->IsStackAllocated());
+ int shift = (slot->type() == Slot::PARAMETER) ? 1 : parameter_count_;
+ return slot->index() + shift;
+ }
Handle<JSFunction> closure_;
// Value array [parameters] [locals] [temporaries].
@@ -567,7 +554,7 @@ class AstContext {
// We want to be able to assert, in a context-specific way, that the stack
// height makes sense when the context is filled.
#ifdef DEBUG
- int original_count_;
+ int original_length_;
#endif
private:
@@ -919,7 +906,7 @@ class HValueMap: public ZoneObject {
class HStatistics: public Malloced {
public:
void Print();
- void SaveTiming(const char* name, int64_t ticks);
+ void SaveTiming(const char* name, int64_t ticks, unsigned size);
static HStatistics* Instance() {
static SetOncePointer<HStatistics> instance;
if (!instance.is_set()) {
@@ -930,11 +917,19 @@ class HStatistics: public Malloced {
private:
- HStatistics() : timing_(5), names_(5), total_(0), full_code_gen_(0) { }
+ HStatistics()
+ : timing_(5),
+ names_(5),
+ sizes_(5),
+ total_(0),
+ total_size_(0),
+ full_code_gen_(0) { }
List<int64_t> timing_;
List<const char*> names_;
+ List<unsigned> sizes_;
int64_t total_;
+ unsigned total_size_;
int64_t full_code_gen_;
};
@@ -971,6 +966,7 @@ class HPhase BASE_EMBEDDED {
HGraph* graph_;
LChunk* chunk_;
LAllocator* allocator_;
+ unsigned start_allocation_size_;
};
diff --git a/deps/v8/src/ia32/assembler-ia32.cc b/deps/v8/src/ia32/assembler-ia32.cc
index c173a3dc5e..552d7b5eea 100644
--- a/deps/v8/src/ia32/assembler-ia32.cc
+++ b/deps/v8/src/ia32/assembler-ia32.cc
@@ -2465,6 +2465,17 @@ void Assembler::pxor(XMMRegister dst, XMMRegister src) {
}
+void Assembler::por(XMMRegister dst, XMMRegister src) {
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0xEB);
+ emit_sse_operand(dst, src);
+}
+
+
void Assembler::ptest(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE4_1));
EnsureSpace ensure_space(this);
@@ -2489,6 +2500,40 @@ void Assembler::psllq(XMMRegister reg, int8_t shift) {
}
+void Assembler::psllq(XMMRegister dst, XMMRegister src) {
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0xF3);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::psrlq(XMMRegister reg, int8_t shift) {
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x73);
+ emit_sse_operand(edx, reg); // edx == 2
+ EMIT(shift);
+}
+
+
+void Assembler::psrlq(XMMRegister dst, XMMRegister src) {
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0xD3);
+ emit_sse_operand(dst, src);
+}
+
+
void Assembler::pshufd(XMMRegister dst, XMMRegister src, int8_t shuffle) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
diff --git a/deps/v8/src/ia32/assembler-ia32.h b/deps/v8/src/ia32/assembler-ia32.h
index 11acb56110..20446b0085 100644
--- a/deps/v8/src/ia32/assembler-ia32.h
+++ b/deps/v8/src/ia32/assembler-ia32.h
@@ -919,9 +919,13 @@ class Assembler : public Malloced {
void pand(XMMRegister dst, XMMRegister src);
void pxor(XMMRegister dst, XMMRegister src);
+ void por(XMMRegister dst, XMMRegister src);
void ptest(XMMRegister dst, XMMRegister src);
void psllq(XMMRegister reg, int8_t shift);
+ void psllq(XMMRegister dst, XMMRegister src);
+ void psrlq(XMMRegister reg, int8_t shift);
+ void psrlq(XMMRegister dst, XMMRegister src);
void pshufd(XMMRegister dst, XMMRegister src, int8_t shuffle);
void pextrd(const Operand& dst, XMMRegister src, int8_t offset);
diff --git a/deps/v8/src/ia32/builtins-ia32.cc b/deps/v8/src/ia32/builtins-ia32.cc
index 918f346d89..0a3e093056 100644
--- a/deps/v8/src/ia32/builtins-ia32.cc
+++ b/deps/v8/src/ia32/builtins-ia32.cc
@@ -399,7 +399,7 @@ void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
bool is_construct) {
// Clear the context before we push it when entering the JS frame.
- __ xor_(esi, Operand(esi)); // clear esi
+ __ Set(esi, Immediate(0));
// Enter an internal frame.
__ EnterInternalFrame();
@@ -421,7 +421,7 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
// Copy arguments to the stack in a loop.
Label loop, entry;
- __ xor_(ecx, Operand(ecx)); // clear ecx
+ __ Set(ecx, Immediate(0));
__ jmp(&entry);
__ bind(&loop);
__ mov(edx, Operand(ebx, ecx, times_4, 0)); // push parameter from argv
@@ -644,7 +644,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ bind(&non_function);
__ mov(Operand(esp, eax, times_4, 0), edi);
// Clear edi to indicate a non-function being called.
- __ xor_(edi, Operand(edi));
+ __ Set(edi, Immediate(0));
// 4. Shift arguments and return address one slot down on the stack
// (overwriting the original receiver). Adjust argument count to make
@@ -665,7 +665,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
{ Label function;
__ test(edi, Operand(edi));
__ j(not_zero, &function, taken);
- __ xor_(ebx, Operand(ebx));
+ __ Set(ebx, Immediate(0));
__ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
__ jmp(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
RelocInfo::CODE_TARGET);
diff --git a/deps/v8/src/ia32/code-stubs-ia32.cc b/deps/v8/src/ia32/code-stubs-ia32.cc
index a371c96393..72213dc817 100644
--- a/deps/v8/src/ia32/code-stubs-ia32.cc
+++ b/deps/v8/src/ia32/code-stubs-ia32.cc
@@ -104,7 +104,7 @@ void FastNewContextStub::Generate(MacroAssembler* masm) {
Immediate(Smi::FromInt(length)));
// Setup the fixed slots.
- __ xor_(ebx, Operand(ebx)); // Set to NULL.
+ __ Set(ebx, Immediate(0)); // Set to NULL.
__ mov(Operand(eax, Context::SlotOffset(Context::CLOSURE_INDEX)), ecx);
__ mov(Operand(eax, Context::SlotOffset(Context::FCONTEXT_INDEX)), eax);
__ mov(Operand(eax, Context::SlotOffset(Context::PREVIOUS_INDEX)), ebx);
@@ -1772,7 +1772,6 @@ void TypeRecordingBinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
}
-
void TypeRecordingBinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
Label call_runtime;
ASSERT(operands_type_ == TRBinaryOpIC::STRING);
@@ -2016,8 +2015,7 @@ void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
void TypeRecordingBinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
Label call_runtime;
- ASSERT(operands_type_ == TRBinaryOpIC::HEAP_NUMBER ||
- operands_type_ == TRBinaryOpIC::INT32);
+ ASSERT(operands_type_ == TRBinaryOpIC::HEAP_NUMBER);
// Floating point case.
switch (op_) {
@@ -4303,7 +4301,7 @@ void CompareStub::Generate(MacroAssembler* masm) {
// that contains the exponent and high bit of the mantissa.
STATIC_ASSERT(((kQuietNaNHighBitsMask << 1) & 0x80000000u) != 0);
__ mov(edx, FieldOperand(edx, HeapNumber::kExponentOffset));
- __ xor_(eax, Operand(eax));
+ __ Set(eax, Immediate(0));
// Shift value and mask so kQuietNaNHighBitsMask applies to topmost
// bits.
__ add(edx, Operand(edx));
@@ -4433,7 +4431,7 @@ void CompareStub::Generate(MacroAssembler* masm) {
__ j(below, &below_label, not_taken);
__ j(above, &above_label, not_taken);
- __ xor_(eax, Operand(eax));
+ __ Set(eax, Immediate(0));
__ ret(0);
__ bind(&below_label);
@@ -4646,7 +4644,7 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
// Before returning we restore the context from the frame pointer if
// not NULL. The frame pointer is NULL in the exception handler of
// a JS entry frame.
- __ xor_(esi, Operand(esi)); // Tentatively set context pointer to NULL.
+ __ Set(esi, Immediate(0)); // Tentatively set context pointer to NULL.
NearLabel skip;
__ cmp(ebp, 0);
__ j(equal, &skip, not_taken);
@@ -4799,7 +4797,7 @@ void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
}
// Clear the context pointer.
- __ xor_(esi, Operand(esi));
+ __ Set(esi, Immediate(0));
// Restore fp from handler and discard handler state.
STATIC_ASSERT(StackHandlerConstants::kFPOffset == 1 * kPointerSize);
@@ -4973,7 +4971,26 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
}
+// Generate stub code for instanceof.
+// This code can patch a call site inlined cache of the instance of check,
+// which looks like this.
+//
+// 81 ff XX XX XX XX cmp edi, <the hole, patched to a map>
+// 75 0a jne <some near label>
+// b8 XX XX XX XX mov eax, <the hole, patched to either true or false>
+//
+// If call site patching is requested the stack will have the delta from the
+// return address to the cmp instruction just below the return address. This
+// also means that call site patching can only take place with arguments in
+// registers. TOS looks like this when call site patching is requested
+//
+// esp[0] : return address
+// esp[4] : delta from return address to cmp instruction
+//
void InstanceofStub::Generate(MacroAssembler* masm) {
+ // Call site inlining and patching implies arguments in registers.
+ ASSERT(HasArgsInRegisters() || !HasCallSiteInlineCheck());
+
// Fixed register usage throughout the stub.
Register object = eax; // Object (lhs).
Register map = ebx; // Map of the object.
@@ -4981,9 +4998,22 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
Register prototype = edi; // Prototype of the function.
Register scratch = ecx;
+ // Constants describing the call site code to patch.
+ static const int kDeltaToCmpImmediate = 2;
+ static const int kDeltaToMov = 8;
+ static const int kDeltaToMovImmediate = 9;
+ static const int8_t kCmpEdiImmediateByte1 = BitCast<int8_t, uint8_t>(0x81);
+ static const int8_t kCmpEdiImmediateByte2 = BitCast<int8_t, uint8_t>(0xff);
+ static const int8_t kMovEaxImmediateByte = BitCast<int8_t, uint8_t>(0xb8);
+
+ ExternalReference roots_address = ExternalReference::roots_address();
+
+ ASSERT_EQ(object.code(), InstanceofStub::left().code());
+ ASSERT_EQ(function.code(), InstanceofStub::right().code());
+
// Get the object and function - they are always both needed.
Label slow, not_js_object;
- if (!args_in_registers()) {
+ if (!HasArgsInRegisters()) {
__ mov(object, Operand(esp, 2 * kPointerSize));
__ mov(function, Operand(esp, 1 * kPointerSize));
}
@@ -4993,22 +5023,26 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
__ j(zero, &not_js_object, not_taken);
__ IsObjectJSObjectType(object, map, scratch, &not_js_object);
- // Look up the function and the map in the instanceof cache.
- NearLabel miss;
- ExternalReference roots_address = ExternalReference::roots_address();
- __ mov(scratch, Immediate(Heap::kInstanceofCacheFunctionRootIndex));
- __ cmp(function,
- Operand::StaticArray(scratch, times_pointer_size, roots_address));
- __ j(not_equal, &miss);
- __ mov(scratch, Immediate(Heap::kInstanceofCacheMapRootIndex));
- __ cmp(map, Operand::StaticArray(scratch, times_pointer_size, roots_address));
- __ j(not_equal, &miss);
- __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
- __ mov(eax, Operand::StaticArray(scratch, times_pointer_size, roots_address));
- __ IncrementCounter(&Counters::instance_of_cache, 1);
- __ ret((args_in_registers() ? 0 : 2) * kPointerSize);
+ // If there is a call site cache don't look in the global cache, but do the
+ // real lookup and update the call site cache.
+ if (!HasCallSiteInlineCheck()) {
+ // Look up the function and the map in the instanceof cache.
+ NearLabel miss;
+ __ mov(scratch, Immediate(Heap::kInstanceofCacheFunctionRootIndex));
+ __ cmp(function,
+ Operand::StaticArray(scratch, times_pointer_size, roots_address));
+ __ j(not_equal, &miss);
+ __ mov(scratch, Immediate(Heap::kInstanceofCacheMapRootIndex));
+ __ cmp(map, Operand::StaticArray(
+ scratch, times_pointer_size, roots_address));
+ __ j(not_equal, &miss);
+ __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
+ __ mov(eax, Operand::StaticArray(
+ scratch, times_pointer_size, roots_address));
+ __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
+ __ bind(&miss);
+ }
- __ bind(&miss);
// Get the prototype of the function.
__ TryGetFunctionPrototype(function, prototype, scratch, &slow);
@@ -5017,13 +5051,29 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
__ j(zero, &slow, not_taken);
__ IsObjectJSObjectType(prototype, scratch, scratch, &slow);
- // Update the golbal instanceof cache with the current map and function. The
- // cached answer will be set when it is known.
+ // Update the global instanceof or call site inlined cache with the current
+ // map and function. The cached answer will be set when it is known below.
+ if (!HasCallSiteInlineCheck()) {
__ mov(scratch, Immediate(Heap::kInstanceofCacheMapRootIndex));
__ mov(Operand::StaticArray(scratch, times_pointer_size, roots_address), map);
__ mov(scratch, Immediate(Heap::kInstanceofCacheFunctionRootIndex));
__ mov(Operand::StaticArray(scratch, times_pointer_size, roots_address),
function);
+ } else {
+ // The constants for the code patching are based on no push instructions
+ // at the call site.
+ ASSERT(HasArgsInRegisters());
+ // Get return address and delta to inlined map check.
+ __ mov(scratch, Operand(esp, 0 * kPointerSize));
+ __ sub(scratch, Operand(esp, 1 * kPointerSize));
+ if (FLAG_debug_code) {
+ __ cmpb(Operand(scratch, 0), kCmpEdiImmediateByte1);
+ __ Assert(equal, "InstanceofStub unexpected call site cache (cmp 1)");
+ __ cmpb(Operand(scratch, 1), kCmpEdiImmediateByte2);
+ __ Assert(equal, "InstanceofStub unexpected call site cache (cmp 2)");
+ }
+ __ mov(Operand(scratch, kDeltaToCmpImmediate), map);
+ }
// Loop through the prototype chain of the object looking for the function
// prototype.
@@ -5039,18 +5089,48 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
__ jmp(&loop);
__ bind(&is_instance);
- __ IncrementCounter(&Counters::instance_of_stub_true, 1);
- __ Set(eax, Immediate(0));
- __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
- __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_address), eax);
- __ ret((args_in_registers() ? 0 : 2) * kPointerSize);
+ if (!HasCallSiteInlineCheck()) {
+ __ Set(eax, Immediate(0));
+ __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
+ __ mov(Operand::StaticArray(scratch,
+ times_pointer_size, roots_address), eax);
+ } else {
+ // Get return address and delta to inlined map check.
+ __ mov(eax, Factory::true_value());
+ __ mov(scratch, Operand(esp, 0 * kPointerSize));
+ __ sub(scratch, Operand(esp, 1 * kPointerSize));
+ if (FLAG_debug_code) {
+ __ cmpb(Operand(scratch, kDeltaToMov), kMovEaxImmediateByte);
+ __ Assert(equal, "InstanceofStub unexpected call site cache (mov)");
+ }
+ __ mov(Operand(scratch, kDeltaToMovImmediate), eax);
+ if (!ReturnTrueFalseObject()) {
+ __ Set(eax, Immediate(0));
+ }
+ }
+ __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
__ bind(&is_not_instance);
- __ IncrementCounter(&Counters::instance_of_stub_false, 1);
- __ Set(eax, Immediate(Smi::FromInt(1)));
- __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
- __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_address), eax);
- __ ret((args_in_registers() ? 0 : 2) * kPointerSize);
+ if (!HasCallSiteInlineCheck()) {
+ __ Set(eax, Immediate(Smi::FromInt(1)));
+ __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
+ __ mov(Operand::StaticArray(
+ scratch, times_pointer_size, roots_address), eax);
+ } else {
+ // Get return address and delta to inlined map check.
+ __ mov(eax, Factory::false_value());
+ __ mov(scratch, Operand(esp, 0 * kPointerSize));
+ __ sub(scratch, Operand(esp, 1 * kPointerSize));
+ if (FLAG_debug_code) {
+ __ cmpb(Operand(scratch, kDeltaToMov), kMovEaxImmediateByte);
+ __ Assert(equal, "InstanceofStub unexpected call site cache (mov)");
+ }
+ __ mov(Operand(scratch, kDeltaToMovImmediate), eax);
+ if (!ReturnTrueFalseObject()) {
+ __ Set(eax, Immediate(Smi::FromInt(1)));
+ }
+ }
+ __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
Label object_not_null, object_not_null_or_smi;
__ bind(&not_js_object);
@@ -5064,39 +5144,61 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
// Null is not instance of anything.
__ cmp(object, Factory::null_value());
__ j(not_equal, &object_not_null);
- __ IncrementCounter(&Counters::instance_of_stub_false_null, 1);
__ Set(eax, Immediate(Smi::FromInt(1)));
- __ ret((args_in_registers() ? 0 : 2) * kPointerSize);
+ __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
__ bind(&object_not_null);
// Smi values is not instance of anything.
__ test(object, Immediate(kSmiTagMask));
__ j(not_zero, &object_not_null_or_smi, not_taken);
__ Set(eax, Immediate(Smi::FromInt(1)));
- __ ret((args_in_registers() ? 0 : 2) * kPointerSize);
+ __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
__ bind(&object_not_null_or_smi);
// String values is not instance of anything.
Condition is_string = masm->IsObjectStringType(object, scratch, scratch);
__ j(NegateCondition(is_string), &slow);
- __ IncrementCounter(&Counters::instance_of_stub_false_string, 1);
__ Set(eax, Immediate(Smi::FromInt(1)));
- __ ret((args_in_registers() ? 0 : 2) * kPointerSize);
+ __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
// Slow-case: Go through the JavaScript implementation.
__ bind(&slow);
- if (args_in_registers()) {
- // Push arguments below return address.
- __ pop(scratch);
+ if (!ReturnTrueFalseObject()) {
+ // Tail call the builtin which returns 0 or 1.
+ if (HasArgsInRegisters()) {
+ // Push arguments below return address.
+ __ pop(scratch);
+ __ push(object);
+ __ push(function);
+ __ push(scratch);
+ }
+ __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
+ } else {
+ // Call the builtin and convert 0/1 to true/false.
+ __ EnterInternalFrame();
__ push(object);
__ push(function);
- __ push(scratch);
+ __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
+ __ LeaveInternalFrame();
+ NearLabel true_value, done;
+ __ test(eax, Operand(eax));
+ __ j(zero, &true_value);
+ __ mov(eax, Factory::false_value());
+ __ jmp(&done);
+ __ bind(&true_value);
+ __ mov(eax, Factory::true_value());
+ __ bind(&done);
+ __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
}
- __ IncrementCounter(&Counters::instance_of_slow, 1);
- __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
}
+Register InstanceofStub::left() { return eax; }
+
+
+Register InstanceofStub::right() { return edx; }
+
+
int CompareStub::MinorKey() {
// Encode the three parameters in a unique 16 bit value. To avoid duplicate
// stubs the never NaN NaN condition is only taken into account if the
diff --git a/deps/v8/src/ia32/code-stubs-ia32.h b/deps/v8/src/ia32/code-stubs-ia32.h
index f66a8c7e45..4a56d0d143 100644
--- a/deps/v8/src/ia32/code-stubs-ia32.h
+++ b/deps/v8/src/ia32/code-stubs-ia32.h
@@ -250,13 +250,6 @@ class TypeRecordingBinaryOpStub: public CodeStub {
result_type_(result_type),
name_(NULL) { }
- // Generate code to call the stub with the supplied arguments. This will add
- // code at the call site to prepare arguments either in registers or on the
- // stack together with the actual call.
- void GenerateCall(MacroAssembler* masm, Register left, Register right);
- void GenerateCall(MacroAssembler* masm, Register left, Smi* right);
- void GenerateCall(MacroAssembler* masm, Smi* left, Register right);
-
private:
enum SmiCodeGenerateHeapNumberResults {
ALLOW_HEAPNUMBER_RESULTS,
@@ -321,10 +314,6 @@ class TypeRecordingBinaryOpStub: public CodeStub {
void GenerateTypeTransition(MacroAssembler* masm);
void GenerateTypeTransitionWithSavedArgs(MacroAssembler* masm);
- bool IsOperationCommutative() {
- return (op_ == Token::ADD) || (op_ == Token::MUL);
- }
-
virtual int GetCodeKind() { return Code::TYPE_RECORDING_BINARY_OP_IC; }
virtual InlineCacheState GetICState() {
diff --git a/deps/v8/src/ia32/codegen-ia32.cc b/deps/v8/src/ia32/codegen-ia32.cc
index 2f14e82e14..1ecfd39ca1 100644
--- a/deps/v8/src/ia32/codegen-ia32.cc
+++ b/deps/v8/src/ia32/codegen-ia32.cc
@@ -745,10 +745,10 @@ Result CodeGenerator::StoreArgumentsObject(bool initial) {
Comment cmnt(masm_, "[ store arguments object");
if (mode == LAZY_ARGUMENTS_ALLOCATION && initial) {
- // When using lazy arguments allocation, we store the hole value
+ // When using lazy arguments allocation, we store the arguments marker value
// as a sentinel indicating that the arguments object hasn't been
// allocated yet.
- frame_->Push(Factory::the_hole_value());
+ frame_->Push(Factory::arguments_marker());
} else {
ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
frame_->PushFunction();
@@ -773,9 +773,9 @@ Result CodeGenerator::StoreArgumentsObject(bool initial) {
if (probe.is_constant()) {
// We have to skip updating the arguments object if it has
// been assigned a proper value.
- skip_arguments = !probe.handle()->IsTheHole();
+ skip_arguments = !probe.handle()->IsArgumentsMarker();
} else {
- __ cmp(Operand(probe.reg()), Immediate(Factory::the_hole_value()));
+ __ cmp(Operand(probe.reg()), Immediate(Factory::arguments_marker()));
probe.Unuse();
done.Branch(not_equal);
}
@@ -3294,9 +3294,9 @@ void CodeGenerator::CallApplyLazy(Expression* applicand,
Label slow, done;
bool try_lazy = true;
if (probe.is_constant()) {
- try_lazy = probe.handle()->IsTheHole();
+ try_lazy = probe.handle()->IsArgumentsMarker();
} else {
- __ cmp(Operand(probe.reg()), Immediate(Factory::the_hole_value()));
+ __ cmp(Operand(probe.reg()), Immediate(Factory::arguments_marker()));
probe.Unuse();
__ j(not_equal, &slow);
}
@@ -5068,7 +5068,7 @@ void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
// object has been lazily loaded yet.
Result result = frame()->Pop();
if (result.is_constant()) {
- if (result.handle()->IsTheHole()) {
+ if (result.handle()->IsArgumentsMarker()) {
result = StoreArgumentsObject(false);
}
frame()->Push(&result);
@@ -5079,7 +5079,7 @@ void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
// indicates that we haven't loaded the arguments object yet, we
// need to do it now.
JumpTarget exit;
- __ cmp(Operand(result.reg()), Immediate(Factory::the_hole_value()));
+ __ cmp(Operand(result.reg()), Immediate(Factory::arguments_marker()));
frame()->Push(&result);
exit.Branch(not_equal);
@@ -6649,38 +6649,41 @@ void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
void CodeGenerator::GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args) {
+ Label bailout, done, one_char_separator, long_separator,
+ non_trivial_array, not_size_one_array, loop, loop_condition,
+ loop_1, loop_1_condition, loop_2, loop_2_entry, loop_3, loop_3_entry;
+
ASSERT(args->length() == 2);
+ // We will leave the separator on the stack until the end of the function.
Load(args->at(1));
+ // Load this to eax (= array)
Load(args->at(0));
Result array_result = frame_->Pop();
array_result.ToRegister(eax);
frame_->SpillAll();
- Label bailout;
- Label done;
// All aliases of the same register have disjoint lifetimes.
Register array = eax;
- Register result_pos = no_reg;
+ Register elements = no_reg; // Will be eax.
- Register index = edi;
+ Register index = edx;
- Register current_string_length = ecx; // Will be ecx when live.
+ Register string_length = ecx;
- Register current_string = edx;
+ Register string = esi;
Register scratch = ebx;
- Register scratch_2 = esi;
- Register new_padding_chars = scratch_2;
-
- Operand separator = Operand(esp, 4 * kPointerSize); // Already pushed.
- Operand elements = Operand(esp, 3 * kPointerSize);
- Operand result = Operand(esp, 2 * kPointerSize);
- Operand padding_chars = Operand(esp, 1 * kPointerSize);
- Operand array_length = Operand(esp, 0);
- __ sub(Operand(esp), Immediate(4 * kPointerSize));
+ Register array_length = edi;
+ Register result_pos = no_reg; // Will be edi.
- // Check that eax is a JSArray
+ // Separator operand is already pushed.
+ Operand separator_operand = Operand(esp, 2 * kPointerSize);
+ Operand result_operand = Operand(esp, 1 * kPointerSize);
+ Operand array_length_operand = Operand(esp, 0);
+ __ sub(Operand(esp), Immediate(2 * kPointerSize));
+ __ cld();
+ // Check that the array is a JSArray
__ test(array, Immediate(kSmiTagMask));
__ j(zero, &bailout);
__ CmpObjectType(array, JS_ARRAY_TYPE, scratch);
@@ -6691,140 +6694,226 @@ void CodeGenerator::GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args) {
1 << Map::kHasFastElements);
__ j(zero, &bailout);
- // If the array is empty, return the empty string.
- __ mov(scratch, FieldOperand(array, JSArray::kLengthOffset));
- __ sar(scratch, 1);
- Label non_trivial;
- __ j(not_zero, &non_trivial);
- __ mov(result, Factory::empty_string());
+ // If the array has length zero, return the empty string.
+ __ mov(array_length, FieldOperand(array, JSArray::kLengthOffset));
+ __ sar(array_length, 1);
+ __ j(not_zero, &non_trivial_array);
+ __ mov(result_operand, Factory::empty_string());
__ jmp(&done);
- __ bind(&non_trivial);
- __ mov(array_length, scratch);
-
- __ mov(scratch, FieldOperand(array, JSArray::kElementsOffset));
- __ mov(elements, scratch);
+ // Save the array length.
+ __ bind(&non_trivial_array);
+ __ mov(array_length_operand, array_length);
+ // Save the FixedArray containing array's elements.
// End of array's live range.
- result_pos = array;
+ elements = array;
+ __ mov(elements, FieldOperand(array, JSArray::kElementsOffset));
array = no_reg;
- // Check that the separator is a flat ascii string.
- __ mov(current_string, separator);
- __ test(current_string, Immediate(kSmiTagMask));
+ // Check that all array elements are sequential ASCII strings, and
+ // accumulate the sum of their lengths, as a smi-encoded value.
+ __ Set(index, Immediate(0));
+ __ Set(string_length, Immediate(0));
+ // Loop condition: while (index < length).
+ // Live loop registers: index, array_length, string,
+ // scratch, string_length, elements.
+ __ jmp(&loop_condition);
+ __ bind(&loop);
+ __ cmp(index, Operand(array_length));
+ __ j(greater_equal, &done);
+
+ __ mov(string, FieldOperand(elements, index,
+ times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ test(string, Immediate(kSmiTagMask));
__ j(zero, &bailout);
- __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset));
- __ mov_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
+ __ mov(scratch, FieldOperand(string, HeapObject::kMapOffset));
+ __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
__ and_(scratch, Immediate(
kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask));
__ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag);
__ j(not_equal, &bailout);
- // If the separator is the empty string, replace it with NULL.
- // The test for NULL is quicker than the empty string test, in a loop.
- __ cmp(FieldOperand(current_string, SeqAsciiString::kLengthOffset),
- Immediate(0));
- Label separator_checked;
- __ j(not_zero, &separator_checked);
- __ mov(separator, Immediate(0));
- __ bind(&separator_checked);
-
- // Check that elements[0] is a flat ascii string, and copy it in new space.
- __ mov(scratch, elements);
- __ mov(current_string, FieldOperand(scratch, FixedArray::kHeaderSize));
- __ test(current_string, Immediate(kSmiTagMask));
+ __ add(string_length,
+ FieldOperand(string, SeqAsciiString::kLengthOffset));
+ __ j(overflow, &bailout);
+ __ add(Operand(index), Immediate(1));
+ __ bind(&loop_condition);
+ __ cmp(index, Operand(array_length));
+ __ j(less, &loop);
+
+ // If array_length is 1, return elements[0], a string.
+ __ cmp(array_length, 1);
+ __ j(not_equal, &not_size_one_array);
+ __ mov(scratch, FieldOperand(elements, FixedArray::kHeaderSize));
+ __ mov(result_operand, scratch);
+ __ jmp(&done);
+
+ __ bind(&not_size_one_array);
+
+ // End of array_length live range.
+ result_pos = array_length;
+ array_length = no_reg;
+
+ // Live registers:
+ // string_length: Sum of string lengths, as a smi.
+ // elements: FixedArray of strings.
+
+ // Check that the separator is a flat ASCII string.
+ __ mov(string, separator_operand);
+ __ test(string, Immediate(kSmiTagMask));
__ j(zero, &bailout);
- __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset));
- __ mov_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
+ __ mov(scratch, FieldOperand(string, HeapObject::kMapOffset));
+ __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
__ and_(scratch, Immediate(
kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask));
__ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag);
__ j(not_equal, &bailout);
- // Allocate space to copy it. Round up the size to the alignment granularity.
- __ mov(current_string_length,
- FieldOperand(current_string, String::kLengthOffset));
- __ shr(current_string_length, 1);
-
+ // Add (separator length times array_length) - separator length
+ // to string_length.
+ __ mov(scratch, separator_operand);
+ __ mov(scratch, FieldOperand(scratch, SeqAsciiString::kLengthOffset));
+ __ sub(string_length, Operand(scratch)); // May be negative, temporarily.
+ __ imul(scratch, array_length_operand);
+ __ j(overflow, &bailout);
+ __ add(string_length, Operand(scratch));
+ __ j(overflow, &bailout);
+
+ __ shr(string_length, 1);
// Live registers and stack values:
- // current_string_length: length of elements[0].
-
- // New string result in new space = elements[0]
- __ AllocateAsciiString(result_pos, current_string_length, scratch_2,
- index, no_reg, &bailout);
- __ mov(result, result_pos);
-
- // Adjust current_string_length to include padding bytes at end of string.
- // Keep track of the number of padding bytes.
- __ mov(new_padding_chars, current_string_length);
- __ add(Operand(current_string_length), Immediate(kObjectAlignmentMask));
- __ and_(Operand(current_string_length), Immediate(~kObjectAlignmentMask));
- __ sub(new_padding_chars, Operand(current_string_length));
- __ neg(new_padding_chars);
- __ mov(padding_chars, new_padding_chars);
-
- Label copy_loop_1_done;
- Label copy_loop_1;
- __ test(current_string_length, Operand(current_string_length));
- __ j(zero, &copy_loop_1_done);
- __ bind(&copy_loop_1);
- __ sub(Operand(current_string_length), Immediate(kPointerSize));
- __ mov(scratch, FieldOperand(current_string, current_string_length,
- times_1, SeqAsciiString::kHeaderSize));
- __ mov(FieldOperand(result_pos, current_string_length,
- times_1, SeqAsciiString::kHeaderSize),
- scratch);
- __ j(not_zero, &copy_loop_1);
- __ bind(&copy_loop_1_done);
-
- __ mov(index, Immediate(1));
+ // string_length
+ // elements
+ __ AllocateAsciiString(result_pos, string_length, scratch,
+ index, string, &bailout);
+ __ mov(result_operand, result_pos);
+ __ lea(result_pos, FieldOperand(result_pos, SeqAsciiString::kHeaderSize));
+
+
+ __ mov(string, separator_operand);
+ __ cmp(FieldOperand(string, SeqAsciiString::kLengthOffset),
+ Immediate(Smi::FromInt(1)));
+ __ j(equal, &one_char_separator);
+ __ j(greater, &long_separator);
+
+
+ // Empty separator case
+ __ mov(index, Immediate(0));
+ __ jmp(&loop_1_condition);
// Loop condition: while (index < length).
- Label loop;
- __ bind(&loop);
- __ cmp(index, array_length);
- __ j(greater_equal, &done);
+ __ bind(&loop_1);
+ // Each iteration of the loop concatenates one string to the result.
+ // Live values in registers:
+ // index: which element of the elements array we are adding to the result.
+ // result_pos: the position to which we are currently copying characters.
+ // elements: the FixedArray of strings we are joining.
+
+ // Get string = array[index].
+ __ mov(string, FieldOperand(elements, index,
+ times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ mov(string_length,
+ FieldOperand(string, String::kLengthOffset));
+ __ shr(string_length, 1);
+ __ lea(string,
+ FieldOperand(string, SeqAsciiString::kHeaderSize));
+ __ CopyBytes(string, result_pos, string_length, scratch);
+ __ add(Operand(index), Immediate(1));
+ __ bind(&loop_1_condition);
+ __ cmp(index, array_length_operand);
+ __ j(less, &loop_1); // End while (index < length).
+ __ jmp(&done);
- // If the separator is the empty string, signalled by NULL, skip it.
- Label separator_done;
- __ mov(current_string, separator);
- __ test(current_string, Operand(current_string));
- __ j(zero, &separator_done);
-
- // Append separator to result. It is known to be a flat ascii string.
- __ AppendStringToTopOfNewSpace(current_string, current_string_length,
- result_pos, scratch, scratch_2, result,
- padding_chars, &bailout);
- __ bind(&separator_done);
-
- // Add next element of array to the end of the result.
- // Get current_string = array[index].
- __ mov(scratch, elements);
- __ mov(current_string, FieldOperand(scratch, index,
- times_pointer_size,
- FixedArray::kHeaderSize));
- // If current != flat ascii string drop result, return undefined.
- __ test(current_string, Immediate(kSmiTagMask));
- __ j(zero, &bailout);
- __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset));
- __ mov_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
- __ and_(scratch, Immediate(
- kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask));
- __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag);
- __ j(not_equal, &bailout);
- // Append current to the result.
- __ AppendStringToTopOfNewSpace(current_string, current_string_length,
- result_pos, scratch, scratch_2, result,
- padding_chars, &bailout);
+
+ // One-character separator case
+ __ bind(&one_char_separator);
+ // Replace separator with its ascii character value.
+ __ mov_b(scratch, FieldOperand(string, SeqAsciiString::kHeaderSize));
+ __ mov_b(separator_operand, scratch);
+
+ __ Set(index, Immediate(0));
+ // Jump into the loop after the code that copies the separator, so the first
+ // element is not preceded by a separator
+ __ jmp(&loop_2_entry);
+ // Loop condition: while (index < length).
+ __ bind(&loop_2);
+ // Each iteration of the loop concatenates one string to the result.
+ // Live values in registers:
+ // index: which element of the elements array we are adding to the result.
+ // result_pos: the position to which we are currently copying characters.
+
+ // Copy the separator character to the result.
+ __ mov_b(scratch, separator_operand);
+ __ mov_b(Operand(result_pos, 0), scratch);
+ __ inc(result_pos);
+
+ __ bind(&loop_2_entry);
+ // Get string = array[index].
+ __ mov(string, FieldOperand(elements, index,
+ times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ mov(string_length,
+ FieldOperand(string, String::kLengthOffset));
+ __ shr(string_length, 1);
+ __ lea(string,
+ FieldOperand(string, SeqAsciiString::kHeaderSize));
+ __ CopyBytes(string, result_pos, string_length, scratch);
__ add(Operand(index), Immediate(1));
- __ jmp(&loop); // End while (index < length).
+
+ __ cmp(index, array_length_operand);
+ __ j(less, &loop_2); // End while (index < length).
+ __ jmp(&done);
+
+
+ // Long separator case (separator is more than one character).
+ __ bind(&long_separator);
+
+ __ Set(index, Immediate(0));
+ // Jump into the loop after the code that copies the separator, so the first
+ // element is not preceded by a separator
+ __ jmp(&loop_3_entry);
+ // Loop condition: while (index < length).
+ __ bind(&loop_3);
+ // Each iteration of the loop concatenates one string to the result.
+ // Live values in registers:
+ // index: which element of the elements array we are adding to the result.
+ // result_pos: the position to which we are currently copying characters.
+
+ // Copy the separator to the result.
+ __ mov(string, separator_operand);
+ __ mov(string_length,
+ FieldOperand(string, String::kLengthOffset));
+ __ shr(string_length, 1);
+ __ lea(string,
+ FieldOperand(string, SeqAsciiString::kHeaderSize));
+ __ CopyBytes(string, result_pos, string_length, scratch);
+
+ __ bind(&loop_3_entry);
+ // Get string = array[index].
+ __ mov(string, FieldOperand(elements, index,
+ times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ mov(string_length,
+ FieldOperand(string, String::kLengthOffset));
+ __ shr(string_length, 1);
+ __ lea(string,
+ FieldOperand(string, SeqAsciiString::kHeaderSize));
+ __ CopyBytes(string, result_pos, string_length, scratch);
+ __ add(Operand(index), Immediate(1));
+
+ __ cmp(index, array_length_operand);
+ __ j(less, &loop_3); // End while (index < length).
+ __ jmp(&done);
+
__ bind(&bailout);
- __ mov(result, Factory::undefined_value());
+ __ mov(result_operand, Factory::undefined_value());
__ bind(&done);
- __ mov(eax, result);
+ __ mov(eax, result_operand);
// Drop temp values from the stack, and restore context register.
- __ add(Operand(esp), Immediate(4 * kPointerSize));
+ __ add(Operand(esp), Immediate(2 * kPointerSize));
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
frame_->Drop(1);
diff --git a/deps/v8/src/ia32/debug-ia32.cc b/deps/v8/src/ia32/debug-ia32.cc
index ee9456564c..678cc93115 100644
--- a/deps/v8/src/ia32/debug-ia32.cc
+++ b/deps/v8/src/ia32/debug-ia32.cc
@@ -125,7 +125,7 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
#ifdef DEBUG
__ RecordComment("// Calling from debug break to runtime - come in - over");
#endif
- __ Set(eax, Immediate(0)); // no arguments
+ __ Set(eax, Immediate(0)); // No arguments.
__ mov(ebx, Immediate(ExternalReference::debug_break()));
CEntryStub ceb(1);
diff --git a/deps/v8/src/ia32/deoptimizer-ia32.cc b/deps/v8/src/ia32/deoptimizer-ia32.cc
index d95df3e7ea..3050c5674f 100644
--- a/deps/v8/src/ia32/deoptimizer-ia32.cc
+++ b/deps/v8/src/ia32/deoptimizer-ia32.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -27,6 +27,8 @@
#include "v8.h"
+#if defined(V8_TARGET_ARCH_IA32)
+
#include "codegen.h"
#include "deoptimizer.h"
#include "full-codegen.h"
@@ -56,8 +58,9 @@ void Deoptimizer::DeoptimizeFunction(JSFunction* function) {
SafepointTable table(function->code());
for (unsigned i = 0; i < table.length(); i++) {
unsigned pc_offset = table.GetPcOffset(i);
- int deoptimization_index = table.GetDeoptimizationIndex(i);
- int gap_code_size = table.GetGapCodeSize(i);
+ SafepointEntry safepoint_entry = table.GetEntry(i);
+ int deoptimization_index = safepoint_entry.deoptimization_index();
+ int gap_code_size = safepoint_entry.gap_code_size();
#ifdef DEBUG
// Destroy the code which is not supposed to run again.
unsigned instructions = pc_offset - last_pc_offset;
@@ -105,23 +108,25 @@ void Deoptimizer::DeoptimizeFunction(JSFunction* function) {
void Deoptimizer::PatchStackCheckCode(RelocInfo* rinfo,
Code* replacement_code) {
- // The stack check code matches the pattern (on ia32, for example):
+ // The stack check code matches the pattern:
//
// cmp esp, <limit>
// jae ok
// call <stack guard>
+ // test eax, <loop nesting depth>
// ok: ...
//
- // We will patch the code to:
+ // We will patch away the branch so the code is:
//
// cmp esp, <limit> ;; Not changed
// nop
// nop
// call <on-stack replacment>
+ // test eax, <loop nesting depth>
// ok:
Address call_target_address = rinfo->pc();
ASSERT(*(call_target_address - 3) == 0x73 && // jae
- *(call_target_address - 2) == 0x05 && // offset
+ *(call_target_address - 2) == 0x07 && // offset
*(call_target_address - 1) == 0xe8); // call
*(call_target_address - 3) = 0x90; // nop
*(call_target_address - 2) = 0x90; // nop
@@ -130,12 +135,14 @@ void Deoptimizer::PatchStackCheckCode(RelocInfo* rinfo,
void Deoptimizer::RevertStackCheckCode(RelocInfo* rinfo, Code* check_code) {
+ // Replace the nops from patching (Deoptimizer::PatchStackCheckCode) to
+ // restore the conditional branch.
Address call_target_address = rinfo->pc();
ASSERT(*(call_target_address - 3) == 0x90 && // nop
*(call_target_address - 2) == 0x90 && // nop
*(call_target_address - 1) == 0xe8); // call
*(call_target_address - 3) = 0x73; // jae
- *(call_target_address - 2) = 0x05; // offset
+ *(call_target_address - 2) = 0x07; // offset
rinfo->set_target_address(check_code->entry());
}
@@ -613,3 +620,5 @@ void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/deps/v8/src/ia32/disasm-ia32.cc b/deps/v8/src/ia32/disasm-ia32.cc
index dfbcbb76d5..4028a93421 100644
--- a/deps/v8/src/ia32/disasm-ia32.cc
+++ b/deps/v8/src/ia32/disasm-ia32.cc
@@ -1182,15 +1182,33 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
NameOfXMMRegister(rm),
static_cast<int>(imm8));
data += 2;
+ } else if (*data == 0xF3) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("psllq %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
} else if (*data == 0x73) {
data++;
int mod, regop, rm;
get_modrm(*data, &mod, &regop, &rm);
int8_t imm8 = static_cast<int8_t>(data[1]);
- AppendToBuffer("psllq %s,%d",
+ ASSERT(regop == esi || regop == edx);
+ AppendToBuffer("%s %s,%d",
+ (regop == esi) ? "psllq" : "psrlq",
NameOfXMMRegister(rm),
static_cast<int>(imm8));
data += 2;
+ } else if (*data == 0xD3) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("psrlq %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
} else if (*data == 0x7F) {
AppendToBuffer("movdqa ");
data++;
@@ -1228,6 +1246,14 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
NameOfXMMRegister(regop),
NameOfXMMRegister(rm));
data++;
+ } else if (*data == 0xEB) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("por %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
} else {
UnimplementedInstruction();
}
diff --git a/deps/v8/src/ia32/full-codegen-ia32.cc b/deps/v8/src/ia32/full-codegen-ia32.cc
index 13a11777ab..2622b5e5b4 100644
--- a/deps/v8/src/ia32/full-codegen-ia32.cc
+++ b/deps/v8/src/ia32/full-codegen-ia32.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -264,16 +264,24 @@ void FullCodeGenerator::EmitStackCheck(IterationStatement* stmt) {
__ j(above_equal, &ok, taken);
StackCheckStub stub;
__ CallStub(&stub);
- __ bind(&ok);
- PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
- PrepareForBailoutForId(stmt->OsrEntryId(), NO_REGISTERS);
+ // Record a mapping of this PC offset to the OSR id. This is used to find
+ // the AST id from the unoptimized code in order to use it as a key into
+ // the deoptimization input data found in the optimized code.
RecordStackCheck(stmt->OsrEntryId());
- // Loop stack checks can be patched to perform on-stack
- // replacement. In order to decide whether or not to perform OSR we
- // embed the loop depth in a test instruction after the call so we
- // can extract it from the OSR builtin.
+
+ // Loop stack checks can be patched to perform on-stack replacement. In
+ // order to decide whether or not to perform OSR we embed the loop depth
+ // in a test instruction after the call so we can extract it from the OSR
+ // builtin.
ASSERT(loop_depth() > 0);
__ test(eax, Immediate(Min(loop_depth(), Code::kMaxLoopNestingMarker)));
+
+ __ bind(&ok);
+ PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
+ // Record a mapping of the OSR id to this PC. This is used if the OSR
+ // entry becomes the target of a bailout. We don't expect it to be, but
+ // we want it to work if it is.
+ PrepareForBailoutForId(stmt->OsrEntryId(), NO_REGISTERS);
}
@@ -379,7 +387,7 @@ void FullCodeGenerator::EffectContext::Plug(Handle<Object> lit) const {
void FullCodeGenerator::AccumulatorValueContext::Plug(
Handle<Object> lit) const {
- __ mov(result_register(), lit);
+ __ Set(result_register(), Immediate(lit));
}
@@ -1497,7 +1505,9 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) {
if (expr->is_compound()) {
if (property->is_arguments_access()) {
VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
- __ push(EmitSlotSearch(obj_proxy->var()->AsSlot(), ecx));
+ MemOperand slot_operand =
+ EmitSlotSearch(obj_proxy->var()->AsSlot(), ecx);
+ __ push(slot_operand);
__ mov(eax, Immediate(property->key()->AsLiteral()->handle()));
} else {
VisitForStackValue(property->obj());
@@ -1508,7 +1518,9 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) {
} else {
if (property->is_arguments_access()) {
VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
- __ push(EmitSlotSearch(obj_proxy->var()->AsSlot(), ecx));
+ MemOperand slot_operand =
+ EmitSlotSearch(obj_proxy->var()->AsSlot(), ecx);
+ __ push(slot_operand);
__ push(Immediate(property->key()->AsLiteral()->handle()));
} else {
VisitForStackValue(property->obj());
@@ -3339,39 +3351,37 @@ void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) {
void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) {
- Label bailout;
- Label done;
+ Label bailout, done, one_char_separator, long_separator,
+ non_trivial_array, not_size_one_array, loop, loop_condition,
+ loop_1, loop_1_condition, loop_2, loop_2_entry, loop_3, loop_3_entry;
ASSERT(args->length() == 2);
// We will leave the separator on the stack until the end of the function.
VisitForStackValue(args->at(1));
// Load this to eax (= array)
VisitForAccumulatorValue(args->at(0));
-
// All aliases of the same register have disjoint lifetimes.
Register array = eax;
- Register result_pos = no_reg;
+ Register elements = no_reg; // Will be eax.
- Register index = edi;
+ Register index = edx;
- Register current_string_length = ecx; // Will be ecx when live.
+ Register string_length = ecx;
- Register current_string = edx;
+ Register string = esi;
Register scratch = ebx;
- Register scratch_2 = esi;
- Register new_padding_chars = scratch_2;
-
- Operand separator = Operand(esp, 4 * kPointerSize); // Already pushed.
- Operand elements = Operand(esp, 3 * kPointerSize);
- Operand result = Operand(esp, 2 * kPointerSize);
- Operand padding_chars = Operand(esp, 1 * kPointerSize);
- Operand array_length = Operand(esp, 0);
- __ sub(Operand(esp), Immediate(4 * kPointerSize));
+ Register array_length = edi;
+ Register result_pos = no_reg; // Will be edi.
-
- // Check that eax is a JSArray
+ // Separator operand is already pushed.
+ Operand separator_operand = Operand(esp, 2 * kPointerSize);
+ Operand result_operand = Operand(esp, 1 * kPointerSize);
+ Operand array_length_operand = Operand(esp, 0);
+ __ sub(Operand(esp), Immediate(2 * kPointerSize));
+ __ cld();
+ // Check that the array is a JSArray
__ test(array, Immediate(kSmiTagMask));
__ j(zero, &bailout);
__ CmpObjectType(array, JS_ARRAY_TYPE, scratch);
@@ -3382,140 +3392,226 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) {
1 << Map::kHasFastElements);
__ j(zero, &bailout);
- // If the array is empty, return the empty string.
- __ mov(scratch, FieldOperand(array, JSArray::kLengthOffset));
- __ sar(scratch, 1);
- Label non_trivial;
- __ j(not_zero, &non_trivial);
- __ mov(result, Factory::empty_string());
+ // If the array has length zero, return the empty string.
+ __ mov(array_length, FieldOperand(array, JSArray::kLengthOffset));
+ __ sar(array_length, 1);
+ __ j(not_zero, &non_trivial_array);
+ __ mov(result_operand, Factory::empty_string());
__ jmp(&done);
- __ bind(&non_trivial);
- __ mov(array_length, scratch);
-
- __ mov(scratch, FieldOperand(array, JSArray::kElementsOffset));
- __ mov(elements, scratch);
+ // Save the array length.
+ __ bind(&non_trivial_array);
+ __ mov(array_length_operand, array_length);
+ // Save the FixedArray containing array's elements.
// End of array's live range.
- result_pos = array;
+ elements = array;
+ __ mov(elements, FieldOperand(array, JSArray::kElementsOffset));
array = no_reg;
- // Check that the separator is a flat ascii string.
- __ mov(current_string, separator);
- __ test(current_string, Immediate(kSmiTagMask));
+ // Check that all array elements are sequential ASCII strings, and
+ // accumulate the sum of their lengths, as a smi-encoded value.
+ __ Set(index, Immediate(0));
+ __ Set(string_length, Immediate(0));
+ // Loop condition: while (index < length).
+ // Live loop registers: index, array_length, string,
+ // scratch, string_length, elements.
+ __ jmp(&loop_condition);
+ __ bind(&loop);
+ __ cmp(index, Operand(array_length));
+ __ j(greater_equal, &done);
+
+ __ mov(string, FieldOperand(elements, index,
+ times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ test(string, Immediate(kSmiTagMask));
__ j(zero, &bailout);
- __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset));
- __ mov_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
+ __ mov(scratch, FieldOperand(string, HeapObject::kMapOffset));
+ __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
__ and_(scratch, Immediate(
kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask));
__ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag);
__ j(not_equal, &bailout);
- // If the separator is the empty string, replace it with NULL.
- // The test for NULL is quicker than the empty string test, in a loop.
- __ cmp(FieldOperand(current_string, SeqAsciiString::kLengthOffset),
- Immediate(0));
- Label separator_checked;
- __ j(not_zero, &separator_checked);
- __ mov(separator, Immediate(0));
- __ bind(&separator_checked);
-
- // Check that elements[0] is a flat ascii string, and copy it in new space.
- __ mov(scratch, elements);
- __ mov(current_string, FieldOperand(scratch, FixedArray::kHeaderSize));
- __ test(current_string, Immediate(kSmiTagMask));
+ __ add(string_length,
+ FieldOperand(string, SeqAsciiString::kLengthOffset));
+ __ j(overflow, &bailout);
+ __ add(Operand(index), Immediate(1));
+ __ bind(&loop_condition);
+ __ cmp(index, Operand(array_length));
+ __ j(less, &loop);
+
+ // If array_length is 1, return elements[0], a string.
+ __ cmp(array_length, 1);
+ __ j(not_equal, &not_size_one_array);
+ __ mov(scratch, FieldOperand(elements, FixedArray::kHeaderSize));
+ __ mov(result_operand, scratch);
+ __ jmp(&done);
+
+ __ bind(&not_size_one_array);
+
+ // End of array_length live range.
+ result_pos = array_length;
+ array_length = no_reg;
+
+ // Live registers:
+ // string_length: Sum of string lengths, as a smi.
+ // elements: FixedArray of strings.
+
+ // Check that the separator is a flat ASCII string.
+ __ mov(string, separator_operand);
+ __ test(string, Immediate(kSmiTagMask));
__ j(zero, &bailout);
- __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset));
- __ mov_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
+ __ mov(scratch, FieldOperand(string, HeapObject::kMapOffset));
+ __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
__ and_(scratch, Immediate(
kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask));
__ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag);
__ j(not_equal, &bailout);
- // Allocate space to copy it. Round up the size to the alignment granularity.
- __ mov(current_string_length,
- FieldOperand(current_string, String::kLengthOffset));
- __ shr(current_string_length, 1);
-
+ // Add (separator length times array_length) - separator length
+ // to string_length.
+ __ mov(scratch, separator_operand);
+ __ mov(scratch, FieldOperand(scratch, SeqAsciiString::kLengthOffset));
+ __ sub(string_length, Operand(scratch)); // May be negative, temporarily.
+ __ imul(scratch, array_length_operand);
+ __ j(overflow, &bailout);
+ __ add(string_length, Operand(scratch));
+ __ j(overflow, &bailout);
+
+ __ shr(string_length, 1);
// Live registers and stack values:
- // current_string_length: length of elements[0].
-
- // New string result in new space = elements[0]
- __ AllocateAsciiString(result_pos, current_string_length, scratch_2,
- index, no_reg, &bailout);
- __ mov(result, result_pos);
-
- // Adjust current_string_length to include padding bytes at end of string.
- // Keep track of the number of padding bytes.
- __ mov(new_padding_chars, current_string_length);
- __ add(Operand(current_string_length), Immediate(kObjectAlignmentMask));
- __ and_(Operand(current_string_length), Immediate(~kObjectAlignmentMask));
- __ sub(new_padding_chars, Operand(current_string_length));
- __ neg(new_padding_chars);
- __ mov(padding_chars, new_padding_chars);
-
- Label copy_loop_1_done;
- Label copy_loop_1;
- __ test(current_string_length, Operand(current_string_length));
- __ j(zero, &copy_loop_1_done);
- __ bind(&copy_loop_1);
- __ sub(Operand(current_string_length), Immediate(kPointerSize));
- __ mov(scratch, FieldOperand(current_string, current_string_length,
- times_1, SeqAsciiString::kHeaderSize));
- __ mov(FieldOperand(result_pos, current_string_length,
- times_1, SeqAsciiString::kHeaderSize),
- scratch);
- __ j(not_zero, &copy_loop_1);
- __ bind(&copy_loop_1_done);
-
- __ mov(index, Immediate(1));
+ // string_length
+ // elements
+ __ AllocateAsciiString(result_pos, string_length, scratch,
+ index, string, &bailout);
+ __ mov(result_operand, result_pos);
+ __ lea(result_pos, FieldOperand(result_pos, SeqAsciiString::kHeaderSize));
+
+
+ __ mov(string, separator_operand);
+ __ cmp(FieldOperand(string, SeqAsciiString::kLengthOffset),
+ Immediate(Smi::FromInt(1)));
+ __ j(equal, &one_char_separator);
+ __ j(greater, &long_separator);
+
+
+ // Empty separator case
+ __ mov(index, Immediate(0));
+ __ jmp(&loop_1_condition);
// Loop condition: while (index < length).
- Label loop;
- __ bind(&loop);
- __ cmp(index, array_length);
- __ j(greater_equal, &done);
+ __ bind(&loop_1);
+ // Each iteration of the loop concatenates one string to the result.
+ // Live values in registers:
+ // index: which element of the elements array we are adding to the result.
+ // result_pos: the position to which we are currently copying characters.
+ // elements: the FixedArray of strings we are joining.
+
+ // Get string = array[index].
+ __ mov(string, FieldOperand(elements, index,
+ times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ mov(string_length,
+ FieldOperand(string, String::kLengthOffset));
+ __ shr(string_length, 1);
+ __ lea(string,
+ FieldOperand(string, SeqAsciiString::kHeaderSize));
+ __ CopyBytes(string, result_pos, string_length, scratch);
+ __ add(Operand(index), Immediate(1));
+ __ bind(&loop_1_condition);
+ __ cmp(index, array_length_operand);
+ __ j(less, &loop_1); // End while (index < length).
+ __ jmp(&done);
+
- // If the separator is the empty string, signalled by NULL, skip it.
- Label separator_done;
- __ mov(current_string, separator);
- __ test(current_string, Operand(current_string));
- __ j(zero, &separator_done);
-
- // Append separator to result. It is known to be a flat ascii string.
- __ AppendStringToTopOfNewSpace(current_string, current_string_length,
- result_pos, scratch, scratch_2, result,
- padding_chars, &bailout);
- __ bind(&separator_done);
-
- // Add next element of array to the end of the result.
- // Get current_string = array[index].
- __ mov(scratch, elements);
- __ mov(current_string, FieldOperand(scratch, index,
- times_pointer_size,
- FixedArray::kHeaderSize));
- // If current != flat ascii string drop result, return undefined.
- __ test(current_string, Immediate(kSmiTagMask));
- __ j(zero, &bailout);
- __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset));
- __ mov_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
- __ and_(scratch, Immediate(
- kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask));
- __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag);
- __ j(not_equal, &bailout);
- // Append current to the result.
- __ AppendStringToTopOfNewSpace(current_string, current_string_length,
- result_pos, scratch, scratch_2, result,
- padding_chars, &bailout);
+ // One-character separator case
+ __ bind(&one_char_separator);
+ // Replace separator with its ascii character value.
+ __ mov_b(scratch, FieldOperand(string, SeqAsciiString::kHeaderSize));
+ __ mov_b(separator_operand, scratch);
+
+ __ Set(index, Immediate(0));
+ // Jump into the loop after the code that copies the separator, so the first
+ // element is not preceded by a separator
+ __ jmp(&loop_2_entry);
+ // Loop condition: while (index < length).
+ __ bind(&loop_2);
+ // Each iteration of the loop concatenates one string to the result.
+ // Live values in registers:
+ // index: which element of the elements array we are adding to the result.
+ // result_pos: the position to which we are currently copying characters.
+
+ // Copy the separator character to the result.
+ __ mov_b(scratch, separator_operand);
+ __ mov_b(Operand(result_pos, 0), scratch);
+ __ inc(result_pos);
+
+ __ bind(&loop_2_entry);
+ // Get string = array[index].
+ __ mov(string, FieldOperand(elements, index,
+ times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ mov(string_length,
+ FieldOperand(string, String::kLengthOffset));
+ __ shr(string_length, 1);
+ __ lea(string,
+ FieldOperand(string, SeqAsciiString::kHeaderSize));
+ __ CopyBytes(string, result_pos, string_length, scratch);
+ __ add(Operand(index), Immediate(1));
+
+ __ cmp(index, array_length_operand);
+ __ j(less, &loop_2); // End while (index < length).
+ __ jmp(&done);
+
+
+ // Long separator case (separator is more than one character).
+ __ bind(&long_separator);
+
+ __ Set(index, Immediate(0));
+ // Jump into the loop after the code that copies the separator, so the first
+ // element is not preceded by a separator
+ __ jmp(&loop_3_entry);
+ // Loop condition: while (index < length).
+ __ bind(&loop_3);
+ // Each iteration of the loop concatenates one string to the result.
+ // Live values in registers:
+ // index: which element of the elements array we are adding to the result.
+ // result_pos: the position to which we are currently copying characters.
+
+ // Copy the separator to the result.
+ __ mov(string, separator_operand);
+ __ mov(string_length,
+ FieldOperand(string, String::kLengthOffset));
+ __ shr(string_length, 1);
+ __ lea(string,
+ FieldOperand(string, SeqAsciiString::kHeaderSize));
+ __ CopyBytes(string, result_pos, string_length, scratch);
+
+ __ bind(&loop_3_entry);
+ // Get string = array[index].
+ __ mov(string, FieldOperand(elements, index,
+ times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ mov(string_length,
+ FieldOperand(string, String::kLengthOffset));
+ __ shr(string_length, 1);
+ __ lea(string,
+ FieldOperand(string, SeqAsciiString::kHeaderSize));
+ __ CopyBytes(string, result_pos, string_length, scratch);
__ add(Operand(index), Immediate(1));
- __ jmp(&loop); // End while (index < length).
+
+ __ cmp(index, array_length_operand);
+ __ j(less, &loop_3); // End while (index < length).
+ __ jmp(&done);
+
__ bind(&bailout);
- __ mov(result, Factory::undefined_value());
+ __ mov(result_operand, Factory::undefined_value());
__ bind(&done);
- __ mov(eax, result);
+ __ mov(eax, result_operand);
// Drop temp values from the stack, and restore context register.
- __ add(Operand(esp), Immediate(5 * kPointerSize));
+ __ add(Operand(esp), Immediate(3 * kPointerSize));
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
context()->Plug(eax);
@@ -3739,7 +3835,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
} else {
if (prop->is_arguments_access()) {
VariableProxy* obj_proxy = prop->obj()->AsVariableProxy();
- __ push(EmitSlotSearch(obj_proxy->var()->AsSlot(), ecx));
+ MemOperand slot_operand =
+ EmitSlotSearch(obj_proxy->var()->AsSlot(), ecx);
+ __ push(slot_operand);
__ mov(eax, Immediate(prop->key()->AsLiteral()->handle()));
} else {
VisitForStackValue(prop->obj());
@@ -4042,7 +4140,6 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
case Token::INSTANCEOF: {
VisitForStackValue(expr->right());
- __ IncrementCounter(&Counters::instance_of_full, 1);
InstanceofStub stub(InstanceofStub::kNoFlags);
__ CallStub(&stub);
PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
diff --git a/deps/v8/src/ia32/ic-ia32.cc b/deps/v8/src/ia32/ic-ia32.cc
index 9c9304d5a4..90bfd4b664 100644
--- a/deps/v8/src/ia32/ic-ia32.cc
+++ b/deps/v8/src/ia32/ic-ia32.cc
@@ -1199,7 +1199,7 @@ void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm,
break;
case kExternalShortArray:
case kExternalUnsignedShortArray:
- __ xor_(ecx, Operand(ecx));
+ __ Set(ecx, Immediate(0));
__ mov_w(Operand(edi, ebx, times_2, 0), ecx);
break;
case kExternalIntArray:
@@ -1219,9 +1219,6 @@ void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm,
}
-// Defined in ic.cc.
-Object* CallIC_Miss(Arguments args);
-
// The generated code does not accept smi keys.
// The generated code falls through if both probes miss.
static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
@@ -1567,9 +1564,6 @@ void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) {
}
-// Defined in ic.cc.
-Object* LoadIC_Miss(Arguments args);
-
void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : receiver
@@ -1795,10 +1789,6 @@ bool KeyedStoreIC::PatchInlinedStore(Address address, Object* map) {
}
-// Defined in ic.cc.
-Object* KeyedLoadIC_Miss(Arguments args);
-
-
void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : key
@@ -1982,9 +1972,6 @@ void StoreIC::GenerateGlobalProxy(MacroAssembler* masm) {
}
-// Defined in ic.cc.
-Object* KeyedStoreIC_Miss(Arguments args);
-
void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : value
diff --git a/deps/v8/src/ia32/lithium-codegen-ia32.cc b/deps/v8/src/ia32/lithium-codegen-ia32.cc
index d64f528e71..24ee1fefdb 100644
--- a/deps/v8/src/ia32/lithium-codegen-ia32.cc
+++ b/deps/v8/src/ia32/lithium-codegen-ia32.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -25,6 +25,10 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include "v8.h"
+
+#if defined(V8_TARGET_ARCH_IA32)
+
#include "ia32/lithium-codegen-ia32.h"
#include "code-stubs.h"
#include "stub-cache.h"
@@ -54,6 +58,157 @@ class SafepointGenerator : public PostCallGenerator {
};
+class LGapNode: public ZoneObject {
+ public:
+ explicit LGapNode(LOperand* operand)
+ : operand_(operand), resolved_(false), visited_id_(-1) { }
+
+ LOperand* operand() const { return operand_; }
+ bool IsResolved() const { return !IsAssigned() || resolved_; }
+ void MarkResolved() {
+ ASSERT(!IsResolved());
+ resolved_ = true;
+ }
+ int visited_id() const { return visited_id_; }
+ void set_visited_id(int id) {
+ ASSERT(id > visited_id_);
+ visited_id_ = id;
+ }
+
+ bool IsAssigned() const { return assigned_from_.is_set(); }
+ LGapNode* assigned_from() const { return assigned_from_.get(); }
+ void set_assigned_from(LGapNode* n) { assigned_from_.set(n); }
+
+ private:
+ LOperand* operand_;
+ SetOncePointer<LGapNode> assigned_from_;
+ bool resolved_;
+ int visited_id_;
+};
+
+
+LGapResolver::LGapResolver()
+ : nodes_(32),
+ identified_cycles_(4),
+ result_(16),
+ next_visited_id_(0) {
+}
+
+
+const ZoneList<LMoveOperands>* LGapResolver::Resolve(
+ const ZoneList<LMoveOperands>* moves,
+ LOperand* marker_operand) {
+ nodes_.Rewind(0);
+ identified_cycles_.Rewind(0);
+ result_.Rewind(0);
+ next_visited_id_ = 0;
+
+ for (int i = 0; i < moves->length(); ++i) {
+ LMoveOperands move = moves->at(i);
+ if (!move.IsRedundant()) RegisterMove(move);
+ }
+
+ for (int i = 0; i < identified_cycles_.length(); ++i) {
+ ResolveCycle(identified_cycles_[i], marker_operand);
+ }
+
+ int unresolved_nodes;
+ do {
+ unresolved_nodes = 0;
+ for (int j = 0; j < nodes_.length(); j++) {
+ LGapNode* node = nodes_[j];
+ if (!node->IsResolved() && node->assigned_from()->IsResolved()) {
+ AddResultMove(node->assigned_from(), node);
+ node->MarkResolved();
+ }
+ if (!node->IsResolved()) ++unresolved_nodes;
+ }
+ } while (unresolved_nodes > 0);
+ return &result_;
+}
+
+
+void LGapResolver::AddResultMove(LGapNode* from, LGapNode* to) {
+ AddResultMove(from->operand(), to->operand());
+}
+
+
+void LGapResolver::AddResultMove(LOperand* from, LOperand* to) {
+ result_.Add(LMoveOperands(from, to));
+}
+
+
+void LGapResolver::ResolveCycle(LGapNode* start, LOperand* marker_operand) {
+ ZoneList<LOperand*> cycle_operands(8);
+ cycle_operands.Add(marker_operand);
+ LGapNode* cur = start;
+ do {
+ cur->MarkResolved();
+ cycle_operands.Add(cur->operand());
+ cur = cur->assigned_from();
+ } while (cur != start);
+ cycle_operands.Add(marker_operand);
+
+ for (int i = cycle_operands.length() - 1; i > 0; --i) {
+ LOperand* from = cycle_operands[i];
+ LOperand* to = cycle_operands[i - 1];
+ AddResultMove(from, to);
+ }
+}
+
+
+bool LGapResolver::CanReach(LGapNode* a, LGapNode* b, int visited_id) {
+ ASSERT(a != b);
+ LGapNode* cur = a;
+ while (cur != b && cur->visited_id() != visited_id && cur->IsAssigned()) {
+ cur->set_visited_id(visited_id);
+ cur = cur->assigned_from();
+ }
+
+ return cur == b;
+}
+
+
+bool LGapResolver::CanReach(LGapNode* a, LGapNode* b) {
+ ASSERT(a != b);
+ return CanReach(a, b, next_visited_id_++);
+}
+
+
+void LGapResolver::RegisterMove(LMoveOperands move) {
+ if (move.from()->IsConstantOperand()) {
+ // Constant moves should be last in the machine code. Therefore add them
+ // first to the result set.
+ AddResultMove(move.from(), move.to());
+ } else {
+ LGapNode* from = LookupNode(move.from());
+ LGapNode* to = LookupNode(move.to());
+ if (to->IsAssigned() && to->assigned_from() == from) {
+ move.Eliminate();
+ return;
+ }
+ ASSERT(!to->IsAssigned());
+ if (CanReach(from, to)) {
+ // This introduces a cycle. Save.
+ identified_cycles_.Add(from);
+ }
+ to->set_assigned_from(from);
+ }
+}
+
+
+LGapNode* LGapResolver::LookupNode(LOperand* operand) {
+ for (int i = 0; i < nodes_.length(); ++i) {
+ if (nodes_[i]->operand()->Equals(operand)) return nodes_[i];
+ }
+
+ // No node found => create a new one.
+ LGapNode* result = new LGapNode(operand);
+ nodes_.Add(result);
+ return result;
+}
+
+
#define __ masm()->
bool LCodeGen::GenerateCode() {
@@ -135,6 +290,17 @@ bool LCodeGen::GeneratePrologue() {
__ j(not_zero, &loop);
} else {
__ sub(Operand(esp), Immediate(slots * kPointerSize));
+#ifdef _MSC_VER
+ // On windows, you may not access the stack more than one page below
+ // the most recently mapped page. To make the allocated area randomly
+ // accessible, we write to each page in turn (the value is irrelevant).
+ const int kPageSize = 4 * KB;
+ for (int offset = slots * kPointerSize - kPageSize;
+ offset > 0;
+ offset -= kPageSize) {
+ __ mov(Operand(esp, offset), eax);
+ }
+#endif
}
}
@@ -261,6 +427,45 @@ Operand LCodeGen::ToOperand(LOperand* op) const {
}
+void LCodeGen::WriteTranslation(LEnvironment* environment,
+ Translation* translation) {
+ if (environment == NULL) return;
+
+ // The translation includes one command per value in the environment.
+ int translation_size = environment->values()->length();
+ // The output frame height does not include the parameters.
+ int height = translation_size - environment->parameter_count();
+
+ WriteTranslation(environment->outer(), translation);
+ int closure_id = DefineDeoptimizationLiteral(environment->closure());
+ translation->BeginFrame(environment->ast_id(), closure_id, height);
+ for (int i = 0; i < translation_size; ++i) {
+ LOperand* value = environment->values()->at(i);
+ // spilled_registers_ and spilled_double_registers_ are either
+ // both NULL or both set.
+ if (environment->spilled_registers() != NULL && value != NULL) {
+ if (value->IsRegister() &&
+ environment->spilled_registers()[value->index()] != NULL) {
+ translation->MarkDuplicate();
+ AddToTranslation(translation,
+ environment->spilled_registers()[value->index()],
+ environment->HasTaggedValueAt(i));
+ } else if (
+ value->IsDoubleRegister() &&
+ environment->spilled_double_registers()[value->index()] != NULL) {
+ translation->MarkDuplicate();
+ AddToTranslation(
+ translation,
+ environment->spilled_double_registers()[value->index()],
+ false);
+ }
+ }
+
+ AddToTranslation(translation, value, environment->HasTaggedValueAt(i));
+ }
+}
+
+
void LCodeGen::AddToTranslation(Translation* translation,
LOperand* op,
bool is_tagged) {
@@ -385,7 +590,7 @@ void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment) {
++frame_count;
}
Translation translation(&translations_, frame_count);
- environment->WriteTranslation(this, &translation);
+ WriteTranslation(environment, &translation);
int deoptimization_index = deoptimizations_.length();
environment->Register(deoptimization_index, translation.index());
deoptimizations_.Add(environment);
@@ -564,8 +769,8 @@ void LCodeGen::DoParallelMove(LParallelMove* move) {
Register cpu_scratch = esi;
bool destroys_cpu_scratch = false;
- LGapResolver resolver(move->move_operands(), &marker_operand);
- const ZoneList<LMoveOperands>* moves = resolver.ResolveInReverseOrder();
+ const ZoneList<LMoveOperands>* moves =
+ resolver_.Resolve(move->move_operands(), &marker_operand);
for (int i = moves->length() - 1; i >= 0; --i) {
LMoveOperands move = moves->at(i);
LOperand* from = move.from();
@@ -940,7 +1145,7 @@ void LCodeGen::DoSubI(LSubI* instr) {
void LCodeGen::DoConstantI(LConstantI* instr) {
ASSERT(instr->result()->IsRegister());
- __ mov(ToRegister(instr->result()), instr->value());
+ __ Set(ToRegister(instr->result()), Immediate(instr->value()));
}
@@ -973,27 +1178,21 @@ void LCodeGen::DoConstantD(LConstantD* instr) {
void LCodeGen::DoConstantT(LConstantT* instr) {
ASSERT(instr->result()->IsRegister());
- __ mov(ToRegister(instr->result()), Immediate(instr->value()));
+ __ Set(ToRegister(instr->result()), Immediate(instr->value()));
}
-void LCodeGen::DoArrayLength(LArrayLength* instr) {
+void LCodeGen::DoJSArrayLength(LJSArrayLength* instr) {
Register result = ToRegister(instr->result());
+ Register array = ToRegister(instr->input());
+ __ mov(result, FieldOperand(array, JSArray::kLengthOffset));
+}
- if (instr->hydrogen()->value()->IsLoadElements()) {
- // We load the length directly from the elements array.
- Register elements = ToRegister(instr->input());
- __ mov(result, FieldOperand(elements, FixedArray::kLengthOffset));
- } else {
- // Check that the receiver really is an array.
- Register array = ToRegister(instr->input());
- Register temporary = ToRegister(instr->temporary());
- __ CmpObjectType(array, JS_ARRAY_TYPE, temporary);
- DeoptimizeIf(not_equal, instr->environment());
- // Load length directly from the array.
- __ mov(result, FieldOperand(array, JSArray::kLengthOffset));
- }
+void LCodeGen::DoFixedArrayLength(LFixedArrayLength* instr) {
+ Register result = ToRegister(instr->result());
+ Register array = ToRegister(instr->input());
+ __ mov(result, FieldOperand(array, FixedArray::kLengthOffset));
}
@@ -1700,7 +1899,7 @@ void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) {
void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
- // Object and function are in fixed registers eax and edx.
+ // Object and function are in fixed registers defined by the stub.
InstanceofStub stub(InstanceofStub::kArgsInRegisters);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
@@ -1726,6 +1925,107 @@ void LCodeGen::DoInstanceOfAndBranch(LInstanceOfAndBranch* instr) {
}
+void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) {
+ class DeferredInstanceOfKnownGlobal: public LDeferredCode {
+ public:
+ DeferredInstanceOfKnownGlobal(LCodeGen* codegen,
+ LInstanceOfKnownGlobal* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() {
+ codegen()->DoDeferredLInstanceOfKnownGlobal(instr_, &map_check_);
+ }
+
+ Label* map_check() { return &map_check_; }
+
+ private:
+ LInstanceOfKnownGlobal* instr_;
+ Label map_check_;
+ };
+
+ DeferredInstanceOfKnownGlobal* deferred;
+ deferred = new DeferredInstanceOfKnownGlobal(this, instr);
+
+ Label done, false_result;
+ Register object = ToRegister(instr->input());
+ Register temp = ToRegister(instr->temp());
+
+ // A Smi is not instance of anything.
+ __ test(object, Immediate(kSmiTagMask));
+ __ j(zero, &false_result, not_taken);
+
+ // This is the inlined call site instanceof cache. The two occourences of the
+ // hole value will be patched to the last map/result pair generated by the
+ // instanceof stub.
+ NearLabel cache_miss;
+ Register map = ToRegister(instr->temp());
+ __ mov(map, FieldOperand(object, HeapObject::kMapOffset));
+ __ bind(deferred->map_check()); // Label for calculating code patching.
+ __ cmp(map, Factory::the_hole_value()); // Patched to cached map.
+ __ j(not_equal, &cache_miss, not_taken);
+ __ mov(eax, Factory::the_hole_value()); // Patched to either true or false.
+ __ jmp(&done);
+
+ // The inlined call site cache did not match. Check null and string before
+ // calling the deferred code.
+ __ bind(&cache_miss);
+ // Null is not instance of anything.
+ __ cmp(object, Factory::null_value());
+ __ j(equal, &false_result);
+
+ // String values are not instances of anything.
+ Condition is_string = masm_->IsObjectStringType(object, temp, temp);
+ __ j(is_string, &false_result);
+
+ // Go to the deferred code.
+ __ jmp(deferred->entry());
+
+ __ bind(&false_result);
+ __ mov(ToRegister(instr->result()), Factory::false_value());
+
+ // Here result has either true or false. Deferred code also produces true or
+ // false object.
+ __ bind(deferred->exit());
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
+ Label* map_check) {
+ __ PushSafepointRegisters();
+
+ InstanceofStub::Flags flags = InstanceofStub::kNoFlags;
+ flags = static_cast<InstanceofStub::Flags>(
+ flags | InstanceofStub::kArgsInRegisters);
+ flags = static_cast<InstanceofStub::Flags>(
+ flags | InstanceofStub::kCallSiteInlineCheck);
+ flags = static_cast<InstanceofStub::Flags>(
+ flags | InstanceofStub::kReturnTrueFalseObject);
+ InstanceofStub stub(flags);
+
+ // Get the temp register reserved by the instruction. This needs to be edi as
+ // its slot of the pushing of safepoint registers is used to communicate the
+ // offset to the location of the map check.
+ Register temp = ToRegister(instr->temp());
+ ASSERT(temp.is(edi));
+ __ mov(InstanceofStub::right(), Immediate(instr->function()));
+ static const int kAdditionalDelta = 13;
+ int delta = masm_->SizeOfCodeGeneratedSince(map_check) + kAdditionalDelta;
+ Label before_push_delta;
+ __ bind(&before_push_delta);
+ __ mov(temp, Immediate(delta));
+ __ mov(Operand(esp, EspIndexForPushAll(temp) * kPointerSize), temp);
+ __ call(stub.GetCode(), RelocInfo::CODE_TARGET);
+ ASSERT_EQ(kAdditionalDelta,
+ masm_->SizeOfCodeGeneratedSince(&before_push_delta));
+ RecordSafepointWithRegisters(
+ instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
+ // Put the result value into the eax slot and restore all registers.
+ __ mov(Operand(esp, EspIndexForPushAll(eax) * kPointerSize), eax);
+
+ __ PopSafepointRegisters();
+}
+
+
static Condition ComputeCompareCondition(Token::Value op) {
switch (op) {
case Token::EQ_STRICT:
@@ -1815,6 +2115,14 @@ void LCodeGen::DoStoreGlobal(LStoreGlobal* instr) {
}
+void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
+ // TODO(antonm): load a context with a separate instruction.
+ Register result = ToRegister(instr->result());
+ __ LoadContext(result, instr->context_chain_length());
+ __ mov(result, ContextOperand(result, instr->slot_index()));
+}
+
+
void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
Register object = ToRegister(instr->input());
Register result = ToRegister(instr->result());
@@ -1837,6 +2145,48 @@ void LCodeGen::DoLoadNamedGeneric(LLoadNamedGeneric* instr) {
}
+void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
+ Register function = ToRegister(instr->function());
+ Register temp = ToRegister(instr->temporary());
+ Register result = ToRegister(instr->result());
+
+ // Check that the function really is a function.
+ __ CmpObjectType(function, JS_FUNCTION_TYPE, result);
+ DeoptimizeIf(not_equal, instr->environment());
+
+ // Check whether the function has an instance prototype.
+ NearLabel non_instance;
+ __ test_b(FieldOperand(result, Map::kBitFieldOffset),
+ 1 << Map::kHasNonInstancePrototype);
+ __ j(not_zero, &non_instance);
+
+ // Get the prototype or initial map from the function.
+ __ mov(result,
+ FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
+
+ // Check that the function has a prototype or an initial map.
+ __ cmp(Operand(result), Immediate(Factory::the_hole_value()));
+ DeoptimizeIf(equal, instr->environment());
+
+ // If the function does not have an initial map, we're done.
+ NearLabel done;
+ __ CmpObjectType(result, MAP_TYPE, temp);
+ __ j(not_equal, &done);
+
+ // Get the prototype from the initial map.
+ __ mov(result, FieldOperand(result, Map::kPrototypeOffset));
+ __ jmp(&done);
+
+ // Non-instance prototype: Fetch prototype from constructor field
+ // in the function's map.
+ __ bind(&non_instance);
+ __ mov(result, FieldOperand(result, Map::kConstructorOffset));
+
+ // All done.
+ __ bind(&done);
+}
+
+
void LCodeGen::DoLoadElements(LLoadElements* instr) {
ASSERT(instr->result()->Equals(instr->input()));
Register reg = ToRegister(instr->input());
@@ -1863,6 +2213,8 @@ void LCodeGen::DoAccessArgumentsAt(LAccessArgumentsAt* instr) {
__ sub(length, index);
DeoptimizeIf(below_equal, instr->environment());
+ // There are two words between the frame pointer and the last argument.
+ // Subtracting from length accounts for one of them add one more.
__ mov(result, Operand(arguments, length, times_4, kPointerSize));
}
@@ -1870,32 +2222,15 @@ void LCodeGen::DoAccessArgumentsAt(LAccessArgumentsAt* instr) {
void LCodeGen::DoLoadKeyedFastElement(LLoadKeyedFastElement* instr) {
Register elements = ToRegister(instr->elements());
Register key = ToRegister(instr->key());
- Register result;
- if (instr->load_result() != NULL) {
- result = ToRegister(instr->load_result());
- } else {
- result = ToRegister(instr->result());
- ASSERT(result.is(elements));
- }
+ Register result = ToRegister(instr->result());
+ ASSERT(result.is(elements));
// Load the result.
__ mov(result, FieldOperand(elements, key, times_4, FixedArray::kHeaderSize));
- Representation r = instr->hydrogen()->representation();
- if (r.IsInteger32()) {
- // Untag and check for smi.
- __ SmiUntag(result);
- DeoptimizeIf(carry, instr->environment());
- } else if (r.IsDouble()) {
- EmitNumberUntagD(result,
- ToDoubleRegister(instr->result()),
- instr->environment());
- } else {
- // Check for the hole value.
- ASSERT(r.IsTagged());
- __ cmp(result, Factory::the_hole_value());
- DeoptimizeIf(equal, instr->environment());
- }
+ // Check for the hole value.
+ __ cmp(result, Factory::the_hole_value());
+ DeoptimizeIf(equal, instr->environment());
}
@@ -1912,7 +2247,7 @@ void LCodeGen::DoArgumentsElements(LArgumentsElements* instr) {
Register result = ToRegister(instr->result());
// Check for arguments adapter frame.
- Label done, adapted;
+ NearLabel done, adapted;
__ mov(result, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
__ mov(result, Operand(result, StandardFrameConstants::kContextOffset));
__ cmp(Operand(result),
@@ -1927,7 +2262,8 @@ void LCodeGen::DoArgumentsElements(LArgumentsElements* instr) {
__ bind(&adapted);
__ mov(result, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
- // Done. Pointer to topmost argument is in result.
+ // Result is the frame pointer for the frame if not adapted and for the real
+ // frame below the adaptor frame if adapted.
__ bind(&done);
}
@@ -1936,9 +2272,9 @@ void LCodeGen::DoArgumentsLength(LArgumentsLength* instr) {
Operand elem = ToOperand(instr->input());
Register result = ToRegister(instr->result());
- Label done;
+ NearLabel done;
- // No arguments adaptor frame. Number of arguments is fixed.
+ // If no arguments adaptor frame the number of arguments is fixed.
__ cmp(ebp, elem);
__ mov(result, Immediate(scope()->num_parameters()));
__ j(equal, &done);
@@ -1949,7 +2285,7 @@ void LCodeGen::DoArgumentsLength(LArgumentsLength* instr) {
ArgumentsAdaptorFrameConstants::kLengthOffset));
__ SmiUntag(result);
- // Done. Argument length is in result register.
+ // Argument length is in result register.
__ bind(&done);
}
@@ -2498,7 +2834,6 @@ void LCodeGen::DoStoreKeyedFastElement(LStoreKeyedFastElement* instr) {
value);
}
- // Update the write barrier unless we're certain that we're storing a smi.
if (instr->hydrogen()->NeedsWriteBarrier()) {
// Compute address of modified element and store it into key register.
__ lea(key, FieldOperand(elements, key, times_4, FixedArray::kHeaderSize));
@@ -2849,9 +3184,60 @@ void LCodeGen::DoDoubleToI(LDoubleToI* instr) {
__ add(Operand(esp), Immediate(kDoubleSize));
__ bind(&done);
} else {
- // This will bail out if the input was not in the int32 range (or,
- // unfortunately, if the input was 0x80000000).
- DeoptimizeIf(equal, instr->environment());
+ NearLabel done;
+ Register temp_reg = ToRegister(instr->temporary());
+ XMMRegister xmm_scratch = xmm0;
+
+ // If cvttsd2si succeeded, we're done. Otherwise, we attempt
+ // manual conversion.
+ __ j(not_equal, &done);
+
+ // Get high 32 bits of the input in result_reg and temp_reg.
+ __ pshufd(xmm_scratch, input_reg, 1);
+ __ movd(Operand(temp_reg), xmm_scratch);
+ __ mov(result_reg, temp_reg);
+
+ // Prepare negation mask in temp_reg.
+ __ sar(temp_reg, kBitsPerInt - 1);
+
+ // Extract the exponent from result_reg and subtract adjusted
+ // bias from it. The adjustment is selected in a way such that
+ // when the difference is zero, the answer is in the low 32 bits
+ // of the input, otherwise a shift has to be performed.
+ __ shr(result_reg, HeapNumber::kExponentShift);
+ __ and_(result_reg,
+ HeapNumber::kExponentMask >> HeapNumber::kExponentShift);
+ __ sub(Operand(result_reg),
+ Immediate(HeapNumber::kExponentBias +
+ HeapNumber::kExponentBits +
+ HeapNumber::kMantissaBits));
+ // Don't handle big (> kMantissaBits + kExponentBits == 63) or
+ // special exponents.
+ DeoptimizeIf(greater, instr->environment());
+
+ // Zero out the sign and the exponent in the input (by shifting
+ // it to the left) and restore the implicit mantissa bit,
+ // i.e. convert the input to unsigned int64 shifted left by
+ // kExponentBits.
+ ExternalReference minus_zero = ExternalReference::address_of_minus_zero();
+ // Minus zero has the most significant bit set and the other
+ // bits cleared.
+ __ movdbl(xmm_scratch, Operand::StaticVariable(minus_zero));
+ __ psllq(input_reg, HeapNumber::kExponentBits);
+ __ por(input_reg, xmm_scratch);
+
+ // Get the amount to shift the input right in xmm_scratch.
+ __ neg(result_reg);
+ __ movd(xmm_scratch, Operand(result_reg));
+
+ // Shift the input right and extract low 32 bits.
+ __ psrlq(input_reg, xmm_scratch);
+ __ movd(Operand(result_reg), input_reg);
+
+ // Use the prepared mask in temp_reg to negate the result if necessary.
+ __ xor_(result_reg, Operand(temp_reg));
+ __ sub(result_reg, Operand(temp_reg));
+ __ bind(&done);
}
} else {
NearLabel done;
@@ -2891,9 +3277,6 @@ void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
InstanceType first = instr->hydrogen()->first();
InstanceType last = instr->hydrogen()->last();
- __ test(input, Immediate(kSmiTagMask));
- DeoptimizeIf(zero, instr->environment());
-
__ mov(temp, FieldOperand(input, HeapObject::kMapOffset));
__ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset),
static_cast<int8_t>(first));
@@ -2931,13 +3314,13 @@ void LCodeGen::DoCheckMap(LCheckMap* instr) {
}
-void LCodeGen::LoadPrototype(Register result, Handle<JSObject> prototype) {
- if (Heap::InNewSpace(*prototype)) {
+void LCodeGen::LoadHeapObject(Register result, Handle<HeapObject> object) {
+ if (Heap::InNewSpace(*object)) {
Handle<JSGlobalPropertyCell> cell =
- Factory::NewJSGlobalPropertyCell(prototype);
+ Factory::NewJSGlobalPropertyCell(object);
__ mov(result, Operand::Cell(cell));
} else {
- __ mov(result, prototype);
+ __ mov(result, object);
}
}
@@ -2946,11 +3329,10 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) {
Register reg = ToRegister(instr->temp());
Handle<JSObject> holder = instr->holder();
- Handle<Map> receiver_map = instr->receiver_map();
- Handle<JSObject> current_prototype(JSObject::cast(receiver_map->prototype()));
+ Handle<JSObject> current_prototype = instr->prototype();
// Load prototype object.
- LoadPrototype(reg, current_prototype);
+ LoadHeapObject(reg, current_prototype);
// Check prototype maps up to the holder.
while (!current_prototype.is_identical_to(holder)) {
@@ -2960,7 +3342,7 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) {
current_prototype =
Handle<JSObject>(JSObject::cast(current_prototype->GetPrototype()));
// Load next prototype object.
- LoadPrototype(reg, current_prototype);
+ LoadHeapObject(reg, current_prototype);
}
// Check the holder map.
@@ -3006,7 +3388,7 @@ void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) {
__ push(Immediate(instr->hydrogen()->constant_properties()));
__ push(Immediate(Smi::FromInt(instr->hydrogen()->fast_elements() ? 1 : 0)));
- // Pick the right runtime function or stub to call.
+ // Pick the right runtime function to call.
if (instr->hydrogen()->depth() > 1) {
CallRuntime(Runtime::kCreateObjectLiteral, 4, instr);
} else {
@@ -3274,3 +3656,5 @@ void LCodeGen::DoOsrEntry(LOsrEntry* instr) {
#undef __
} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/deps/v8/src/ia32/lithium-codegen-ia32.h b/deps/v8/src/ia32/lithium-codegen-ia32.h
index 6d8173a1cf..ef8fb5c493 100644
--- a/deps/v8/src/ia32/lithium-codegen-ia32.h
+++ b/deps/v8/src/ia32/lithium-codegen-ia32.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -40,8 +40,30 @@ namespace internal {
// Forward declarations.
class LDeferredCode;
+class LGapNode;
class SafepointGenerator;
+class LGapResolver BASE_EMBEDDED {
+ public:
+ LGapResolver();
+ const ZoneList<LMoveOperands>* Resolve(const ZoneList<LMoveOperands>* moves,
+ LOperand* marker_operand);
+
+ private:
+ LGapNode* LookupNode(LOperand* operand);
+ bool CanReach(LGapNode* a, LGapNode* b, int visited_id);
+ bool CanReach(LGapNode* a, LGapNode* b);
+ void RegisterMove(LMoveOperands move);
+ void AddResultMove(LOperand* from, LOperand* to);
+ void AddResultMove(LGapNode* from, LGapNode* to);
+ void ResolveCycle(LGapNode* start, LOperand* marker_operand);
+
+ ZoneList<LGapNode*> nodes_;
+ ZoneList<LGapNode*> identified_cycles_;
+ ZoneList<LMoveOperands> result_;
+ int next_visited_id_;
+};
+
class LCodeGen BASE_EMBEDDED {
public:
@@ -77,10 +99,15 @@ class LCodeGen BASE_EMBEDDED {
void DoDeferredTaggedToI(LTaggedToI* instr);
void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr);
void DoDeferredStackCheck(LGoto* instr);
+ void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
+ Label* map_check);
// Parallel move support.
void DoParallelMove(LParallelMove* move);
+ // Emit frame translation commands for an environment.
+ void WriteTranslation(LEnvironment* environment, Translation* translation);
+
// Declare methods that deal with the individual node types.
#define DECLARE_DO(type) void Do##type(L##type* node);
LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_DO)
@@ -148,7 +175,7 @@ class LCodeGen BASE_EMBEDDED {
int arity,
LInstruction* instr);
- void LoadPrototype(Register result, Handle<JSObject> prototype);
+ void LoadHeapObject(Register result, Handle<HeapObject> object);
void RegisterLazyDeoptimization(LInstruction* instr);
void RegisterEnvironmentForDeoptimization(LEnvironment* environment);
@@ -228,6 +255,9 @@ class LCodeGen BASE_EMBEDDED {
// itself is emitted at the end of the generated code.
SafepointTableBuilder safepoints_;
+ // Compiler from a set of parallel moves to a sequential list of moves.
+ LGapResolver resolver_;
+
friend class LDeferredCode;
friend class LEnvironment;
friend class SafepointGenerator;
diff --git a/deps/v8/src/ia32/lithium-ia32.cc b/deps/v8/src/ia32/lithium-ia32.cc
index 3b272d0b02..254a47af78 100644
--- a/deps/v8/src/ia32/lithium-ia32.cc
+++ b/deps/v8/src/ia32/lithium-ia32.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -25,6 +25,10 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include "v8.h"
+
+#if defined(V8_TARGET_ARCH_IA32)
+
#include "ia32/lithium-ia32.h"
#include "ia32/lithium-codegen-ia32.h"
@@ -64,12 +68,12 @@ void LOsrEntry::MarkSpilledDoubleRegister(int allocation_index,
}
-void LInstruction::PrintTo(StringStream* stream) const {
+void LInstruction::PrintTo(StringStream* stream) {
stream->Add("%s ", this->Mnemonic());
if (HasResult()) {
- result()->PrintTo(stream);
- stream->Add(" ");
+ PrintOutputOperandTo(stream);
}
+
PrintDataTo(stream);
if (HasEnvironment()) {
@@ -84,37 +88,29 @@ void LInstruction::PrintTo(StringStream* stream) const {
}
-void LLabel::PrintDataTo(StringStream* stream) const {
- LGap::PrintDataTo(stream);
- LLabel* rep = replacement();
- if (rep != NULL) {
- stream->Add(" Dead block replaced with B%d", rep->block_id());
+template<int R, int I, int T>
+void LTemplateInstruction<R, I, T>::PrintDataTo(StringStream* stream) {
+ for (int i = 0; i < I; i++) {
+ stream->Add(i == 0 ? "= " : " ");
+ inputs_.at(i)->PrintTo(stream);
}
}
-bool LParallelMove::IsRedundant() const {
- for (int i = 0; i < move_operands_.length(); ++i) {
- if (!move_operands_[i].IsRedundant()) return false;
+template<int R, int I, int T>
+void LTemplateInstruction<R, I, T>::PrintOutputOperandTo(StringStream* stream) {
+ if (this->HasResult()) {
+ this->result()->PrintTo(stream);
+ stream->Add(" ");
}
- return true;
}
-void LParallelMove::PrintDataTo(StringStream* stream) const {
- for (int i = move_operands_.length() - 1; i >= 0; --i) {
- if (!move_operands_[i].IsEliminated()) {
- LOperand* from = move_operands_[i].from();
- LOperand* to = move_operands_[i].to();
- if (from->Equals(to)) {
- to->PrintTo(stream);
- } else {
- to->PrintTo(stream);
- stream->Add(" = ");
- from->PrintTo(stream);
- }
- stream->Add("; ");
- }
+void LLabel::PrintDataTo(StringStream* stream) {
+ LGap::PrintDataTo(stream);
+ LLabel* rep = replacement();
+ if (rep != NULL) {
+ stream->Add(" Dead block replaced with B%d", rep->block_id());
}
}
@@ -130,7 +126,7 @@ bool LGap::IsRedundant() const {
}
-void LGap::PrintDataTo(StringStream* stream) const {
+void LGap::PrintDataTo(StringStream* stream) {
for (int i = 0; i < 4; i++) {
stream->Add("(");
if (parallel_moves_[i] != NULL) {
@@ -169,27 +165,18 @@ const char* LArithmeticT::Mnemonic() const {
}
-
-void LBinaryOperation::PrintDataTo(StringStream* stream) const {
- stream->Add("= ");
- left()->PrintTo(stream);
- stream->Add(" ");
- right()->PrintTo(stream);
-}
-
-
-void LGoto::PrintDataTo(StringStream* stream) const {
+void LGoto::PrintDataTo(StringStream* stream) {
stream->Add("B%d", block_id());
}
-void LBranch::PrintDataTo(StringStream* stream) const {
+void LBranch::PrintDataTo(StringStream* stream) {
stream->Add("B%d | B%d on ", true_block_id(), false_block_id());
input()->PrintTo(stream);
}
-void LCmpIDAndBranch::PrintDataTo(StringStream* stream) const {
+void LCmpIDAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if ");
left()->PrintTo(stream);
stream->Add(" %s ", Token::String(op()));
@@ -198,7 +185,7 @@ void LCmpIDAndBranch::PrintDataTo(StringStream* stream) const {
}
-void LIsNullAndBranch::PrintDataTo(StringStream* stream) const {
+void LIsNullAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if ");
input()->PrintTo(stream);
stream->Add(is_strict() ? " === null" : " == null");
@@ -206,35 +193,35 @@ void LIsNullAndBranch::PrintDataTo(StringStream* stream) const {
}
-void LIsObjectAndBranch::PrintDataTo(StringStream* stream) const {
+void LIsObjectAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if is_object(");
input()->PrintTo(stream);
stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
}
-void LIsSmiAndBranch::PrintDataTo(StringStream* stream) const {
+void LIsSmiAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if is_smi(");
input()->PrintTo(stream);
stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
}
-void LHasInstanceTypeAndBranch::PrintDataTo(StringStream* stream) const {
+void LHasInstanceTypeAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if has_instance_type(");
input()->PrintTo(stream);
stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
}
-void LHasCachedArrayIndexAndBranch::PrintDataTo(StringStream* stream) const {
+void LHasCachedArrayIndexAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if has_cached_array_index(");
input()->PrintTo(stream);
stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
}
-void LClassOfTestAndBranch::PrintDataTo(StringStream* stream) const {
+void LClassOfTestAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if class_of_test(");
input()->PrintTo(stream);
stream->Add(", \"%o\") then B%d else B%d",
@@ -244,13 +231,13 @@ void LClassOfTestAndBranch::PrintDataTo(StringStream* stream) const {
}
-void LTypeofIs::PrintDataTo(StringStream* stream) const {
+void LTypeofIs::PrintDataTo(StringStream* stream) {
input()->PrintTo(stream);
stream->Add(" == \"%s\"", *hydrogen()->type_literal()->ToCString());
}
-void LTypeofIsAndBranch::PrintDataTo(StringStream* stream) const {
+void LTypeofIsAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if typeof ");
input()->PrintTo(stream);
stream->Add(" == \"%s\" then B%d else B%d",
@@ -259,59 +246,59 @@ void LTypeofIsAndBranch::PrintDataTo(StringStream* stream) const {
}
-void LCallConstantFunction::PrintDataTo(StringStream* stream) const {
+void LCallConstantFunction::PrintDataTo(StringStream* stream) {
stream->Add("#%d / ", arity());
}
-void LUnaryMathOperation::PrintDataTo(StringStream* stream) const {
+void LUnaryMathOperation::PrintDataTo(StringStream* stream) {
stream->Add("/%s ", hydrogen()->OpName());
input()->PrintTo(stream);
}
-void LCallKeyed::PrintDataTo(StringStream* stream) const {
+void LLoadContextSlot::PrintDataTo(StringStream* stream) {
+ stream->Add("(%d, %d)", context_chain_length(), slot_index());
+}
+
+
+void LCallKeyed::PrintDataTo(StringStream* stream) {
stream->Add("[ecx] #%d / ", arity());
}
-void LCallNamed::PrintDataTo(StringStream* stream) const {
+void LCallNamed::PrintDataTo(StringStream* stream) {
SmartPointer<char> name_string = name()->ToCString();
stream->Add("%s #%d / ", *name_string, arity());
}
-void LCallGlobal::PrintDataTo(StringStream* stream) const {
+void LCallGlobal::PrintDataTo(StringStream* stream) {
SmartPointer<char> name_string = name()->ToCString();
stream->Add("%s #%d / ", *name_string, arity());
}
-void LCallKnownGlobal::PrintDataTo(StringStream* stream) const {
+void LCallKnownGlobal::PrintDataTo(StringStream* stream) {
stream->Add("#%d / ", arity());
}
-void LCallNew::PrintDataTo(StringStream* stream) const {
- LUnaryOperation::PrintDataTo(stream);
+void LCallNew::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ input()->PrintTo(stream);
stream->Add(" #%d / ", arity());
}
-void LClassOfTest::PrintDataTo(StringStream* stream) const {
+void LClassOfTest::PrintDataTo(StringStream* stream) {
stream->Add("= class_of_test(");
input()->PrintTo(stream);
stream->Add(", \"%o\")", *hydrogen()->class_name());
}
-void LUnaryOperation::PrintDataTo(StringStream* stream) const {
- stream->Add("= ");
- input()->PrintTo(stream);
-}
-
-
-void LAccessArgumentsAt::PrintDataTo(StringStream* stream) const {
+void LAccessArgumentsAt::PrintDataTo(StringStream* stream) {
arguments()->PrintTo(stream);
stream->Add(" length ");
@@ -322,20 +309,6 @@ void LAccessArgumentsAt::PrintDataTo(StringStream* stream) const {
}
-LChunk::LChunk(HGraph* graph)
- : spill_slot_count_(0),
- graph_(graph),
- instructions_(32),
- pointer_maps_(8),
- inlined_closures_(1) {
-}
-
-
-void LChunk::Verify() const {
- // TODO(twuerthinger): Implement verification for chunk.
-}
-
-
int LChunk::GetNextSpillIndex(bool is_double) {
// Skip a slot if for a double-width slot.
if (is_double) spill_slot_count_++;
@@ -390,7 +363,7 @@ void LChunk::MarkEmptyBlocks() {
}
-void LStoreNamed::PrintDataTo(StringStream* stream) const {
+void LStoreNamed::PrintDataTo(StringStream* stream) {
object()->PrintTo(stream);
stream->Add(".");
stream->Add(*String::cast(*name())->ToCString());
@@ -399,7 +372,7 @@ void LStoreNamed::PrintDataTo(StringStream* stream) const {
}
-void LStoreKeyed::PrintDataTo(StringStream* stream) const {
+void LStoreKeyed::PrintDataTo(StringStream* stream) {
object()->PrintTo(stream);
stream->Add("[");
key()->PrintTo(stream);
@@ -472,151 +445,6 @@ void LChunk::AddGapMove(int index, LOperand* from, LOperand* to) {
}
-class LGapNode: public ZoneObject {
- public:
- explicit LGapNode(LOperand* operand)
- : operand_(operand), resolved_(false), visited_id_(-1) { }
-
- LOperand* operand() const { return operand_; }
- bool IsResolved() const { return !IsAssigned() || resolved_; }
- void MarkResolved() {
- ASSERT(!IsResolved());
- resolved_ = true;
- }
- int visited_id() const { return visited_id_; }
- void set_visited_id(int id) {
- ASSERT(id > visited_id_);
- visited_id_ = id;
- }
-
- bool IsAssigned() const { return assigned_from_.is_set(); }
- LGapNode* assigned_from() const { return assigned_from_.get(); }
- void set_assigned_from(LGapNode* n) { assigned_from_.set(n); }
-
- private:
- LOperand* operand_;
- SetOncePointer<LGapNode> assigned_from_;
- bool resolved_;
- int visited_id_;
-};
-
-
-LGapResolver::LGapResolver(const ZoneList<LMoveOperands>* moves,
- LOperand* marker_operand)
- : nodes_(4),
- identified_cycles_(4),
- result_(4),
- marker_operand_(marker_operand),
- next_visited_id_(0) {
- for (int i = 0; i < moves->length(); ++i) {
- LMoveOperands move = moves->at(i);
- if (!move.IsRedundant()) RegisterMove(move);
- }
-}
-
-
-const ZoneList<LMoveOperands>* LGapResolver::ResolveInReverseOrder() {
- for (int i = 0; i < identified_cycles_.length(); ++i) {
- ResolveCycle(identified_cycles_[i]);
- }
-
- int unresolved_nodes;
- do {
- unresolved_nodes = 0;
- for (int j = 0; j < nodes_.length(); j++) {
- LGapNode* node = nodes_[j];
- if (!node->IsResolved() && node->assigned_from()->IsResolved()) {
- AddResultMove(node->assigned_from(), node);
- node->MarkResolved();
- }
- if (!node->IsResolved()) ++unresolved_nodes;
- }
- } while (unresolved_nodes > 0);
- return &result_;
-}
-
-
-void LGapResolver::AddResultMove(LGapNode* from, LGapNode* to) {
- AddResultMove(from->operand(), to->operand());
-}
-
-
-void LGapResolver::AddResultMove(LOperand* from, LOperand* to) {
- result_.Add(LMoveOperands(from, to));
-}
-
-
-void LGapResolver::ResolveCycle(LGapNode* start) {
- ZoneList<LOperand*> circle_operands(8);
- circle_operands.Add(marker_operand_);
- LGapNode* cur = start;
- do {
- cur->MarkResolved();
- circle_operands.Add(cur->operand());
- cur = cur->assigned_from();
- } while (cur != start);
- circle_operands.Add(marker_operand_);
-
- for (int i = circle_operands.length() - 1; i > 0; --i) {
- LOperand* from = circle_operands[i];
- LOperand* to = circle_operands[i - 1];
- AddResultMove(from, to);
- }
-}
-
-
-bool LGapResolver::CanReach(LGapNode* a, LGapNode* b, int visited_id) {
- ASSERT(a != b);
- LGapNode* cur = a;
- while (cur != b && cur->visited_id() != visited_id && cur->IsAssigned()) {
- cur->set_visited_id(visited_id);
- cur = cur->assigned_from();
- }
-
- return cur == b;
-}
-
-
-bool LGapResolver::CanReach(LGapNode* a, LGapNode* b) {
- ASSERT(a != b);
- return CanReach(a, b, next_visited_id_++);
-}
-
-
-void LGapResolver::RegisterMove(LMoveOperands move) {
- if (move.from()->IsConstantOperand()) {
- // Constant moves should be last in the machine code. Therefore add them
- // first to the result set.
- AddResultMove(move.from(), move.to());
- } else {
- LGapNode* from = LookupNode(move.from());
- LGapNode* to = LookupNode(move.to());
- if (to->IsAssigned() && to->assigned_from() == from) {
- move.Eliminate();
- return;
- }
- ASSERT(!to->IsAssigned());
- if (CanReach(from, to)) {
- // This introduces a circle. Save.
- identified_cycles_.Add(from);
- }
- to->set_assigned_from(from);
- }
-}
-
-
-LGapNode* LGapResolver::LookupNode(LOperand* operand) {
- for (int i = 0; i < nodes_.length(); ++i) {
- if (nodes_[i]->operand()->Equals(operand)) return nodes_[i];
- }
-
- // No node found => create a new one.
- LGapNode* result = new LGapNode(operand);
- nodes_.Add(result);
- return result;
-}
-
-
Handle<Object> LChunk::LookupLiteral(LConstantOperand* operand) const {
return HConstant::cast(graph_->LookupValue(operand->index()))->handle();
}
@@ -752,38 +580,54 @@ LOperand* LChunkBuilder::Use(HValue* value, LUnallocated* operand) {
}
-LInstruction* LChunkBuilder::Define(LInstruction* instr) {
- return Define(instr, new LUnallocated(LUnallocated::NONE));
+template<int I, int T>
+LInstruction* LChunkBuilder::Define(LTemplateInstruction<1, I, T>* instr,
+ LUnallocated* result) {
+ allocator_->RecordDefinition(current_instruction_, result);
+ instr->set_result(result);
+ return instr;
}
-LInstruction* LChunkBuilder::DefineAsRegister(LInstruction* instr) {
- return Define(instr, new LUnallocated(LUnallocated::MUST_HAVE_REGISTER));
+template<int I, int T>
+LInstruction* LChunkBuilder::Define(LTemplateInstruction<1, I, T>* instr) {
+ return Define(instr, new LUnallocated(LUnallocated::NONE));
}
-LInstruction* LChunkBuilder::DefineAsSpilled(LInstruction* instr, int index) {
- return Define(instr, new LUnallocated(LUnallocated::FIXED_SLOT, index));
+template<int I, int T>
+LInstruction* LChunkBuilder::DefineAsRegister(
+ LTemplateInstruction<1, I, T>* instr) {
+ return Define(instr, new LUnallocated(LUnallocated::MUST_HAVE_REGISTER));
}
-LInstruction* LChunkBuilder::DefineSameAsAny(LInstruction* instr) {
- return Define(instr, new LUnallocated(LUnallocated::SAME_AS_ANY_INPUT));
+template<int I, int T>
+LInstruction* LChunkBuilder::DefineAsSpilled(
+ LTemplateInstruction<1, I, T>* instr,
+ int index) {
+ return Define(instr, new LUnallocated(LUnallocated::FIXED_SLOT, index));
}
-LInstruction* LChunkBuilder::DefineSameAsFirst(LInstruction* instr) {
+template<int I, int T>
+LInstruction* LChunkBuilder::DefineSameAsFirst(
+ LTemplateInstruction<1, I, T>* instr) {
return Define(instr, new LUnallocated(LUnallocated::SAME_AS_FIRST_INPUT));
}
-LInstruction* LChunkBuilder::DefineFixed(LInstruction* instr, Register reg) {
+template<int I, int T>
+LInstruction* LChunkBuilder::DefineFixed(LTemplateInstruction<1, I, T>* instr,
+ Register reg) {
return Define(instr, ToUnallocated(reg));
}
-LInstruction* LChunkBuilder::DefineFixedDouble(LInstruction* instr,
- XMMRegister reg) {
+template<int I, int T>
+LInstruction* LChunkBuilder::DefineFixedDouble(
+ LTemplateInstruction<1, I, T>* instr,
+ XMMRegister reg) {
return Define(instr, ToUnallocated(reg));
}
@@ -838,27 +682,19 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr,
}
-LInstruction* LChunkBuilder::AssignPointerMap(LInstruction* instr) {
- ASSERT(!instr->HasPointerMap());
- instr->set_pointer_map(new LPointerMap(position_));
+LInstruction* LChunkBuilder::MarkAsSaveDoubles(LInstruction* instr) {
+ allocator_->MarkAsSaveDoubles();
return instr;
}
-LInstruction* LChunkBuilder::Define(LInstruction* instr, LUnallocated* result) {
- allocator_->RecordDefinition(current_instruction_, result);
- instr->set_result(result);
+LInstruction* LChunkBuilder::AssignPointerMap(LInstruction* instr) {
+ ASSERT(!instr->HasPointerMap());
+ instr->set_pointer_map(new LPointerMap(position_));
return instr;
}
-LOperand* LChunkBuilder::Temp() {
- LUnallocated* operand = new LUnallocated(LUnallocated::NONE);
- allocator_->RecordTemporary(operand);
- return operand;
-}
-
-
LUnallocated* LChunkBuilder::TempRegister() {
LUnallocated* operand = new LUnallocated(LUnallocated::MUST_HAVE_REGISTER);
allocator_->RecordTemporary(operand);
@@ -934,10 +770,10 @@ LInstruction* LChunkBuilder::DoShift(Token::Value op,
can_deopt = !can_truncate;
}
- LInstruction* result =
- DefineSameAsFirst(new LShiftI(op, left, right, can_deopt));
- if (can_deopt) AssignEnvironment(result);
- return result;
+ LShiftI* result = new LShiftI(op, left, right, can_deopt);
+ return can_deopt
+ ? AssignEnvironment(DefineSameAsFirst(result))
+ : DefineSameAsFirst(result);
}
@@ -966,7 +802,7 @@ LInstruction* LChunkBuilder::DoArithmeticT(Token::Value op,
ASSERT(right->representation().IsTagged());
LOperand* left_operand = UseFixed(left, edx);
LOperand* right_operand = UseFixed(right, eax);
- LInstruction* result = new LArithmeticT(op, left_operand, right_operand);
+ LArithmeticT* result = new LArithmeticT(op, left_operand, right_operand);
return MarkAsCall(DefineFixed(result, eax), instr);
}
@@ -1016,9 +852,6 @@ void LChunkBuilder::DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block) {
HInstruction* current = block->first();
int start = chunk_->instructions()->length();
while (current != NULL && !is_aborted()) {
- if (FLAG_trace_environment) {
- PrintF("Process instruction %d\n", current->id());
- }
// Code for constants in registers is generated lazily.
if (!current->EmitAtUses()) {
VisitInstruction(current);
@@ -1066,66 +899,13 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) {
}
-void LEnvironment::WriteTranslation(LCodeGen* cgen,
- Translation* translation) const {
- if (this == NULL) return;
-
- // The translation includes one command per value in the environment.
- int translation_size = values()->length();
- // The output frame height does not include the parameters.
- int height = translation_size - parameter_count();
-
- outer()->WriteTranslation(cgen, translation);
- int closure_id = cgen->DefineDeoptimizationLiteral(closure());
- translation->BeginFrame(ast_id(), closure_id, height);
- for (int i = 0; i < translation_size; ++i) {
- LOperand* value = values()->at(i);
- // spilled_registers_ and spilled_double_registers_ are either
- // both NULL or both set.
- if (spilled_registers_ != NULL && value != NULL) {
- if (value->IsRegister() &&
- spilled_registers_[value->index()] != NULL) {
- translation->MarkDuplicate();
- cgen->AddToTranslation(translation,
- spilled_registers_[value->index()],
- HasTaggedValueAt(i));
- } else if (value->IsDoubleRegister() &&
- spilled_double_registers_[value->index()] != NULL) {
- translation->MarkDuplicate();
- cgen->AddToTranslation(translation,
- spilled_double_registers_[value->index()],
- false);
- }
- }
-
- cgen->AddToTranslation(translation, value, HasTaggedValueAt(i));
- }
-}
-
-
-void LEnvironment::PrintTo(StringStream* stream) const {
- stream->Add("[id=%d|", ast_id());
- stream->Add("[parameters=%d|", parameter_count());
- stream->Add("[arguments_stack_height=%d|", arguments_stack_height());
- for (int i = 0; i < values_.length(); ++i) {
- if (i != 0) stream->Add(";");
- if (values_[i] == NULL) {
- stream->Add("[hole]");
- } else {
- values_[i]->PrintTo(stream);
- }
- }
- stream->Add("]");
-}
-
-
LEnvironment* LChunkBuilder::CreateEnvironment(HEnvironment* hydrogen_env) {
if (hydrogen_env == NULL) return NULL;
LEnvironment* outer = CreateEnvironment(hydrogen_env->outer());
int ast_id = hydrogen_env->ast_id();
ASSERT(ast_id != AstNode::kNoNumber);
- int value_count = hydrogen_env->values()->length();
+ int value_count = hydrogen_env->length();
LEnvironment* result = new LEnvironment(hydrogen_env->closure(),
ast_id,
hydrogen_env->parameter_count(),
@@ -1155,10 +935,11 @@ LEnvironment* LChunkBuilder::CreateEnvironment(HEnvironment* hydrogen_env) {
LInstruction* LChunkBuilder::DoGoto(HGoto* instr) {
- LInstruction* result = new LGoto(instr->FirstSuccessor()->block_id(),
- instr->include_stack_check());
- if (instr->include_stack_check()) result = AssignPointerMap(result);
- return result;
+ LGoto* result = new LGoto(instr->FirstSuccessor()->block_id(),
+ instr->include_stack_check());
+ return (instr->include_stack_check())
+ ? AssignPointerMap(result)
+ : result;
}
@@ -1185,32 +966,33 @@ LInstruction* LChunkBuilder::DoBranch(HBranch* instr) {
Token::Value op = compare->token();
HValue* left = compare->left();
HValue* right = compare->right();
- if (left->representation().IsInteger32()) {
+ Representation r = compare->GetInputRepresentation();
+ if (r.IsInteger32()) {
+ ASSERT(left->representation().IsInteger32());
ASSERT(right->representation().IsInteger32());
- return new LCmpIDAndBranch(op,
- UseRegisterAtStart(left),
+
+ return new LCmpIDAndBranch(UseRegisterAtStart(left),
UseOrConstantAtStart(right),
first_id,
- second_id,
- false);
- } else if (left->representation().IsDouble()) {
+ second_id);
+ } else if (r.IsDouble()) {
+ ASSERT(left->representation().IsDouble());
ASSERT(right->representation().IsDouble());
- return new LCmpIDAndBranch(op,
- UseRegisterAtStart(left),
+
+ return new LCmpIDAndBranch(UseRegisterAtStart(left),
UseRegisterAtStart(right),
first_id,
- second_id,
- true);
+ second_id);
} else {
ASSERT(left->representation().IsTagged());
ASSERT(right->representation().IsTagged());
bool reversed = op == Token::GT || op == Token::LTE;
LOperand* left_operand = UseFixed(left, reversed ? eax : edx);
LOperand* right_operand = UseFixed(right, reversed ? edx : eax);
- LInstruction* result = new LCmpTAndBranch(left_operand,
- right_operand,
- first_id,
- second_id);
+ LCmpTAndBranch* result = new LCmpTAndBranch(left_operand,
+ right_operand,
+ first_id,
+ second_id);
return MarkAsCall(result, instr);
}
} else if (v->IsIsSmi()) {
@@ -1241,7 +1023,6 @@ LInstruction* LChunkBuilder::DoBranch(HBranch* instr) {
// We only need a temp register for non-strict compare.
LOperand* temp = compare->is_strict() ? NULL : TempRegister();
return new LIsNullAndBranch(UseRegisterAtStart(compare->value()),
- compare->is_strict(),
temp,
first_id,
second_id);
@@ -1264,11 +1045,12 @@ LInstruction* LChunkBuilder::DoBranch(HBranch* instr) {
second_id);
} else if (v->IsInstanceOf()) {
HInstanceOf* instance_of = HInstanceOf::cast(v);
- LInstruction* result =
- new LInstanceOfAndBranch(UseFixed(instance_of->left(), eax),
- UseFixed(instance_of->right(), edx),
- first_id,
- second_id);
+ LInstanceOfAndBranch* result =
+ new LInstanceOfAndBranch(
+ UseFixed(instance_of->left(), InstanceofStub::left()),
+ UseFixed(instance_of->right(), InstanceofStub::right()),
+ first_id,
+ second_id);
return MarkAsCall(result, instr);
} else if (v->IsTypeofIs()) {
HTypeofIs* typeof_is = HTypeofIs::cast(v);
@@ -1295,12 +1077,7 @@ LInstruction* LChunkBuilder::DoCompareMapAndBranch(
HCompareMapAndBranch* instr) {
ASSERT(instr->value()->representation().IsTagged());
LOperand* value = UseRegisterAtStart(instr->value());
- HBasicBlock* first = instr->FirstSuccessor();
- HBasicBlock* second = instr->SecondSuccessor();
- return new LCmpMapAndBranch(value,
- instr->map(),
- first->block_id(),
- second->block_id());
+ return new LCmpMapAndBranch(value);
}
@@ -1315,22 +1092,33 @@ LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) {
LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
- LInstruction* result =
- new LInstanceOf(UseFixed(instr->left(), eax),
- UseFixed(instr->right(), edx));
+ LInstanceOf* result =
+ new LInstanceOf(UseFixed(instr->left(), InstanceofStub::left()),
+ UseFixed(instr->right(), InstanceofStub::right()));
return MarkAsCall(DefineFixed(result, eax), instr);
}
+LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal(
+ HInstanceOfKnownGlobal* instr) {
+ LInstanceOfKnownGlobal* result =
+ new LInstanceOfKnownGlobal(
+ UseFixed(instr->value(), InstanceofStub::left()),
+ FixedTemp(edi));
+ MarkAsSaveDoubles(result);
+ return AssignEnvironment(AssignPointerMap(DefineFixed(result, eax)));
+}
+
+
LInstruction* LChunkBuilder::DoApplyArguments(HApplyArguments* instr) {
LOperand* function = UseFixed(instr->function(), edi);
LOperand* receiver = UseFixed(instr->receiver(), eax);
LOperand* length = UseRegisterAtStart(instr->length());
LOperand* elements = UseRegisterAtStart(instr->elements());
- LInstruction* result = new LApplyArguments(function,
- receiver,
- length,
- elements);
+ LApplyArguments* result = new LApplyArguments(function,
+ receiver,
+ length,
+ elements);
return MarkAsCall(DefineFixed(result, eax), instr, CAN_DEOPTIMIZE_EAGERLY);
}
@@ -1363,11 +1151,11 @@ LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
BuiltinFunctionId op = instr->op();
if (op == kMathLog || op == kMathSin || op == kMathCos) {
LOperand* input = UseFixedDouble(instr->value(), xmm1);
- LInstruction* result = new LUnaryMathOperation(input);
+ LUnaryMathOperation* result = new LUnaryMathOperation(input);
return MarkAsCall(DefineFixedDouble(result, xmm1), instr);
} else {
LOperand* input = UseRegisterAtStart(instr->value());
- LInstruction* result = new LUnaryMathOperation(input);
+ LUnaryMathOperation* result = new LUnaryMathOperation(input);
switch (op) {
case kMathAbs:
return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result)));
@@ -1416,7 +1204,7 @@ LInstruction* LChunkBuilder::DoCallKnownGlobal(HCallKnownGlobal* instr) {
LInstruction* LChunkBuilder::DoCallNew(HCallNew* instr) {
LOperand* constructor = UseFixed(instr->constructor(), edi);
argument_count_ -= instr->argument_count();
- LInstruction* result = new LCallNew(constructor);
+ LCallNew* result = new LCallNew(constructor);
return MarkAsCall(DefineFixed(result, eax), instr);
}
@@ -1456,7 +1244,9 @@ LInstruction* LChunkBuilder::DoBitAnd(HBitAnd* instr) {
LInstruction* LChunkBuilder::DoBitNot(HBitNot* instr) {
ASSERT(instr->value()->representation().IsInteger32());
ASSERT(instr->representation().IsInteger32());
- return DefineSameAsFirst(new LBitNotI(UseRegisterAtStart(instr->value())));
+ LOperand* input = UseRegisterAtStart(instr->value());
+ LBitNotI* result = new LBitNotI(input);
+ return DefineSameAsFirst(result);
}
@@ -1496,12 +1286,12 @@ LInstruction* LChunkBuilder::DoMod(HMod* instr) {
FixedTemp(edx);
LOperand* value = UseFixed(instr->left(), eax);
LOperand* divisor = UseRegister(instr->right());
- LInstruction* result = DefineFixed(new LModI(value, divisor), edx);
- if (instr->CheckFlag(HValue::kBailoutOnMinusZero) ||
- instr->CheckFlag(HValue::kCanBeDivByZero)) {
- result = AssignEnvironment(result);
- }
- return result;
+ LModI* mod = new LModI(value, divisor);
+ LInstruction* result = DefineFixed(mod, edx);
+ return (instr->CheckFlag(HValue::kBailoutOnMinusZero) ||
+ instr->CheckFlag(HValue::kCanBeDivByZero))
+ ? AssignEnvironment(result)
+ : result;
} else if (instr->representation().IsTagged()) {
return DoArithmeticT(Token::MOD, instr);
} else {
@@ -1598,21 +1388,26 @@ LInstruction* LChunkBuilder::DoPower(HPower* instr) {
LInstruction* LChunkBuilder::DoCompare(HCompare* instr) {
Token::Value op = instr->token();
- if (instr->left()->representation().IsInteger32()) {
+ Representation r = instr->GetInputRepresentation();
+ if (r.IsInteger32()) {
+ ASSERT(instr->left()->representation().IsInteger32());
ASSERT(instr->right()->representation().IsInteger32());
LOperand* left = UseRegisterAtStart(instr->left());
LOperand* right = UseOrConstantAtStart(instr->right());
- return DefineAsRegister(new LCmpID(op, left, right, false));
- } else if (instr->left()->representation().IsDouble()) {
+ return DefineAsRegister(new LCmpID(left, right));
+ } else if (r.IsDouble()) {
+ ASSERT(instr->left()->representation().IsDouble());
ASSERT(instr->right()->representation().IsDouble());
LOperand* left = UseRegisterAtStart(instr->left());
LOperand* right = UseRegisterAtStart(instr->right());
- return DefineAsRegister(new LCmpID(op, left, right, true));
+ return DefineAsRegister(new LCmpID(left, right));
} else {
+ ASSERT(instr->left()->representation().IsTagged());
+ ASSERT(instr->right()->representation().IsTagged());
bool reversed = (op == Token::GT || op == Token::LTE);
LOperand* left = UseFixed(instr->left(), reversed ? eax : edx);
LOperand* right = UseFixed(instr->right(), reversed ? edx : eax);
- LInstruction* result = new LCmpT(left, right);
+ LCmpT* result = new LCmpT(left, right);
return MarkAsCall(DefineFixed(result, eax), instr);
}
}
@@ -1622,7 +1417,7 @@ LInstruction* LChunkBuilder::DoCompareJSObjectEq(
HCompareJSObjectEq* instr) {
LOperand* left = UseRegisterAtStart(instr->left());
LOperand* right = UseRegisterAtStart(instr->right());
- LInstruction* result = new LCmpJSObjectEq(left, right);
+ LCmpJSObjectEq* result = new LCmpJSObjectEq(left, right);
return DefineAsRegister(result);
}
@@ -1631,8 +1426,7 @@ LInstruction* LChunkBuilder::DoIsNull(HIsNull* instr) {
ASSERT(instr->value()->representation().IsTagged());
LOperand* value = UseRegisterAtStart(instr->value());
- return DefineAsRegister(new LIsNull(value,
- instr->is_strict()));
+ return DefineAsRegister(new LIsNull(value));
}
@@ -1677,25 +1471,21 @@ LInstruction* LChunkBuilder::DoClassOfTest(HClassOfTest* instr) {
}
-LInstruction* LChunkBuilder::DoArrayLength(HArrayLength* instr) {
- LOperand* array = NULL;
- LOperand* temporary = NULL;
+LInstruction* LChunkBuilder::DoJSArrayLength(HJSArrayLength* instr) {
+ LOperand* array = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new LJSArrayLength(array));
+}
- if (instr->value()->IsLoadElements()) {
- array = UseRegisterAtStart(instr->value());
- } else {
- array = UseRegister(instr->value());
- temporary = TempRegister();
- }
- LInstruction* result = new LArrayLength(array, temporary);
- return AssignEnvironment(DefineAsRegister(result));
+LInstruction* LChunkBuilder::DoFixedArrayLength(HFixedArrayLength* instr) {
+ LOperand* array = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new LFixedArrayLength(array));
}
LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) {
LOperand* object = UseRegister(instr->value());
- LInstruction* result = new LValueOf(object, TempRegister());
+ LValueOf* result = new LValueOf(object, TempRegister());
return AssignEnvironment(DefineSameAsFirst(result));
}
@@ -1718,7 +1508,7 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) {
if (from.IsTagged()) {
if (to.IsDouble()) {
LOperand* value = UseRegister(instr->value());
- LInstruction* res = new LNumberUntagD(value);
+ LNumberUntagD* res = new LNumberUntagD(value);
return AssignEnvironment(DefineAsRegister(res));
} else {
ASSERT(to.IsInteger32());
@@ -1729,7 +1519,7 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) {
(instr->CanTruncateToInt32() && CpuFeatures::IsSupported(SSE3))
? NULL
: FixedTemp(xmm1);
- LInstruction* res = new LTaggedToI(value, xmm_temp);
+ LTaggedToI* res = new LTaggedToI(value, xmm_temp);
return AssignEnvironment(DefineSameAsFirst(res));
} else {
return DefineSameAsFirst(new LSmiUntag(value, needs_check));
@@ -1742,12 +1532,16 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) {
// Make sure that temp and result_temp are different registers.
LUnallocated* result_temp = TempRegister();
- LInstruction* result = new LNumberTagD(value, temp);
+ LNumberTagD* result = new LNumberTagD(value, temp);
return AssignPointerMap(Define(result, result_temp));
} else {
ASSERT(to.IsInteger32());
- LOperand* value = UseRegister(instr->value());
- return AssignEnvironment(DefineAsRegister(new LDoubleToI(value)));
+ bool needs_temp = instr->CanTruncateToInt32() &&
+ !CpuFeatures::IsSupported(SSE3);
+ LOperand* value = needs_temp ?
+ UseTempRegister(instr->value()) : UseRegister(instr->value());
+ LOperand* temp = needs_temp ? TempRegister() : NULL;
+ return AssignEnvironment(DefineAsRegister(new LDoubleToI(value, temp)));
}
} else if (from.IsInteger32()) {
if (to.IsTagged()) {
@@ -1756,7 +1550,7 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) {
if (val->HasRange() && val->range()->IsInSmiRange()) {
return DefineSameAsFirst(new LSmiTag(value));
} else {
- LInstruction* result = new LNumberTagI(value);
+ LNumberTagI* result = new LNumberTagI(value);
return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result)));
}
} else {
@@ -1778,17 +1572,14 @@ LInstruction* LChunkBuilder::DoCheckNonSmi(HCheckNonSmi* instr) {
LInstruction* LChunkBuilder::DoCheckInstanceType(HCheckInstanceType* instr) {
LOperand* value = UseRegisterAtStart(instr->value());
LOperand* temp = TempRegister();
- LInstruction* result = new LCheckInstanceType(value, temp);
+ LCheckInstanceType* result = new LCheckInstanceType(value, temp);
return AssignEnvironment(result);
}
LInstruction* LChunkBuilder::DoCheckPrototypeMaps(HCheckPrototypeMaps* instr) {
LOperand* temp = TempRegister();
- LInstruction* result =
- new LCheckPrototypeMaps(temp,
- instr->holder(),
- instr->receiver_map());
+ LCheckPrototypeMaps* result = new LCheckPrototypeMaps(temp);
return AssignEnvironment(result);
}
@@ -1807,7 +1598,7 @@ LInstruction* LChunkBuilder::DoCheckFunction(HCheckFunction* instr) {
LInstruction* LChunkBuilder::DoCheckMap(HCheckMap* instr) {
LOperand* value = UseRegisterAtStart(instr->value());
- LInstruction* result = new LCheckMap(value);
+ LCheckMap* result = new LCheckMap(value);
return AssignEnvironment(result);
}
@@ -1828,14 +1619,14 @@ LInstruction* LChunkBuilder::DoConstant(HConstant* instr) {
} else if (r.IsTagged()) {
return DefineAsRegister(new LConstantT(instr->handle()));
} else {
- Abort("unsupported constant of type double");
+ UNREACHABLE();
return NULL;
}
}
LInstruction* LChunkBuilder::DoLoadGlobal(HLoadGlobal* instr) {
- LInstruction* result = new LLoadGlobal;
+ LLoadGlobal* result = new LLoadGlobal;
return instr->check_hole_value()
? AssignEnvironment(DefineAsRegister(result))
: DefineAsRegister(result);
@@ -1847,16 +1638,30 @@ LInstruction* LChunkBuilder::DoStoreGlobal(HStoreGlobal* instr) {
}
+LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) {
+ return DefineAsRegister(new LLoadContextSlot);
+}
+
+
LInstruction* LChunkBuilder::DoLoadNamedField(HLoadNamedField* instr) {
- return DefineAsRegister(
- new LLoadNamedField(UseRegisterAtStart(instr->object())));
+ ASSERT(instr->representation().IsTagged());
+ LOperand* obj = UseRegisterAtStart(instr->object());
+ return DefineAsRegister(new LLoadNamedField(obj));
}
LInstruction* LChunkBuilder::DoLoadNamedGeneric(HLoadNamedGeneric* instr) {
LOperand* object = UseFixed(instr->object(), eax);
- LInstruction* result = DefineFixed(new LLoadNamedGeneric(object), eax);
- return MarkAsCall(result, instr);
+ LLoadNamedGeneric* result = new LLoadNamedGeneric(object);
+ return MarkAsCall(DefineFixed(result, eax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoLoadFunctionPrototype(
+ HLoadFunctionPrototype* instr) {
+ return AssignEnvironment(DefineAsRegister(
+ new LLoadFunctionPrototype(UseRegister(instr->function()),
+ TempRegister())));
}
@@ -1868,23 +1673,12 @@ LInstruction* LChunkBuilder::DoLoadElements(HLoadElements* instr) {
LInstruction* LChunkBuilder::DoLoadKeyedFastElement(
HLoadKeyedFastElement* instr) {
- Representation r = instr->representation();
- LOperand* obj = UseRegisterAtStart(instr->object());
+ ASSERT(instr->representation().IsTagged());
ASSERT(instr->key()->representation().IsInteger32());
+ LOperand* obj = UseRegisterAtStart(instr->object());
LOperand* key = UseRegisterAtStart(instr->key());
- LOperand* load_result = NULL;
- // Double needs an extra temp, because the result is converted from heap
- // number to a double register.
- if (r.IsDouble()) load_result = TempRegister();
- LInstruction* result = new LLoadKeyedFastElement(obj,
- key,
- load_result);
- if (r.IsDouble()) {
- result = DefineAsRegister(result);
- } else {
- result = DefineSameAsFirst(result);
- }
- return AssignEnvironment(result);
+ LLoadKeyedFastElement* result = new LLoadKeyedFastElement(obj, key);
+ return AssignEnvironment(DefineSameAsFirst(result));
}
@@ -1892,9 +1686,8 @@ LInstruction* LChunkBuilder::DoLoadKeyedGeneric(HLoadKeyedGeneric* instr) {
LOperand* object = UseFixed(instr->object(), edx);
LOperand* key = UseFixed(instr->key(), eax);
- LInstruction* result =
- DefineFixed(new LLoadKeyedGeneric(object, key), eax);
- return MarkAsCall(result, instr);
+ LLoadKeyedGeneric* result = new LLoadKeyedGeneric(object, key);
+ return MarkAsCall(DefineFixed(result, eax), instr);
}
@@ -1931,7 +1724,7 @@ LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) {
LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
- bool needs_write_barrier = !instr->value()->type().IsSmi();
+ bool needs_write_barrier = instr->NeedsWriteBarrier();
LOperand* obj = needs_write_barrier
? UseTempRegister(instr->object())
@@ -1946,14 +1739,7 @@ LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
LOperand* temp = (!instr->is_in_object() || needs_write_barrier)
? TempRegister() : NULL;
- return new LStoreNamedField(obj,
- instr->name(),
- val,
- instr->is_in_object(),
- instr->offset(),
- temp,
- needs_write_barrier,
- instr->transition());
+ return new LStoreNamedField(obj, val, temp);
}
@@ -1961,7 +1747,7 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
LOperand* obj = UseFixed(instr->object(), edx);
LOperand* val = UseFixed(instr->value(), eax);
- LInstruction* result = new LStoreNamedGeneric(obj, instr->name(), val);
+ LStoreNamedGeneric* result = new LStoreNamedGeneric(obj, val);
return MarkAsCall(result, instr);
}
@@ -1987,8 +1773,8 @@ LInstruction* LChunkBuilder::DoFunctionLiteral(HFunctionLiteral* instr) {
LInstruction* LChunkBuilder::DoDeleteProperty(HDeleteProperty* instr) {
- LInstruction* result = new LDeleteProperty(Use(instr->object()),
- UseOrConstant(instr->key()));
+ LDeleteProperty* result = new LDeleteProperty(Use(instr->object()),
+ UseOrConstant(instr->key()));
return MarkAsCall(DefineFixed(result, eax), instr);
}
@@ -2029,13 +1815,13 @@ LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) {
LOperand* arguments = UseRegister(instr->arguments());
LOperand* length = UseTempRegister(instr->length());
LOperand* index = Use(instr->index());
- LInstruction* result = new LAccessArgumentsAt(arguments, length, index);
- return DefineAsRegister(AssignEnvironment(result));
+ LAccessArgumentsAt* result = new LAccessArgumentsAt(arguments, length, index);
+ return AssignEnvironment(DefineAsRegister(result));
}
LInstruction* LChunkBuilder::DoTypeof(HTypeof* instr) {
- LInstruction* result = new LTypeof(Use(instr->value()));
+ LTypeof* result = new LTypeof(UseAtStart(instr->value()));
return MarkAsCall(DefineFixed(result, eax), instr);
}
@@ -2059,20 +1845,13 @@ LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
env->Push(value);
}
}
-
- if (FLAG_trace_environment) {
- PrintF("Reconstructed environment ast_id=%d, instr_id=%d\n",
- instr->ast_id(),
- instr->id());
- env->PrintToStd();
- }
- ASSERT(env->values()->length() == instr->environment_height());
+ ASSERT(env->length() == instr->environment_length());
// If there is an instruction pending deoptimization environment create a
// lazy bailout instruction to capture the environment.
if (pending_deoptimization_ast_id_ == instr->ast_id()) {
- LInstruction* result = new LLazyBailout;
- result = AssignEnvironment(result);
+ LLazyBailout* lazy_bailout = new LLazyBailout;
+ LInstruction* result = AssignEnvironment(lazy_bailout);
instructions_pending_deoptimization_environment_->
set_deoptimization_environment(result->environment());
ClearInstructionPendingDeoptimizationEnvironment();
@@ -2108,21 +1887,6 @@ LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) {
}
-void LPointerMap::RecordPointer(LOperand* op) {
- // Do not record arguments as pointers.
- if (op->IsStackSlot() && op->index() < 0) return;
- ASSERT(!op->IsDoubleRegister() && !op->IsDoubleStackSlot());
- pointer_operands_.Add(op);
-}
-
-
-void LPointerMap::PrintTo(StringStream* stream) const {
- stream->Add("{");
- for (int i = 0; i < pointer_operands_.length(); ++i) {
- if (i != 0) stream->Add(";");
- pointer_operands_[i]->PrintTo(stream);
- }
- stream->Add("} @%d", position());
-}
-
} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/deps/v8/src/ia32/lithium-ia32.h b/deps/v8/src/ia32/lithium-ia32.h
index 3f48e50e22..07f0a8d90b 100644
--- a/deps/v8/src/ia32/lithium-ia32.h
+++ b/deps/v8/src/ia32/lithium-ia32.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -30,6 +30,7 @@
#include "hydrogen.h"
#include "lithium-allocator.h"
+#include "lithium.h"
#include "safepoint-table.h"
namespace v8 {
@@ -37,9 +38,6 @@ namespace internal {
// Forward declarations.
class LCodeGen;
-class LEnvironment;
-class Translation;
-class LGapNode;
// Type hierarchy:
@@ -63,6 +61,7 @@ class LGapNode;
// LDivI
// LInstanceOf
// LInstanceOfAndBranch
+// LInstanceOfKnownGlobal
// LLoadKeyedFastElement
// LLoadKeyedGeneric
// LModI
@@ -78,16 +77,20 @@ class LGapNode;
// LCallNamed
// LCallRuntime
// LCallStub
+// LCheckPrototypeMaps
// LConstant
// LConstantD
// LConstantI
// LConstantT
// LDeoptimize
// LFunctionLiteral
+// LGap
+// LLabel
// LGlobalObject
// LGlobalReceiver
-// LLabel
-// LLayzBailout
+// LGoto
+// LLazyBailout
+// LLoadContextSlot
// LLoadGlobal
// LMaterializedLiteral
// LArrayLiteral
@@ -104,19 +107,18 @@ class LGapNode;
// LStoreNamedField
// LStoreNamedGeneric
// LUnaryOperation
-// LArrayLength
// LBitNotI
// LBranch
// LCallNew
// LCheckFunction
// LCheckInstanceType
// LCheckMap
-// LCheckPrototypeMaps
// LCheckSmi
// LClassOfTest
// LClassOfTestAndBranch
// LDeleteProperty
// LDoubleToI
+// LFixedArrayLength
// LHasCachedArrayIndex
// LHasCachedArrayIndexAndBranch
// LHasInstanceType
@@ -128,8 +130,10 @@ class LGapNode;
// LIsObjectAndBranch
// LIsSmi
// LIsSmiAndBranch
+// LJSArrayLength
// LLoadNamedField
// LLoadNamedGeneric
+// LLoadFunctionPrototype
// LNumberTagD
// LNumberTagI
// LPushArgument
@@ -164,7 +168,6 @@ class LGapNode;
V(ArgumentsLength) \
V(ArithmeticD) \
V(ArithmeticT) \
- V(ArrayLength) \
V(ArrayLiteral) \
V(BitI) \
V(BitNotI) \
@@ -203,8 +206,10 @@ class LGapNode;
V(GlobalObject) \
V(GlobalReceiver) \
V(Goto) \
+ V(FixedArrayLength) \
V(InstanceOf) \
V(InstanceOfAndBranch) \
+ V(InstanceOfKnownGlobal) \
V(Integer32ToDouble) \
V(IsNull) \
V(IsNullAndBranch) \
@@ -212,6 +217,7 @@ class LGapNode;
V(IsObjectAndBranch) \
V(IsSmi) \
V(IsSmiAndBranch) \
+ V(JSArrayLength) \
V(HasInstanceType) \
V(HasInstanceTypeAndBranch) \
V(HasCachedArrayIndex) \
@@ -220,12 +226,14 @@ class LGapNode;
V(ClassOfTestAndBranch) \
V(Label) \
V(LazyBailout) \
+ V(LoadContextSlot) \
V(LoadElements) \
V(LoadGlobal) \
V(LoadKeyedFastElement) \
V(LoadKeyedGeneric) \
V(LoadNamedField) \
V(LoadNamedGeneric) \
+ V(LoadFunctionPrototype) \
V(ModI) \
V(MulI) \
V(NumberTagD) \
@@ -286,8 +294,9 @@ class LInstruction: public ZoneObject {
virtual void CompileToNative(LCodeGen* generator) = 0;
virtual const char* Mnemonic() const = 0;
- virtual void PrintTo(StringStream* stream) const;
- virtual void PrintDataTo(StringStream* stream) const { }
+ virtual void PrintTo(StringStream* stream);
+ virtual void PrintDataTo(StringStream* stream) = 0;
+ virtual void PrintOutputOperandTo(StringStream* stream) = 0;
// Declare virtual type testers.
#define DECLARE_DO(type) virtual bool Is##type() const { return false; }
@@ -303,9 +312,7 @@ class LInstruction: public ZoneObject {
LPointerMap* pointer_map() const { return pointer_map_.get(); }
bool HasPointerMap() const { return pointer_map_.is_set(); }
- void set_result(LOperand* operand) { result_.set(operand); }
- LOperand* result() const { return result_.get(); }
- bool HasResult() const { return result_.is_set(); }
+ virtual bool HasResult() const = 0;
void set_hydrogen_value(HValue* value) { hydrogen_value_ = value; }
HValue* hydrogen_value() const { return hydrogen_value_; }
@@ -323,57 +330,66 @@ class LInstruction: public ZoneObject {
private:
SetOncePointer<LEnvironment> environment_;
SetOncePointer<LPointerMap> pointer_map_;
- SetOncePointer<LOperand> result_;
HValue* hydrogen_value_;
SetOncePointer<LEnvironment> deoptimization_environment_;
};
-class LGapResolver BASE_EMBEDDED {
+template<typename T, int N>
+class OperandContainer {
public:
- LGapResolver(const ZoneList<LMoveOperands>* moves, LOperand* marker_operand);
- const ZoneList<LMoveOperands>* ResolveInReverseOrder();
-
+ OperandContainer() {
+ for (int i = 0; i < N; i++) elems_[i] = NULL;
+ }
+ int length() const { return N; }
+ T at(int i) const { return elems_[i]; }
+ void set_at(int i, T value) { elems_[i] = value; }
private:
- LGapNode* LookupNode(LOperand* operand);
- bool CanReach(LGapNode* a, LGapNode* b, int visited_id);
- bool CanReach(LGapNode* a, LGapNode* b);
- void RegisterMove(LMoveOperands move);
- void AddResultMove(LOperand* from, LOperand* to);
- void AddResultMove(LGapNode* from, LGapNode* to);
- void ResolveCycle(LGapNode* start);
-
- ZoneList<LGapNode*> nodes_;
- ZoneList<LGapNode*> identified_cycles_;
- ZoneList<LMoveOperands> result_;
- LOperand* marker_operand_;
- int next_visited_id_;
- int bailout_after_ast_id_;
+ T elems_[N];
};
-class LParallelMove : public ZoneObject {
+template<typename T>
+class OperandContainer<T, 0> {
public:
- LParallelMove() : move_operands_(4) { }
-
- void AddMove(LOperand* from, LOperand* to) {
- move_operands_.Add(LMoveOperands(from, to));
+ int length() const { return 0; }
+ T at(int i) const {
+ UNREACHABLE();
+ return NULL;
}
+ void set_at(int i, T value) {
+ UNREACHABLE();
+ }
+};
- bool IsRedundant() const;
- const ZoneList<LMoveOperands>* move_operands() const {
- return &move_operands_;
- }
+template<int R, int I, int T>
+class LTemplateInstruction: public LInstruction {
+ public:
+ // Allow 0 or 1 output operands.
+ STATIC_ASSERT(R == 0 || R == 1);
+ virtual bool HasResult() const { return R != 0; }
+ void set_result(LOperand* operand) { outputs_.set_at(0, operand); }
+ LOperand* result() const { return outputs_.at(0); }
+
+ int InputCount() const { return inputs_.length(); }
+ LOperand* InputAt(int i) const { return inputs_.at(i); }
+ void SetInputAt(int i, LOperand* operand) { inputs_.set_at(i, operand); }
- void PrintDataTo(StringStream* stream) const;
+ int TempCount() const { return temps_.length(); }
+ LOperand* TempAt(int i) const { return temps_.at(i); }
+
+ virtual void PrintDataTo(StringStream* stream);
+ virtual void PrintOutputOperandTo(StringStream* stream);
private:
- ZoneList<LMoveOperands> move_operands_;
+ OperandContainer<LOperand*, R> outputs_;
+ OperandContainer<LOperand*, I> inputs_;
+ OperandContainer<LOperand*, T> temps_;
};
-class LGap: public LInstruction {
+class LGap: public LTemplateInstruction<0, 0, 0> {
public:
explicit LGap(HBasicBlock* block)
: block_(block) {
@@ -384,7 +400,7 @@ class LGap: public LInstruction {
}
DECLARE_CONCRETE_INSTRUCTION(Gap, "gap")
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
bool IsRedundant() const;
@@ -414,13 +430,13 @@ class LGap: public LInstruction {
};
-class LGoto: public LInstruction {
+class LGoto: public LTemplateInstruction<0, 0, 0> {
public:
LGoto(int block_id, bool include_stack_check = false)
: block_id_(block_id), include_stack_check_(include_stack_check) { }
DECLARE_CONCRETE_INSTRUCTION(Goto, "goto")
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
virtual bool IsControl() const { return true; }
int block_id() const { return block_id_; }
@@ -432,7 +448,7 @@ class LGoto: public LInstruction {
};
-class LLazyBailout: public LInstruction {
+class LLazyBailout: public LTemplateInstruction<0, 0, 0> {
public:
LLazyBailout() : gap_instructions_size_(0) { }
@@ -448,7 +464,7 @@ class LLazyBailout: public LInstruction {
};
-class LDeoptimize: public LInstruction {
+class LDeoptimize: public LTemplateInstruction<0, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(Deoptimize, "deoptimize")
};
@@ -461,7 +477,7 @@ class LLabel: public LGap {
DECLARE_CONCRETE_INSTRUCTION(Label, "label")
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
int block_id() const { return block()->block_id(); }
bool is_loop_header() const { return block()->IsLoopHeader(); }
@@ -476,13 +492,13 @@ class LLabel: public LGap {
};
-class LParameter: public LInstruction {
+class LParameter: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(Parameter, "parameter")
};
-class LCallStub: public LInstruction {
+class LCallStub: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(CallStub, "call-stub")
DECLARE_HYDROGEN_ACCESSOR(CallStub)
@@ -493,96 +509,89 @@ class LCallStub: public LInstruction {
};
-class LUnknownOSRValue: public LInstruction {
+class LUnknownOSRValue: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(UnknownOSRValue, "unknown-osr-value")
};
-class LUnaryOperation: public LInstruction {
+template<int R>
+class LUnaryOperation: public LTemplateInstruction<R, 1, 0> {
public:
- explicit LUnaryOperation(LOperand* input) : input_(input) { }
-
- DECLARE_INSTRUCTION(UnaryOperation)
-
- LOperand* input() const { return input_; }
+ explicit LUnaryOperation<R>(LOperand* input) {
+ this->SetInputAt(0, input);
+ }
- virtual void PrintDataTo(StringStream* stream) const;
+ LOperand* input() const { return this->InputAt(0); }
- private:
- LOperand* input_;
+ DECLARE_INSTRUCTION(UnaryOperation)
};
-class LBinaryOperation: public LInstruction {
+template<int R>
+class LBinaryOperation: public LTemplateInstruction<R, 2, 0> {
public:
- LBinaryOperation(LOperand* left, LOperand* right)
- : left_(left), right_(right) { }
+ LBinaryOperation(LOperand* left, LOperand* right) {
+ this->SetInputAt(0, left);
+ this->SetInputAt(1, right);
+ }
DECLARE_INSTRUCTION(BinaryOperation)
- LOperand* left() const { return left_; }
- LOperand* right() const { return right_; }
- virtual void PrintDataTo(StringStream* stream) const;
-
- private:
- LOperand* left_;
- LOperand* right_;
+ LOperand* left() const { return this->InputAt(0); }
+ LOperand* right() const { return this->InputAt(1); }
};
-class LApplyArguments: public LBinaryOperation {
+class LApplyArguments: public LTemplateInstruction<1, 4, 0> {
public:
LApplyArguments(LOperand* function,
LOperand* receiver,
LOperand* length,
- LOperand* elements)
- : LBinaryOperation(function, receiver),
- length_(length),
- elements_(elements) { }
+ LOperand* elements) {
+ this->SetInputAt(0, function);
+ this->SetInputAt(1, receiver);
+ this->SetInputAt(2, length);
+ this->SetInputAt(3, elements);
+ }
DECLARE_CONCRETE_INSTRUCTION(ApplyArguments, "apply-arguments")
- LOperand* function() const { return left(); }
- LOperand* receiver() const { return right(); }
- LOperand* length() const { return length_; }
- LOperand* elements() const { return elements_; }
-
- private:
- LOperand* length_;
- LOperand* elements_;
+ LOperand* function() const { return InputAt(0); }
+ LOperand* receiver() const { return InputAt(1); }
+ LOperand* length() const { return InputAt(2); }
+ LOperand* elements() const { return InputAt(3); }
};
-class LAccessArgumentsAt: public LInstruction {
+class LAccessArgumentsAt: public LTemplateInstruction<1, 3, 0> {
public:
- LAccessArgumentsAt(LOperand* arguments, LOperand* length, LOperand* index)
- : arguments_(arguments), length_(length), index_(index) { }
+ LAccessArgumentsAt(LOperand* arguments, LOperand* length, LOperand* index) {
+ this->SetInputAt(0, arguments);
+ this->SetInputAt(1, length);
+ this->SetInputAt(2, index);
+ }
DECLARE_CONCRETE_INSTRUCTION(AccessArgumentsAt, "access-arguments-at")
- LOperand* arguments() const { return arguments_; }
- LOperand* length() const { return length_; }
- LOperand* index() const { return index_; }
+ LOperand* arguments() const { return this->InputAt(0); }
+ LOperand* length() const { return this->InputAt(1); }
+ LOperand* index() const { return this->InputAt(2); }
- virtual void PrintDataTo(StringStream* stream) const;
-
- private:
- LOperand* arguments_;
- LOperand* length_;
- LOperand* index_;
+ virtual void PrintDataTo(StringStream* stream);
};
-class LArgumentsLength: public LUnaryOperation {
+class LArgumentsLength: public LUnaryOperation<1> {
public:
- explicit LArgumentsLength(LOperand* elements) : LUnaryOperation(elements) {}
+ explicit LArgumentsLength(LOperand* elements)
+ : LUnaryOperation<1>(elements) {}
DECLARE_CONCRETE_INSTRUCTION(ArgumentsLength, "arguments-length")
};
-class LArgumentsElements: public LInstruction {
+class LArgumentsElements: public LTemplateInstruction<1, 0, 0> {
public:
LArgumentsElements() { }
@@ -590,29 +599,29 @@ class LArgumentsElements: public LInstruction {
};
-class LModI: public LBinaryOperation {
+class LModI: public LBinaryOperation<1> {
public:
- LModI(LOperand* left, LOperand* right) : LBinaryOperation(left, right) { }
+ LModI(LOperand* left, LOperand* right) : LBinaryOperation<1>(left, right) { }
DECLARE_CONCRETE_INSTRUCTION(ModI, "mod-i")
DECLARE_HYDROGEN_ACCESSOR(Mod)
};
-class LDivI: public LBinaryOperation {
+class LDivI: public LBinaryOperation<1> {
public:
LDivI(LOperand* left, LOperand* right)
- : LBinaryOperation(left, right) { }
+ : LBinaryOperation<1>(left, right) { }
DECLARE_CONCRETE_INSTRUCTION(DivI, "div-i")
DECLARE_HYDROGEN_ACCESSOR(Div)
};
-class LMulI: public LBinaryOperation {
+class LMulI: public LBinaryOperation<1> {
public:
LMulI(LOperand* left, LOperand* right, LOperand* temp)
- : LBinaryOperation(left, right), temp_(temp) { }
+ : LBinaryOperation<1>(left, right), temp_(temp) { }
DECLARE_CONCRETE_INSTRUCTION(MulI, "mul-i")
DECLARE_HYDROGEN_ACCESSOR(Mul)
@@ -624,36 +633,33 @@ class LMulI: public LBinaryOperation {
};
-class LCmpID: public LBinaryOperation {
+class LCmpID: public LBinaryOperation<1> {
public:
- LCmpID(Token::Value op, LOperand* left, LOperand* right, bool is_double)
- : LBinaryOperation(left, right), op_(op), is_double_(is_double) { }
+ LCmpID(LOperand* left, LOperand* right)
+ : LBinaryOperation<1>(left, right) { }
- Token::Value op() const { return op_; }
- bool is_double() const { return is_double_; }
+ Token::Value op() const { return hydrogen()->token(); }
+ bool is_double() const {
+ return hydrogen()->GetInputRepresentation().IsDouble();
+ }
DECLARE_CONCRETE_INSTRUCTION(CmpID, "cmp-id")
-
- private:
- Token::Value op_;
- bool is_double_;
+ DECLARE_HYDROGEN_ACCESSOR(Compare)
};
class LCmpIDAndBranch: public LCmpID {
public:
- LCmpIDAndBranch(Token::Value op,
- LOperand* left,
+ LCmpIDAndBranch(LOperand* left,
LOperand* right,
int true_block_id,
- int false_block_id,
- bool is_double)
- : LCmpID(op, left, right, is_double),
+ int false_block_id)
+ : LCmpID(left, right),
true_block_id_(true_block_id),
false_block_id_(false_block_id) { }
DECLARE_CONCRETE_INSTRUCTION(CmpIDAndBranch, "cmp-id-and-branch")
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
virtual bool IsControl() const { return true; }
int true_block_id() const { return true_block_id_; }
@@ -665,23 +671,23 @@ class LCmpIDAndBranch: public LCmpID {
};
-class LUnaryMathOperation: public LUnaryOperation {
+class LUnaryMathOperation: public LUnaryOperation<1> {
public:
explicit LUnaryMathOperation(LOperand* value)
- : LUnaryOperation(value) { }
+ : LUnaryOperation<1>(value) { }
DECLARE_CONCRETE_INSTRUCTION(UnaryMathOperation, "unary-math-operation")
DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation)
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
BuiltinFunctionId op() const { return hydrogen()->op(); }
};
-class LCmpJSObjectEq: public LBinaryOperation {
+class LCmpJSObjectEq: public LBinaryOperation<1> {
public:
LCmpJSObjectEq(LOperand* left, LOperand* right)
- : LBinaryOperation(left, right) {}
+ : LBinaryOperation<1>(left, right) {}
DECLARE_CONCRETE_INSTRUCTION(CmpJSObjectEq, "cmp-jsobject-eq")
};
@@ -709,34 +715,30 @@ class LCmpJSObjectEqAndBranch: public LCmpJSObjectEq {
};
-class LIsNull: public LUnaryOperation {
+class LIsNull: public LUnaryOperation<1> {
public:
- LIsNull(LOperand* value, bool is_strict)
- : LUnaryOperation(value), is_strict_(is_strict) {}
+ explicit LIsNull(LOperand* value) : LUnaryOperation<1>(value) { }
DECLARE_CONCRETE_INSTRUCTION(IsNull, "is-null")
+ DECLARE_HYDROGEN_ACCESSOR(IsNull)
- bool is_strict() const { return is_strict_; }
-
- private:
- bool is_strict_;
+ bool is_strict() const { return hydrogen()->is_strict(); }
};
class LIsNullAndBranch: public LIsNull {
public:
LIsNullAndBranch(LOperand* value,
- bool is_strict,
LOperand* temp,
int true_block_id,
int false_block_id)
- : LIsNull(value, is_strict),
+ : LIsNull(value),
temp_(temp),
true_block_id_(true_block_id),
false_block_id_(false_block_id) { }
DECLARE_CONCRETE_INSTRUCTION(IsNullAndBranch, "is-null-and-branch")
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
virtual bool IsControl() const { return true; }
int true_block_id() const { return true_block_id_; }
@@ -751,10 +753,10 @@ class LIsNullAndBranch: public LIsNull {
};
-class LIsObject: public LUnaryOperation {
+class LIsObject: public LUnaryOperation<1> {
public:
LIsObject(LOperand* value, LOperand* temp)
- : LUnaryOperation(value), temp_(temp) {}
+ : LUnaryOperation<1>(value), temp_(temp) {}
DECLARE_CONCRETE_INSTRUCTION(IsObject, "is-object")
@@ -778,7 +780,7 @@ class LIsObjectAndBranch: public LIsObject {
false_block_id_(false_block_id) { }
DECLARE_CONCRETE_INSTRUCTION(IsObjectAndBranch, "is-object-and-branch")
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
virtual bool IsControl() const { return true; }
int true_block_id() const { return true_block_id_; }
@@ -793,9 +795,9 @@ class LIsObjectAndBranch: public LIsObject {
};
-class LIsSmi: public LUnaryOperation {
+class LIsSmi: public LUnaryOperation<1> {
public:
- explicit LIsSmi(LOperand* value) : LUnaryOperation(value) {}
+ explicit LIsSmi(LOperand* value) : LUnaryOperation<1>(value) {}
DECLARE_CONCRETE_INSTRUCTION(IsSmi, "is-smi")
DECLARE_HYDROGEN_ACCESSOR(IsSmi)
@@ -812,7 +814,7 @@ class LIsSmiAndBranch: public LIsSmi {
false_block_id_(false_block_id) { }
DECLARE_CONCRETE_INSTRUCTION(IsSmiAndBranch, "is-smi-and-branch")
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
virtual bool IsControl() const { return true; }
int true_block_id() const { return true_block_id_; }
@@ -824,10 +826,10 @@ class LIsSmiAndBranch: public LIsSmi {
};
-class LHasInstanceType: public LUnaryOperation {
+class LHasInstanceType: public LUnaryOperation<1> {
public:
explicit LHasInstanceType(LOperand* value)
- : LUnaryOperation(value) { }
+ : LUnaryOperation<1>(value) { }
DECLARE_CONCRETE_INSTRUCTION(HasInstanceType, "has-instance-type")
DECLARE_HYDROGEN_ACCESSOR(HasInstanceType)
@@ -850,7 +852,7 @@ class LHasInstanceTypeAndBranch: public LHasInstanceType {
DECLARE_CONCRETE_INSTRUCTION(HasInstanceTypeAndBranch,
"has-instance-type-and-branch")
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
virtual bool IsControl() const { return true; }
int true_block_id() const { return true_block_id_; }
@@ -865,9 +867,9 @@ class LHasInstanceTypeAndBranch: public LHasInstanceType {
};
-class LHasCachedArrayIndex: public LUnaryOperation {
+class LHasCachedArrayIndex: public LUnaryOperation<1> {
public:
- explicit LHasCachedArrayIndex(LOperand* value) : LUnaryOperation(value) {}
+ explicit LHasCachedArrayIndex(LOperand* value) : LUnaryOperation<1>(value) {}
DECLARE_CONCRETE_INSTRUCTION(HasCachedArrayIndex, "has-cached-array-index")
DECLARE_HYDROGEN_ACCESSOR(HasCachedArrayIndex)
@@ -885,7 +887,7 @@ class LHasCachedArrayIndexAndBranch: public LHasCachedArrayIndex {
DECLARE_CONCRETE_INSTRUCTION(HasCachedArrayIndexAndBranch,
"has-cached-array-index-and-branch")
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
virtual bool IsControl() const { return true; }
int true_block_id() const { return true_block_id_; }
@@ -897,20 +899,20 @@ class LHasCachedArrayIndexAndBranch: public LHasCachedArrayIndex {
};
-class LClassOfTest: public LUnaryOperation {
+class LClassOfTest: public LUnaryOperation<1> {
public:
LClassOfTest(LOperand* value, LOperand* temp)
- : LUnaryOperation(value), temporary_(temp) {}
+ : LUnaryOperation<1>(value), temporary_(temp) {}
DECLARE_CONCRETE_INSTRUCTION(ClassOfTest, "class-of-test")
DECLARE_HYDROGEN_ACCESSOR(ClassOfTest)
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
LOperand* temporary() { return temporary_; }
private:
- LOperand *temporary_;
+ LOperand* temporary_;
};
@@ -928,7 +930,7 @@ class LClassOfTestAndBranch: public LClassOfTest {
DECLARE_CONCRETE_INSTRUCTION(ClassOfTestAndBranch,
"class-of-test-and-branch")
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
virtual bool IsControl() const { return true; }
int true_block_id() const { return true_block_id_; }
@@ -942,9 +944,9 @@ class LClassOfTestAndBranch: public LClassOfTest {
};
-class LCmpT: public LBinaryOperation {
+class LCmpT: public LBinaryOperation<1> {
public:
- LCmpT(LOperand* left, LOperand* right) : LBinaryOperation(left, right) {}
+ LCmpT(LOperand* left, LOperand* right) : LBinaryOperation<1>(left, right) {}
DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t")
DECLARE_HYDROGEN_ACCESSOR(Compare)
@@ -974,10 +976,10 @@ class LCmpTAndBranch: public LCmpT {
};
-class LInstanceOf: public LBinaryOperation {
+class LInstanceOf: public LBinaryOperation<1> {
public:
LInstanceOf(LOperand* left, LOperand* right)
- : LBinaryOperation(left, right) { }
+ : LBinaryOperation<1>(left, right) { }
DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of")
};
@@ -1004,10 +1006,27 @@ class LInstanceOfAndBranch: public LInstanceOf {
};
-class LBoundsCheck: public LBinaryOperation {
+class LInstanceOfKnownGlobal: public LUnaryOperation<1> {
+ public:
+ LInstanceOfKnownGlobal(LOperand* left, LOperand* temp)
+ : LUnaryOperation<1>(left), temp_(temp) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal,
+ "instance-of-known-global")
+ DECLARE_HYDROGEN_ACCESSOR(InstanceOfKnownGlobal)
+
+ Handle<JSFunction> function() const { return hydrogen()->function(); }
+ LOperand* temp() const { return temp_; }
+
+ private:
+ LOperand* temp_;
+};
+
+
+class LBoundsCheck: public LBinaryOperation<0> {
public:
LBoundsCheck(LOperand* index, LOperand* length)
- : LBinaryOperation(index, length) { }
+ : LBinaryOperation<0>(index, length) { }
LOperand* index() const { return left(); }
LOperand* length() const { return right(); }
@@ -1016,10 +1035,10 @@ class LBoundsCheck: public LBinaryOperation {
};
-class LBitI: public LBinaryOperation {
+class LBitI: public LBinaryOperation<1> {
public:
LBitI(Token::Value op, LOperand* left, LOperand* right)
- : LBinaryOperation(left, right), op_(op) { }
+ : LBinaryOperation<1>(left, right), op_(op) { }
Token::Value op() const { return op_; }
@@ -1030,10 +1049,10 @@ class LBitI: public LBinaryOperation {
};
-class LShiftI: public LBinaryOperation {
+class LShiftI: public LBinaryOperation<1> {
public:
LShiftI(Token::Value op, LOperand* left, LOperand* right, bool can_deopt)
- : LBinaryOperation(left, right), op_(op), can_deopt_(can_deopt) { }
+ : LBinaryOperation<1>(left, right), op_(op), can_deopt_(can_deopt) { }
Token::Value op() const { return op_; }
@@ -1047,17 +1066,17 @@ class LShiftI: public LBinaryOperation {
};
-class LSubI: public LBinaryOperation {
+class LSubI: public LBinaryOperation<1> {
public:
LSubI(LOperand* left, LOperand* right)
- : LBinaryOperation(left, right) { }
+ : LBinaryOperation<1>(left, right) { }
DECLARE_CONCRETE_INSTRUCTION(SubI, "sub-i")
DECLARE_HYDROGEN_ACCESSOR(Sub)
};
-class LConstant: public LInstruction {
+class LConstant: public LTemplateInstruction<1, 0, 0> {
DECLARE_INSTRUCTION(Constant)
};
@@ -1098,17 +1117,17 @@ class LConstantT: public LConstant {
};
-class LBranch: public LUnaryOperation {
+class LBranch: public LUnaryOperation<0> {
public:
LBranch(LOperand* input, int true_block_id, int false_block_id)
- : LUnaryOperation(input),
+ : LUnaryOperation<0>(input),
true_block_id_(true_block_id),
false_block_id_(false_block_id) { }
DECLARE_CONCRETE_INSTRUCTION(Branch, "branch")
DECLARE_HYDROGEN_ACCESSOR(Value)
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
virtual bool IsControl() const { return true; }
int true_block_id() const { return true_block_id_; }
@@ -1120,51 +1139,47 @@ class LBranch: public LUnaryOperation {
};
-class LCmpMapAndBranch: public LUnaryOperation {
+class LCmpMapAndBranch: public LUnaryOperation<0> {
public:
- LCmpMapAndBranch(LOperand* value,
- Handle<Map> map,
- int true_block_id,
- int false_block_id)
- : LUnaryOperation(value),
- map_(map),
- true_block_id_(true_block_id),
- false_block_id_(false_block_id) { }
+ explicit LCmpMapAndBranch(LOperand* value) : LUnaryOperation<0>(value) { }
DECLARE_CONCRETE_INSTRUCTION(CmpMapAndBranch, "cmp-map-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareMapAndBranch)
virtual bool IsControl() const { return true; }
- Handle<Map> map() const { return map_; }
- int true_block_id() const { return true_block_id_; }
- int false_block_id() const { return false_block_id_; }
-
- private:
- Handle<Map> map_;
- int true_block_id_;
- int false_block_id_;
+ Handle<Map> map() const { return hydrogen()->map(); }
+ int true_block_id() const {
+ return hydrogen()->true_destination()->block_id();
+ }
+ int false_block_id() const {
+ return hydrogen()->false_destination()->block_id();
+ }
};
-class LArrayLength: public LUnaryOperation {
+class LJSArrayLength: public LUnaryOperation<1> {
public:
- LArrayLength(LOperand* input, LOperand* temporary)
- : LUnaryOperation(input), temporary_(temporary) { }
+ explicit LJSArrayLength(LOperand* input) : LUnaryOperation<1>(input) { }
- LOperand* temporary() const { return temporary_; }
+ DECLARE_CONCRETE_INSTRUCTION(JSArrayLength, "js-array-length")
+ DECLARE_HYDROGEN_ACCESSOR(JSArrayLength)
+};
- DECLARE_CONCRETE_INSTRUCTION(ArrayLength, "array-length")
- DECLARE_HYDROGEN_ACCESSOR(ArrayLength)
- private:
- LOperand* temporary_;
+class LFixedArrayLength: public LUnaryOperation<1> {
+ public:
+ explicit LFixedArrayLength(LOperand* input) : LUnaryOperation<1>(input) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(FixedArrayLength, "fixed-array-length")
+ DECLARE_HYDROGEN_ACCESSOR(FixedArrayLength)
};
-class LValueOf: public LUnaryOperation {
+class LValueOf: public LUnaryOperation<1> {
public:
LValueOf(LOperand* input, LOperand* temporary)
- : LUnaryOperation(input), temporary_(temporary) { }
+ : LUnaryOperation<1>(input), temporary_(temporary) { }
LOperand* temporary() const { return temporary_; }
@@ -1176,46 +1191,46 @@ class LValueOf: public LUnaryOperation {
};
-class LThrow: public LUnaryOperation {
+class LThrow: public LUnaryOperation<0> {
public:
- explicit LThrow(LOperand* value) : LUnaryOperation(value) { }
+ explicit LThrow(LOperand* value) : LUnaryOperation<0>(value) { }
DECLARE_CONCRETE_INSTRUCTION(Throw, "throw")
};
-class LBitNotI: public LUnaryOperation {
+class LBitNotI: public LUnaryOperation<1> {
public:
- explicit LBitNotI(LOperand* use) : LUnaryOperation(use) { }
+ explicit LBitNotI(LOperand* input) : LUnaryOperation<1>(input) { }
DECLARE_CONCRETE_INSTRUCTION(BitNotI, "bit-not-i")
};
-class LAddI: public LBinaryOperation {
+class LAddI: public LBinaryOperation<1> {
public:
LAddI(LOperand* left, LOperand* right)
- : LBinaryOperation(left, right) { }
+ : LBinaryOperation<1>(left, right) { }
DECLARE_CONCRETE_INSTRUCTION(AddI, "add-i")
DECLARE_HYDROGEN_ACCESSOR(Add)
};
-class LPower: public LBinaryOperation {
+class LPower: public LBinaryOperation<1> {
public:
LPower(LOperand* left, LOperand* right)
- : LBinaryOperation(left, right) { }
+ : LBinaryOperation<1>(left, right) { }
DECLARE_CONCRETE_INSTRUCTION(Power, "power")
DECLARE_HYDROGEN_ACCESSOR(Power)
};
-class LArithmeticD: public LBinaryOperation {
+class LArithmeticD: public LBinaryOperation<1> {
public:
LArithmeticD(Token::Value op, LOperand* left, LOperand* right)
- : LBinaryOperation(left, right), op_(op) { }
+ : LBinaryOperation<1>(left, right), op_(op) { }
Token::Value op() const { return op_; }
@@ -1227,10 +1242,10 @@ class LArithmeticD: public LBinaryOperation {
};
-class LArithmeticT: public LBinaryOperation {
+class LArithmeticT: public LBinaryOperation<1> {
public:
LArithmeticT(Token::Value op, LOperand* left, LOperand* right)
- : LBinaryOperation(left, right), op_(op) { }
+ : LBinaryOperation<1>(left, right), op_(op) { }
virtual void CompileToNative(LCodeGen* generator);
virtual const char* Mnemonic() const;
@@ -1242,26 +1257,26 @@ class LArithmeticT: public LBinaryOperation {
};
-class LReturn: public LUnaryOperation {
+class LReturn: public LUnaryOperation<0> {
public:
- explicit LReturn(LOperand* use) : LUnaryOperation(use) { }
+ explicit LReturn(LOperand* use) : LUnaryOperation<0>(use) { }
DECLARE_CONCRETE_INSTRUCTION(Return, "return")
};
-class LLoadNamedField: public LUnaryOperation {
+class LLoadNamedField: public LUnaryOperation<1> {
public:
- explicit LLoadNamedField(LOperand* object) : LUnaryOperation(object) { }
+ explicit LLoadNamedField(LOperand* object) : LUnaryOperation<1>(object) { }
DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load-named-field")
DECLARE_HYDROGEN_ACCESSOR(LoadNamedField)
};
-class LLoadNamedGeneric: public LUnaryOperation {
+class LLoadNamedGeneric: public LUnaryOperation<1> {
public:
- explicit LLoadNamedGeneric(LOperand* object) : LUnaryOperation(object) { }
+ explicit LLoadNamedGeneric(LOperand* object) : LUnaryOperation<1>(object) { }
DECLARE_CONCRETE_INSTRUCTION(LoadNamedGeneric, "load-named-generic")
DECLARE_HYDROGEN_ACCESSOR(LoadNamedGeneric)
@@ -1271,38 +1286,47 @@ class LLoadNamedGeneric: public LUnaryOperation {
};
-class LLoadElements: public LUnaryOperation {
+class LLoadFunctionPrototype: public LUnaryOperation<1> {
+ public:
+ LLoadFunctionPrototype(LOperand* function, LOperand* temporary)
+ : LUnaryOperation<1>(function), temporary_(temporary) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadFunctionPrototype, "load-function-prototype")
+ DECLARE_HYDROGEN_ACCESSOR(LoadFunctionPrototype)
+
+ LOperand* function() const { return input(); }
+ LOperand* temporary() const { return temporary_; }
+
+ private:
+ LOperand* temporary_;
+};
+
+
+class LLoadElements: public LUnaryOperation<1> {
public:
- explicit LLoadElements(LOperand* obj) : LUnaryOperation(obj) { }
+ explicit LLoadElements(LOperand* obj) : LUnaryOperation<1>(obj) { }
DECLARE_CONCRETE_INSTRUCTION(LoadElements, "load-elements")
};
-class LLoadKeyedFastElement: public LBinaryOperation {
+class LLoadKeyedFastElement: public LBinaryOperation<1> {
public:
- LLoadKeyedFastElement(LOperand* elements,
- LOperand* key,
- LOperand* load_result)
- : LBinaryOperation(elements, key),
- load_result_(load_result) { }
+ LLoadKeyedFastElement(LOperand* elements, LOperand* key)
+ : LBinaryOperation<1>(elements, key) { }
DECLARE_CONCRETE_INSTRUCTION(LoadKeyedFastElement, "load-keyed-fast-element")
DECLARE_HYDROGEN_ACCESSOR(LoadKeyedFastElement)
LOperand* elements() const { return left(); }
LOperand* key() const { return right(); }
- LOperand* load_result() const { return load_result_; }
-
- private:
- LOperand* load_result_;
};
-class LLoadKeyedGeneric: public LBinaryOperation {
+class LLoadKeyedGeneric: public LBinaryOperation<1> {
public:
LLoadKeyedGeneric(LOperand* obj, LOperand* key)
- : LBinaryOperation(obj, key) { }
+ : LBinaryOperation<1>(obj, key) { }
DECLARE_CONCRETE_INSTRUCTION(LoadKeyedGeneric, "load-keyed-generic")
@@ -1311,78 +1335,92 @@ class LLoadKeyedGeneric: public LBinaryOperation {
};
-class LLoadGlobal: public LInstruction {
+class LLoadGlobal: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(LoadGlobal, "load-global")
DECLARE_HYDROGEN_ACCESSOR(LoadGlobal)
};
-class LStoreGlobal: public LUnaryOperation {
+class LStoreGlobal: public LUnaryOperation<0> {
public:
- explicit LStoreGlobal(LOperand* value) : LUnaryOperation(value) {}
+ explicit LStoreGlobal(LOperand* value) : LUnaryOperation<0>(value) {}
DECLARE_CONCRETE_INSTRUCTION(StoreGlobal, "store-global")
DECLARE_HYDROGEN_ACCESSOR(StoreGlobal)
};
-class LPushArgument: public LUnaryOperation {
+class LLoadContextSlot: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(LoadContextSlot, "load-context-slot")
+ DECLARE_HYDROGEN_ACCESSOR(LoadContextSlot)
+
+ int context_chain_length() const {
+ return hydrogen()->context_chain_length();
+ }
+ int slot_index() const { return hydrogen()->slot_index(); }
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LPushArgument: public LUnaryOperation<0> {
public:
- explicit LPushArgument(LOperand* argument) : LUnaryOperation(argument) {}
+ explicit LPushArgument(LOperand* argument) : LUnaryOperation<0>(argument) {}
DECLARE_CONCRETE_INSTRUCTION(PushArgument, "push-argument")
};
-class LGlobalObject: public LInstruction {
+class LGlobalObject: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(GlobalObject, "global-object")
};
-class LGlobalReceiver: public LInstruction {
+class LGlobalReceiver: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(GlobalReceiver, "global-receiver")
};
-class LCallConstantFunction: public LInstruction {
+class LCallConstantFunction: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(CallConstantFunction, "call-constant-function")
DECLARE_HYDROGEN_ACCESSOR(CallConstantFunction)
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
- Handle<JSFunction> function() const { return hydrogen()->function(); }
+ Handle<JSFunction> function() { return hydrogen()->function(); }
int arity() const { return hydrogen()->argument_count() - 1; }
};
-class LCallKeyed: public LInstruction {
+class LCallKeyed: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(CallKeyed, "call-keyed")
DECLARE_HYDROGEN_ACCESSOR(CallKeyed)
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
int arity() const { return hydrogen()->argument_count() - 1; }
};
-class LCallNamed: public LInstruction {
+class LCallNamed: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(CallNamed, "call-named")
DECLARE_HYDROGEN_ACCESSOR(CallNamed)
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
Handle<String> name() const { return hydrogen()->name(); }
int arity() const { return hydrogen()->argument_count() - 1; }
};
-class LCallFunction: public LInstruction {
+class LCallFunction: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(CallFunction, "call-function")
DECLARE_HYDROGEN_ACCESSOR(CallFunction)
@@ -1391,44 +1429,44 @@ class LCallFunction: public LInstruction {
};
-class LCallGlobal: public LInstruction {
+class LCallGlobal: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(CallGlobal, "call-global")
DECLARE_HYDROGEN_ACCESSOR(CallGlobal)
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
Handle<String> name() const {return hydrogen()->name(); }
int arity() const { return hydrogen()->argument_count() - 1; }
};
-class LCallKnownGlobal: public LInstruction {
+class LCallKnownGlobal: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(CallKnownGlobal, "call-known-global")
DECLARE_HYDROGEN_ACCESSOR(CallKnownGlobal)
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
Handle<JSFunction> target() const { return hydrogen()->target(); }
int arity() const { return hydrogen()->argument_count() - 1; }
};
-class LCallNew: public LUnaryOperation {
+class LCallNew: public LUnaryOperation<1> {
public:
- explicit LCallNew(LOperand* constructor) : LUnaryOperation(constructor) { }
+ explicit LCallNew(LOperand* constructor) : LUnaryOperation<1>(constructor) { }
DECLARE_CONCRETE_INSTRUCTION(CallNew, "call-new")
DECLARE_HYDROGEN_ACCESSOR(CallNew)
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
int arity() const { return hydrogen()->argument_count() - 1; }
};
-class LCallRuntime: public LInstruction {
+class LCallRuntime: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(CallRuntime, "call-runtime")
DECLARE_HYDROGEN_ACCESSOR(CallRuntime)
@@ -1438,26 +1476,26 @@ class LCallRuntime: public LInstruction {
};
-class LInteger32ToDouble: public LUnaryOperation {
+class LInteger32ToDouble: public LUnaryOperation<1> {
public:
- explicit LInteger32ToDouble(LOperand* use) : LUnaryOperation(use) { }
+ explicit LInteger32ToDouble(LOperand* use) : LUnaryOperation<1>(use) { }
DECLARE_CONCRETE_INSTRUCTION(Integer32ToDouble, "int32-to-double")
};
-class LNumberTagI: public LUnaryOperation {
+class LNumberTagI: public LUnaryOperation<1> {
public:
- explicit LNumberTagI(LOperand* use) : LUnaryOperation(use) { }
+ explicit LNumberTagI(LOperand* use) : LUnaryOperation<1>(use) { }
DECLARE_CONCRETE_INSTRUCTION(NumberTagI, "number-tag-i")
};
-class LNumberTagD: public LUnaryOperation {
+class LNumberTagD: public LUnaryOperation<1> {
public:
explicit LNumberTagD(LOperand* value, LOperand* temp)
- : LUnaryOperation(value), temp_(temp) { }
+ : LUnaryOperation<1>(value), temp_(temp) { }
DECLARE_CONCRETE_INSTRUCTION(NumberTagD, "number-tag-d")
@@ -1469,22 +1507,27 @@ class LNumberTagD: public LUnaryOperation {
// Sometimes truncating conversion from a tagged value to an int32.
-class LDoubleToI: public LUnaryOperation {
+class LDoubleToI: public LUnaryOperation<1> {
public:
- explicit LDoubleToI(LOperand* value) : LUnaryOperation(value) { }
+ LDoubleToI(LOperand* value, LOperand* temporary)
+ : LUnaryOperation<1>(value), temporary_(temporary) { }
DECLARE_CONCRETE_INSTRUCTION(DoubleToI, "double-to-i")
DECLARE_HYDROGEN_ACCESSOR(Change)
bool truncating() { return hydrogen()->CanTruncateToInt32(); }
+ LOperand* temporary() const { return temporary_; }
+
+ private:
+ LOperand* temporary_;
};
// Truncating conversion from a tagged value to an int32.
-class LTaggedToI: public LUnaryOperation {
+class LTaggedToI: public LUnaryOperation<1> {
public:
LTaggedToI(LOperand* value, LOperand* temp)
- : LUnaryOperation(value), temp_(temp) { }
+ : LUnaryOperation<1>(value), temp_(temp) { }
DECLARE_CONCRETE_INSTRUCTION(TaggedToI, "tagged-to-i")
DECLARE_HYDROGEN_ACCESSOR(Change)
@@ -1497,26 +1540,26 @@ class LTaggedToI: public LUnaryOperation {
};
-class LSmiTag: public LUnaryOperation {
+class LSmiTag: public LUnaryOperation<1> {
public:
- explicit LSmiTag(LOperand* use) : LUnaryOperation(use) { }
+ explicit LSmiTag(LOperand* use) : LUnaryOperation<1>(use) { }
DECLARE_CONCRETE_INSTRUCTION(SmiTag, "smi-tag")
};
-class LNumberUntagD: public LUnaryOperation {
+class LNumberUntagD: public LUnaryOperation<1> {
public:
- explicit LNumberUntagD(LOperand* value) : LUnaryOperation(value) { }
+ explicit LNumberUntagD(LOperand* value) : LUnaryOperation<1>(value) { }
DECLARE_CONCRETE_INSTRUCTION(NumberUntagD, "double-untag")
};
-class LSmiUntag: public LUnaryOperation {
+class LSmiUntag: public LUnaryOperation<1> {
public:
LSmiUntag(LOperand* use, bool needs_check)
- : LUnaryOperation(use), needs_check_(needs_check) { }
+ : LUnaryOperation<1>(use), needs_check_(needs_check) { }
DECLARE_CONCRETE_INSTRUCTION(SmiUntag, "smi-untag")
@@ -1527,89 +1570,69 @@ class LSmiUntag: public LUnaryOperation {
};
-class LStoreNamed: public LInstruction {
+class LStoreNamed: public LTemplateInstruction<0, 2, 0> {
public:
- LStoreNamed(LOperand* obj, Handle<Object> name, LOperand* val)
- : object_(obj), name_(name), value_(val) { }
+ LStoreNamed(LOperand* obj, LOperand* val) {
+ this->SetInputAt(0, obj);
+ this->SetInputAt(1, val);
+ }
DECLARE_INSTRUCTION(StoreNamed)
+ DECLARE_HYDROGEN_ACCESSOR(StoreNamed)
- virtual void PrintDataTo(StringStream* stream) const;
-
- LOperand* object() const { return object_; }
- Handle<Object> name() const { return name_; }
- LOperand* value() const { return value_; }
+ virtual void PrintDataTo(StringStream* stream);
- private:
- LOperand* object_;
- Handle<Object> name_;
- LOperand* value_;
+ LOperand* object() const { return this->InputAt(0); }
+ LOperand* value() const { return this->InputAt(1); }
+ Handle<Object> name() const { return hydrogen()->name(); }
};
class LStoreNamedField: public LStoreNamed {
public:
- LStoreNamedField(LOperand* obj,
- Handle<Object> name,
- LOperand* val,
- bool in_object,
- int offset,
- LOperand* temp,
- bool needs_write_barrier,
- Handle<Map> transition)
- : LStoreNamed(obj, name, val),
- is_in_object_(in_object),
- offset_(offset),
- temp_(temp),
- needs_write_barrier_(needs_write_barrier),
- transition_(transition) { }
+ LStoreNamedField(LOperand* obj, LOperand* val, LOperand* temp)
+ : LStoreNamed(obj, val), temp_(temp) { }
DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field")
+ DECLARE_HYDROGEN_ACCESSOR(StoreNamedField)
+
+ bool is_in_object() { return hydrogen()->is_in_object(); }
+ int offset() { return hydrogen()->offset(); }
+ bool needs_write_barrier() { return hydrogen()->NeedsWriteBarrier(); }
+ Handle<Map> transition() const { return hydrogen()->transition(); }
- bool is_in_object() { return is_in_object_; }
- int offset() { return offset_; }
LOperand* temp() { return temp_; }
- bool needs_write_barrier() { return needs_write_barrier_; }
- Handle<Map> transition() const { return transition_; }
- void set_transition(Handle<Map> map) { transition_ = map; }
private:
- bool is_in_object_;
- int offset_;
LOperand* temp_;
- bool needs_write_barrier_;
- Handle<Map> transition_;
};
class LStoreNamedGeneric: public LStoreNamed {
public:
- LStoreNamedGeneric(LOperand* obj,
- Handle<Object> name,
- LOperand* val)
- : LStoreNamed(obj, name, val) { }
+ LStoreNamedGeneric(LOperand* obj, LOperand* val)
+ : LStoreNamed(obj, val) { }
DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric, "store-named-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreNamedGeneric)
};
-class LStoreKeyed: public LInstruction {
+class LStoreKeyed: public LTemplateInstruction<0, 3, 0> {
public:
- LStoreKeyed(LOperand* obj, LOperand* key, LOperand* val)
- : object_(obj), key_(key), value_(val) { }
+ LStoreKeyed(LOperand* obj, LOperand* key, LOperand* val) {
+ this->SetInputAt(0, obj);
+ this->SetInputAt(1, key);
+ this->SetInputAt(2, val);
+ }
DECLARE_INSTRUCTION(StoreKeyed)
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
- LOperand* object() const { return object_; }
- LOperand* key() const { return key_; }
- LOperand* value() const { return value_; }
-
- private:
- LOperand* object_;
- LOperand* key_;
- LOperand* value_;
+ LOperand* object() const { return this->InputAt(0); }
+ LOperand* key() const { return this->InputAt(1); }
+ LOperand* value() const { return this->InputAt(2); }
};
@@ -1633,19 +1656,19 @@ class LStoreKeyedGeneric: public LStoreKeyed {
};
-class LCheckFunction: public LUnaryOperation {
+class LCheckFunction: public LUnaryOperation<0> {
public:
- explicit LCheckFunction(LOperand* use) : LUnaryOperation(use) { }
+ explicit LCheckFunction(LOperand* use) : LUnaryOperation<0>(use) { }
DECLARE_CONCRETE_INSTRUCTION(CheckFunction, "check-function")
DECLARE_HYDROGEN_ACCESSOR(CheckFunction)
};
-class LCheckInstanceType: public LUnaryOperation {
+class LCheckInstanceType: public LUnaryOperation<0> {
public:
LCheckInstanceType(LOperand* use, LOperand* temp)
- : LUnaryOperation(use), temp_(temp) { }
+ : LUnaryOperation<0>(use), temp_(temp) { }
DECLARE_CONCRETE_INSTRUCTION(CheckInstanceType, "check-instance-type")
DECLARE_HYDROGEN_ACCESSOR(CheckInstanceType)
@@ -1657,41 +1680,36 @@ class LCheckInstanceType: public LUnaryOperation {
};
-class LCheckMap: public LUnaryOperation {
+class LCheckMap: public LUnaryOperation<0> {
public:
- explicit LCheckMap(LOperand* use) : LUnaryOperation(use) { }
+ explicit LCheckMap(LOperand* use) : LUnaryOperation<0>(use) { }
DECLARE_CONCRETE_INSTRUCTION(CheckMap, "check-map")
DECLARE_HYDROGEN_ACCESSOR(CheckMap)
};
-class LCheckPrototypeMaps: public LInstruction {
+class LCheckPrototypeMaps: public LTemplateInstruction<0, 0, 0> {
public:
- LCheckPrototypeMaps(LOperand* temp,
- Handle<JSObject> holder,
- Handle<Map> receiver_map)
- : temp_(temp),
- holder_(holder),
- receiver_map_(receiver_map) { }
+ explicit LCheckPrototypeMaps(LOperand* temp) : temp_(temp) { }
DECLARE_CONCRETE_INSTRUCTION(CheckPrototypeMaps, "check-prototype-maps")
+ DECLARE_HYDROGEN_ACCESSOR(CheckPrototypeMaps)
+
+ Handle<JSObject> prototype() const { return hydrogen()->prototype(); }
+ Handle<JSObject> holder() const { return hydrogen()->holder(); }
LOperand* temp() const { return temp_; }
- Handle<JSObject> holder() const { return holder_; }
- Handle<Map> receiver_map() const { return receiver_map_; }
private:
LOperand* temp_;
- Handle<JSObject> holder_;
- Handle<Map> receiver_map_;
};
-class LCheckSmi: public LUnaryOperation {
+class LCheckSmi: public LUnaryOperation<0> {
public:
LCheckSmi(LOperand* use, Condition condition)
- : LUnaryOperation(use), condition_(condition) { }
+ : LUnaryOperation<0>(use), condition_(condition) { }
Condition condition() const { return condition_; }
@@ -1705,7 +1723,7 @@ class LCheckSmi: public LUnaryOperation {
};
-class LMaterializedLiteral: public LInstruction {
+class LMaterializedLiteral: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_INSTRUCTION(MaterializedLiteral)
};
@@ -1732,7 +1750,7 @@ class LRegExpLiteral: public LMaterializedLiteral {
};
-class LFunctionLiteral: public LInstruction {
+class LFunctionLiteral: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(FunctionLiteral, "function-literal")
DECLARE_HYDROGEN_ACCESSOR(FunctionLiteral)
@@ -1741,18 +1759,18 @@ class LFunctionLiteral: public LInstruction {
};
-class LTypeof: public LUnaryOperation {
+class LTypeof: public LUnaryOperation<1> {
public:
- explicit LTypeof(LOperand* input) : LUnaryOperation(input) { }
+ explicit LTypeof(LOperand* input) : LUnaryOperation<1>(input) { }
DECLARE_CONCRETE_INSTRUCTION(Typeof, "typeof")
};
-class LTypeofIs: public LUnaryOperation {
+class LTypeofIs: public LUnaryOperation<1> {
public:
- explicit LTypeofIs(LOperand* input) : LUnaryOperation(input) { }
- virtual void PrintDataTo(StringStream* stream) const;
+ explicit LTypeofIs(LOperand* input) : LUnaryOperation<1>(input) { }
+ virtual void PrintDataTo(StringStream* stream);
DECLARE_CONCRETE_INSTRUCTION(TypeofIs, "typeof-is")
DECLARE_HYDROGEN_ACCESSOR(TypeofIs)
@@ -1772,7 +1790,7 @@ class LTypeofIsAndBranch: public LTypeofIs {
DECLARE_CONCRETE_INSTRUCTION(TypeofIsAndBranch, "typeof-is-and-branch")
- virtual void PrintDataTo(StringStream* stream) const;
+ virtual void PrintDataTo(StringStream* stream);
virtual bool IsControl() const { return true; }
int true_block_id() const { return true_block_id_; }
@@ -1784,9 +1802,10 @@ class LTypeofIsAndBranch: public LTypeofIs {
};
-class LDeleteProperty: public LBinaryOperation {
+class LDeleteProperty: public LBinaryOperation<1> {
public:
- LDeleteProperty(LOperand* obj, LOperand* key) : LBinaryOperation(obj, key) {}
+ LDeleteProperty(LOperand* obj, LOperand* key)
+ : LBinaryOperation<1>(obj, key) { }
DECLARE_CONCRETE_INSTRUCTION(DeleteProperty, "delete-property")
@@ -1795,7 +1814,7 @@ class LDeleteProperty: public LBinaryOperation {
};
-class LOsrEntry: public LInstruction {
+class LOsrEntry: public LTemplateInstruction<0, 0, 0> {
public:
LOsrEntry();
@@ -1818,118 +1837,21 @@ class LOsrEntry: public LInstruction {
};
-class LStackCheck: public LInstruction {
+class LStackCheck: public LTemplateInstruction<0, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(StackCheck, "stack-check")
};
-class LPointerMap: public ZoneObject {
- public:
- explicit LPointerMap(int position)
- : pointer_operands_(8), position_(position), lithium_position_(-1) { }
-
- const ZoneList<LOperand*>* operands() const { return &pointer_operands_; }
- int position() const { return position_; }
- int lithium_position() const { return lithium_position_; }
-
- void set_lithium_position(int pos) {
- ASSERT(lithium_position_ == -1);
- lithium_position_ = pos;
- }
-
- void RecordPointer(LOperand* op);
- void PrintTo(StringStream* stream) const;
-
- private:
- ZoneList<LOperand*> pointer_operands_;
- int position_;
- int lithium_position_;
-};
-
-
-class LEnvironment: public ZoneObject {
- public:
- LEnvironment(Handle<JSFunction> closure,
- int ast_id,
- int parameter_count,
- int argument_count,
- int value_count,
- LEnvironment* outer)
- : closure_(closure),
- arguments_stack_height_(argument_count),
- deoptimization_index_(Safepoint::kNoDeoptimizationIndex),
- translation_index_(-1),
- ast_id_(ast_id),
- parameter_count_(parameter_count),
- values_(value_count),
- representations_(value_count),
- spilled_registers_(NULL),
- spilled_double_registers_(NULL),
- outer_(outer) {
- }
-
- Handle<JSFunction> closure() const { return closure_; }
- int arguments_stack_height() const { return arguments_stack_height_; }
- int deoptimization_index() const { return deoptimization_index_; }
- int translation_index() const { return translation_index_; }
- int ast_id() const { return ast_id_; }
- int parameter_count() const { return parameter_count_; }
- const ZoneList<LOperand*>* values() const { return &values_; }
- LEnvironment* outer() const { return outer_; }
-
- void AddValue(LOperand* operand, Representation representation) {
- values_.Add(operand);
- representations_.Add(representation);
- }
-
- bool HasTaggedValueAt(int index) const {
- return representations_[index].IsTagged();
- }
-
- void Register(int deoptimization_index, int translation_index) {
- ASSERT(!HasBeenRegistered());
- deoptimization_index_ = deoptimization_index;
- translation_index_ = translation_index;
- }
- bool HasBeenRegistered() const {
- return deoptimization_index_ != Safepoint::kNoDeoptimizationIndex;
- }
-
- void SetSpilledRegisters(LOperand** registers,
- LOperand** double_registers) {
- spilled_registers_ = registers;
- spilled_double_registers_ = double_registers;
- }
-
- // Emit frame translation commands for this environment.
- void WriteTranslation(LCodeGen* cgen, Translation* translation) const;
-
- void PrintTo(StringStream* stream) const;
-
- private:
- Handle<JSFunction> closure_;
- int arguments_stack_height_;
- int deoptimization_index_;
- int translation_index_;
- int ast_id_;
- int parameter_count_;
- ZoneList<LOperand*> values_;
- ZoneList<Representation> representations_;
-
- // Allocation index indexed arrays of spill slot operands for registers
- // that are also in spill slots at an OSR entry. NULL for environments
- // that do not correspond to an OSR entry.
- LOperand** spilled_registers_;
- LOperand** spilled_double_registers_;
-
- LEnvironment* outer_;
-};
-
class LChunkBuilder;
class LChunk: public ZoneObject {
public:
- explicit LChunk(HGraph* graph);
+ explicit LChunk(HGraph* graph)
+ : spill_slot_count_(0),
+ graph_(graph),
+ instructions_(32),
+ pointer_maps_(8),
+ inlined_closures_(1) { }
int AddInstruction(LInstruction* instruction, HBasicBlock* block);
LConstantOperand* DefineConstantOperand(HConstant* constant);
@@ -1976,8 +1898,6 @@ class LChunk: public ZoneObject {
inlined_closures_.Add(closure);
}
- void Verify() const;
-
private:
int spill_slot_count_;
HGraph* const graph_;
@@ -2060,14 +1980,24 @@ class LChunkBuilder BASE_EMBEDDED {
// Methods for setting up define-use relationships.
// Return the same instruction that they are passed.
- LInstruction* Define(LInstruction* instr, LUnallocated* result);
- LInstruction* Define(LInstruction* instr);
- LInstruction* DefineAsRegister(LInstruction* instr);
- LInstruction* DefineAsSpilled(LInstruction* instr, int index);
- LInstruction* DefineSameAsAny(LInstruction* instr);
- LInstruction* DefineSameAsFirst(LInstruction* instr);
- LInstruction* DefineFixed(LInstruction* instr, Register reg);
- LInstruction* DefineFixedDouble(LInstruction* instr, XMMRegister reg);
+ template<int I, int T>
+ LInstruction* Define(LTemplateInstruction<1, I, T>* instr,
+ LUnallocated* result);
+ template<int I, int T>
+ LInstruction* Define(LTemplateInstruction<1, I, T>* instr);
+ template<int I, int T>
+ LInstruction* DefineAsRegister(LTemplateInstruction<1, I, T>* instr);
+ template<int I, int T>
+ LInstruction* DefineAsSpilled(LTemplateInstruction<1, I, T>* instr,
+ int index);
+ template<int I, int T>
+ LInstruction* DefineSameAsFirst(LTemplateInstruction<1, I, T>* instr);
+ template<int I, int T>
+ LInstruction* DefineFixed(LTemplateInstruction<1, I, T>* instr,
+ Register reg);
+ template<int I, int T>
+ LInstruction* DefineFixedDouble(LTemplateInstruction<1, I, T>* instr,
+ XMMRegister reg);
LInstruction* AssignEnvironment(LInstruction* instr);
LInstruction* AssignPointerMap(LInstruction* instr);
@@ -2080,6 +2010,7 @@ class LChunkBuilder BASE_EMBEDDED {
LInstruction* instr,
HInstruction* hinstr,
CanDeoptimize can_deoptimize = CANNOT_DEOPTIMIZE_EAGERLY);
+ LInstruction* MarkAsSaveDoubles(LInstruction* instr);
LInstruction* SetInstructionPendingDeoptimizationEnvironment(
LInstruction* instr, int ast_id);
@@ -2087,8 +2018,6 @@ class LChunkBuilder BASE_EMBEDDED {
LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env);
- // Temporary operand that may be a memory location.
- LOperand* Temp();
// Temporary operand that must be in a register.
LUnallocated* TempRegister();
LOperand* FixedTemp(Register reg);
diff --git a/deps/v8/src/ia32/macro-assembler-ia32.cc b/deps/v8/src/ia32/macro-assembler-ia32.cc
index 7c33906527..10c942a5b3 100644
--- a/deps/v8/src/ia32/macro-assembler-ia32.cc
+++ b/deps/v8/src/ia32/macro-assembler-ia32.cc
@@ -877,55 +877,53 @@ void MacroAssembler::AllocateAsciiConsString(Register result,
Immediate(Factory::cons_ascii_string_map()));
}
-// All registers must be distinct. Only current_string needs valid contents
-// on entry. All registers may be invalid on exit. result_operand is
-// unchanged, padding_chars is updated correctly.
-void MacroAssembler::AppendStringToTopOfNewSpace(
- Register current_string, // Tagged pointer to string to copy.
- Register current_string_length,
- Register result_pos,
- Register scratch,
- Register new_padding_chars,
- Operand operand_result,
- Operand operand_padding_chars,
- Label* bailout) {
- mov(current_string_length,
- FieldOperand(current_string, String::kLengthOffset));
- shr(current_string_length, 1);
- sub(current_string_length, operand_padding_chars);
- mov(new_padding_chars, current_string_length);
- add(Operand(current_string_length), Immediate(kObjectAlignmentMask));
- and_(Operand(current_string_length), Immediate(~kObjectAlignmentMask));
- sub(new_padding_chars, Operand(current_string_length));
- neg(new_padding_chars);
- // We need an allocation even if current_string_length is 0, to fetch
- // result_pos. Consider using a faster fetch of result_pos in that case.
- AllocateInNewSpace(current_string_length, result_pos, scratch, no_reg,
- bailout, NO_ALLOCATION_FLAGS);
- sub(result_pos, operand_padding_chars);
- mov(operand_padding_chars, new_padding_chars);
-
- Register scratch_2 = new_padding_chars; // Used to compute total length.
- // Copy string to the end of result.
- mov(current_string_length,
- FieldOperand(current_string, String::kLengthOffset));
- mov(scratch, operand_result);
- mov(scratch_2, current_string_length);
- add(scratch_2, FieldOperand(scratch, String::kLengthOffset));
- mov(FieldOperand(scratch, String::kLengthOffset), scratch_2);
- shr(current_string_length, 1);
- lea(current_string,
- FieldOperand(current_string, SeqAsciiString::kHeaderSize));
- // Loop condition: while (--current_string_length >= 0).
- Label copy_loop;
- Label copy_loop_entry;
- jmp(&copy_loop_entry);
- bind(&copy_loop);
- mov_b(scratch, Operand(current_string, current_string_length, times_1, 0));
- mov_b(Operand(result_pos, current_string_length, times_1, 0), scratch);
- bind(&copy_loop_entry);
- sub(Operand(current_string_length), Immediate(1));
- j(greater_equal, &copy_loop);
+
+// Copy memory, byte-by-byte, from source to destination. Not optimized for
+// long or aligned copies. The contents of scratch and length are destroyed.
+// Source and destination are incremented by length.
+// Many variants of movsb, loop unrolling, word moves, and indexed operands
+// have been tried here already, and this is fastest.
+// A simpler loop is faster on small copies, but 30% slower on large ones.
+// The cld() instruction must have been emitted, to set the direction flag(),
+// before calling this function.
+void MacroAssembler::CopyBytes(Register source,
+ Register destination,
+ Register length,
+ Register scratch) {
+ Label loop, done, short_string, short_loop;
+ // Experimentation shows that the short string loop is faster if length < 10.
+ cmp(Operand(length), Immediate(10));
+ j(less_equal, &short_string);
+
+ ASSERT(source.is(esi));
+ ASSERT(destination.is(edi));
+ ASSERT(length.is(ecx));
+
+ // Because source is 4-byte aligned in our uses of this function,
+ // we keep source aligned for the rep_movs call by copying the odd bytes
+ // at the end of the ranges.
+ mov(scratch, Operand(source, length, times_1, -4));
+ mov(Operand(destination, length, times_1, -4), scratch);
+ mov(scratch, ecx);
+ shr(ecx, 2);
+ rep_movs();
+ and_(Operand(scratch), Immediate(0x3));
+ add(destination, Operand(scratch));
+ jmp(&done);
+
+ bind(&short_string);
+ test(length, Operand(length));
+ j(zero, &done);
+
+ bind(&short_loop);
+ mov_b(scratch, Operand(source, 0));
+ mov_b(Operand(destination, 0), scratch);
+ inc(source);
+ inc(destination);
+ dec(length);
+ j(not_zero, &short_loop);
+
+ bind(&done);
}
@@ -1715,7 +1713,7 @@ void MacroAssembler::Abort(const char* msg) {
}
#endif
// Disable stub call restrictions to always allow calls to abort.
- set_allow_stub_calls(true);
+ AllowStubCallsScope allow_scope(this, true);
push(eax);
push(Immediate(p0));
diff --git a/deps/v8/src/ia32/macro-assembler-ia32.h b/deps/v8/src/ia32/macro-assembler-ia32.h
index 6f5fa87297..6f180c6c2b 100644
--- a/deps/v8/src/ia32/macro-assembler-ia32.h
+++ b/deps/v8/src/ia32/macro-assembler-ia32.h
@@ -386,22 +386,13 @@ class MacroAssembler: public Assembler {
Register scratch2,
Label* gc_required);
- // All registers must be distinct. Only current_string needs valid contents
- // on entry. All registers may be invalid on exit. result_operand is
- // unchanged, padding_chars is updated correctly.
- // The top of new space must contain a sequential ascii string with
- // padding_chars bytes free in its top word. The sequential ascii string
- // current_string is concatenated to it, allocating the necessary amount
- // of new memory.
- void AppendStringToTopOfNewSpace(
- Register current_string, // Tagged pointer to string to copy.
- Register current_string_length,
- Register result_pos,
- Register scratch,
- Register new_padding_chars,
- Operand operand_result,
- Operand operand_padding_chars,
- Label* bailout);
+ // Copy memory, byte-by-byte, from source to destination. Not optimized for
+ // long or aligned copies.
+ // The contents of index and scratch are destroyed.
+ void CopyBytes(Register source,
+ Register destination,
+ Register length,
+ Register scratch);
// ---------------------------------------------------------------------------
// Support functions.
diff --git a/deps/v8/src/ia32/regexp-macro-assembler-ia32.cc b/deps/v8/src/ia32/regexp-macro-assembler-ia32.cc
index d435a70775..1213448841 100644
--- a/deps/v8/src/ia32/regexp-macro-assembler-ia32.cc
+++ b/deps/v8/src/ia32/regexp-macro-assembler-ia32.cc
@@ -211,9 +211,7 @@ void RegExpMacroAssemblerIA32::CheckCharacters(Vector<const uc16> str,
// If input is ASCII, don't even bother calling here if the string to
// match contains a non-ascii character.
if (mode_ == ASCII) {
- for (int i = 0; i < str.length(); i++) {
- ASSERT(str[i] <= String::kMaxAsciiCharCodeU);
- }
+ ASSERT(String::IsAscii(str.start(), str.length()));
}
#endif
int byte_length = str.length() * char_size();
@@ -654,7 +652,7 @@ bool RegExpMacroAssemblerIA32::CheckSpecialCharacterClass(uc16 type,
void RegExpMacroAssemblerIA32::Fail() {
ASSERT(FAILURE == 0); // Return value for failure is zero.
- __ xor_(eax, Operand(eax)); // zero eax.
+ __ Set(eax, Immediate(0));
__ jmp(&exit_label_);
}
diff --git a/deps/v8/src/ia32/stub-cache-ia32.cc b/deps/v8/src/ia32/stub-cache-ia32.cc
index 99888b0898..bcb02ed797 100644
--- a/deps/v8/src/ia32/stub-cache-ia32.cc
+++ b/deps/v8/src/ia32/stub-cache-ia32.cc
@@ -1686,6 +1686,7 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
Label miss;
Label index_out_of_range;
+
GenerateNameCheck(name, &miss);
// Check that the maps starting from the prototype haven't changed.
diff --git a/deps/v8/src/json.js b/deps/v8/src/json.js
index 89009a9630..e90d5d1d0a 100644
--- a/deps/v8/src/json.js
+++ b/deps/v8/src/json.js
@@ -27,11 +27,6 @@
var $JSON = global.JSON;
-function ParseJSONUnfiltered(text) {
- var s = $String(text);
- return %ParseJson(s);
-}
-
function Revive(holder, name, reviver) {
var val = holder[name];
if (IS_OBJECT(val)) {
@@ -58,7 +53,7 @@ function Revive(holder, name, reviver) {
}
function JSONParse(text, reviver) {
- var unfiltered = ParseJSONUnfiltered(text);
+ var unfiltered = %ParseJson(TO_STRING_INLINE(text));
if (IS_FUNCTION(reviver)) {
return Revive({'': unfiltered}, '', reviver);
} else {
@@ -158,7 +153,7 @@ function JSONSerialize(key, holder, replacer, stack, indent, gap) {
if (IS_STRING(value)) {
return %QuoteJSONString(value);
} else if (IS_NUMBER(value)) {
- return $isFinite(value) ? $String(value) : "null";
+ return NUMBER_IS_FINITE(value) ? $String(value) : "null";
} else if (IS_BOOLEAN(value)) {
return value ? "true" : "false";
} else if (IS_NULL(value)) {
@@ -169,7 +164,7 @@ function JSONSerialize(key, holder, replacer, stack, indent, gap) {
return SerializeArray(value, replacer, stack, indent, gap);
} else if (IS_NUMBER_WRAPPER(value)) {
value = ToNumber(value);
- return $isFinite(value) ? ToString(value) : "null";
+ return NUMBER_IS_FINITE(value) ? ToString(value) : "null";
} else if (IS_STRING_WRAPPER(value)) {
return %QuoteJSONString(ToString(value));
} else if (IS_BOOLEAN_WRAPPER(value)) {
@@ -184,24 +179,60 @@ function JSONSerialize(key, holder, replacer, stack, indent, gap) {
function BasicSerializeArray(value, stack, builder) {
+ var len = value.length;
+ if (len == 0) {
+ builder.push("[]");
+ return;
+ }
if (!%PushIfAbsent(stack, value)) {
throw MakeTypeError('circular_structure', []);
}
builder.push("[");
- var len = value.length;
- for (var i = 0; i < len; i++) {
+ var val = value[0];
+ if (IS_STRING(val)) {
+ // First entry is a string. Remaining entries are likely to be strings too.
+ builder.push(%QuoteJSONString(val));
+ for (var i = 1; i < len; i++) {
+ val = value[i];
+ if (IS_STRING(val)) {
+ builder.push(%QuoteJSONStringComma(val));
+ } else {
+ builder.push(",");
+ var before = builder.length;
+ BasicJSONSerialize(i, value[i], stack, builder);
+ if (before == builder.length) builder[before - 1] = ",null";
+ }
+ }
+ } else if (IS_NUMBER(val)) {
+ // First entry is a number. Remaining entries are likely to be numbers too.
+ builder.push(NUMBER_IS_FINITE(val) ? %_NumberToString(val) : "null");
+ for (var i = 1; i < len; i++) {
+ builder.push(",");
+ val = value[i];
+ if (IS_NUMBER(val)) {
+ builder.push(NUMBER_IS_FINITE(val)
+ ? %_NumberToString(val)
+ : "null");
+ } else {
+ var before = builder.length;
+ BasicJSONSerialize(i, value[i], stack, builder);
+ if (before == builder.length) builder[before - 1] = ",null";
+ }
+ }
+ } else {
var before = builder.length;
- BasicJSONSerialize(i, value, stack, builder);
+ BasicJSONSerialize(0, val, stack, builder);
if (before == builder.length) builder.push("null");
- builder.push(",");
+ for (var i = 1; i < len; i++) {
+ builder.push(",");
+ before = builder.length;
+ val = value[i];
+ BasicJSONSerialize(i, val, stack, builder);
+ if (before == builder.length) builder[before - 1] = ",null";
+ }
}
stack.pop();
- if (builder.pop() != ",") {
- builder.push("[]"); // Zero length array. Push "[" back on.
- } else {
- builder.push("]");
- }
-
+ builder.push("]");
}
@@ -210,31 +241,31 @@ function BasicSerializeObject(value, stack, builder) {
throw MakeTypeError('circular_structure', []);
}
builder.push("{");
+ var first = true;
for (var p in value) {
if (%HasLocalProperty(value, p)) {
- builder.push(%QuoteJSONString(p));
+ if (!first) {
+ builder.push(%QuoteJSONStringComma(p));
+ } else {
+ builder.push(%QuoteJSONString(p));
+ }
builder.push(":");
var before = builder.length;
- BasicJSONSerialize(p, value, stack, builder);
+ BasicJSONSerialize(p, value[p], stack, builder);
if (before == builder.length) {
builder.pop();
builder.pop();
} else {
- builder.push(",");
+ first = false;
}
}
}
stack.pop();
- if (builder.pop() != ",") {
- builder.push("{}"); // Object has no own properties. Push "{" back on.
- } else {
- builder.push("}");
- }
+ builder.push("}");
}
-function BasicJSONSerialize(key, holder, stack, builder) {
- var value = holder[key];
+function BasicJSONSerialize(key, value, stack, builder) {
if (IS_SPEC_OBJECT(value)) {
var toJSON = value.toJSON;
if (IS_FUNCTION(toJSON)) {
@@ -244,7 +275,7 @@ function BasicJSONSerialize(key, holder, stack, builder) {
if (IS_STRING(value)) {
builder.push(%QuoteJSONString(value));
} else if (IS_NUMBER(value)) {
- builder.push(($isFinite(value) ? %_NumberToString(value) : "null"));
+ builder.push(NUMBER_IS_FINITE(value) ? %_NumberToString(value) : "null");
} else if (IS_BOOLEAN(value)) {
builder.push(value ? "true" : "false");
} else if (IS_NULL(value)) {
@@ -254,7 +285,7 @@ function BasicJSONSerialize(key, holder, stack, builder) {
// Unwrap value if necessary
if (IS_NUMBER_WRAPPER(value)) {
value = ToNumber(value);
- builder.push(($isFinite(value) ? %_NumberToString(value) : "null"));
+ builder.push(NUMBER_IS_FINITE(value) ? %_NumberToString(value) : "null");
} else if (IS_STRING_WRAPPER(value)) {
builder.push(%QuoteJSONString(ToString(value)));
} else if (IS_BOOLEAN_WRAPPER(value)) {
@@ -271,7 +302,7 @@ function BasicJSONSerialize(key, holder, stack, builder) {
function JSONStringify(value, replacer, space) {
if (%_ArgumentsLength() == 1) {
var builder = [];
- BasicJSONSerialize('', {'': value}, [], builder);
+ BasicJSONSerialize('', value, [], builder);
if (builder.length == 0) return;
var result = %_FastAsciiArrayJoin(builder, "");
if (!IS_UNDEFINED(result)) return result;
diff --git a/deps/v8/src/jsregexp.cc b/deps/v8/src/jsregexp.cc
index e0f2e62166..8e7c35f582 100644
--- a/deps/v8/src/jsregexp.cc
+++ b/deps/v8/src/jsregexp.cc
@@ -425,7 +425,7 @@ RegExpImpl::IrregexpResult RegExpImpl::IrregexpExecOnce(
Handle<JSRegExp> regexp,
Handle<String> subject,
int index,
- Vector<int32_t> output) {
+ Vector<int> output) {
Handle<FixedArray> irregexp(FixedArray::cast(regexp->data()));
ASSERT(index >= 0);
@@ -521,8 +521,8 @@ Handle<Object> RegExpImpl::IrregexpExec(Handle<JSRegExp> jsregexp,
OffsetsVector registers(required_registers);
IrregexpResult res = RegExpImpl::IrregexpExecOnce(
- jsregexp, subject, previous_index, Vector<int32_t>(registers.vector(),
- registers.length()));
+ jsregexp, subject, previous_index, Vector<int>(registers.vector(),
+ registers.length()));
if (res == RE_SUCCESS) {
int capture_register_count =
(IrregexpNumberOfCaptures(FixedArray::cast(jsregexp->data())) + 1) * 2;
diff --git a/deps/v8/src/jsregexp.h b/deps/v8/src/jsregexp.h
index 6f04be3683..af28a87226 100644
--- a/deps/v8/src/jsregexp.h
+++ b/deps/v8/src/jsregexp.h
@@ -114,7 +114,7 @@ class RegExpImpl {
static IrregexpResult IrregexpExecOnce(Handle<JSRegExp> regexp,
Handle<String> subject,
int index,
- Vector<int32_t> registers);
+ Vector<int> registers);
// Execute an Irregexp bytecode pattern.
// On a successful match, the result is a JSArray containing
diff --git a/deps/v8/src/lithium-allocator.cc b/deps/v8/src/lithium-allocator.cc
index ac61c17ba7..29662c94a9 100644
--- a/deps/v8/src/lithium-allocator.cc
+++ b/deps/v8/src/lithium-allocator.cc
@@ -27,7 +27,6 @@
#include "lithium-allocator.h"
-#include "data-flow.h"
#include "hydrogen.h"
#include "string-stream.h"
@@ -107,9 +106,6 @@ void LOperand::PrintTo(StringStream* stream) {
case LUnallocated::SAME_AS_FIRST_INPUT:
stream->Add("(1)");
break;
- case LUnallocated::SAME_AS_ANY_INPUT:
- stream->Add("(A)");
- break;
case LUnallocated::ANY:
stream->Add("(-)");
break;
@@ -832,9 +828,19 @@ void LAllocator::MeetConstraintsBetween(InstructionSummary* first,
AllocateFixed(cur_input, gap_index + 1, is_tagged);
AddConstraintsGapMove(gap_index, input_copy, cur_input);
} else if (cur_input->policy() == LUnallocated::WRITABLE_REGISTER) {
+ // The live range of writable input registers always goes until the end
+ // of the instruction.
+ ASSERT(!cur_input->IsUsedAtStart());
+
LUnallocated* input_copy = cur_input->CopyUnconstrained();
cur_input->set_virtual_register(next_virtual_register_++);
- second->AddTemp(cur_input);
+
+ if (RequiredRegisterKind(input_copy->virtual_register()) ==
+ DOUBLE_REGISTERS) {
+ double_artificial_registers_.Add(
+ cur_input->virtual_register() - first_artificial_register_);
+ }
+
AddConstraintsGapMove(gap_index, input_copy, cur_input);
}
}
@@ -937,6 +943,9 @@ void LAllocator::ProcessInstructions(HBasicBlock* block, BitVector* live) {
curr_position.InstructionEnd());
}
}
+ }
+
+ if (summary->IsCall() || summary->IsSaveDoubles()) {
for (int i = 0; i < DoubleRegister::kNumAllocatableRegisters; ++i) {
if (output == NULL || !output->IsDoubleRegister() ||
output->index() != i) {
@@ -1036,6 +1045,7 @@ void LAllocator::Allocate(LChunk* chunk) {
void LAllocator::MeetRegisterConstraints() {
HPhase phase("Register constraints", chunk());
+ first_artificial_register_ = next_virtual_register_;
const ZoneList<HBasicBlock*>* blocks = graph()->blocks();
for (int i = 0; i < blocks->length(); ++i) {
HBasicBlock* block = blocks->at(i);
@@ -1564,16 +1574,47 @@ bool LAllocator::HasTaggedValue(int virtual_register) const {
RegisterKind LAllocator::RequiredRegisterKind(int virtual_register) const {
- HValue* value = graph()->LookupValue(virtual_register);
- if (value != NULL && value->representation().IsDouble()) {
+ if (virtual_register < first_artificial_register_) {
+ HValue* value = graph()->LookupValue(virtual_register);
+ if (value != NULL && value->representation().IsDouble()) {
+ return DOUBLE_REGISTERS;
+ }
+ } else if (double_artificial_registers_.Contains(
+ virtual_register - first_artificial_register_)) {
return DOUBLE_REGISTERS;
}
+
return GENERAL_REGISTERS;
}
void LAllocator::MarkAsCall() {
- current_summary()->MarkAsCall();
+ // Call instructions can use only fixed registers as
+ // temporaries and outputs because all registers
+ // are blocked by the calling convention.
+ // Inputs can use either fixed register or have a short lifetime (be
+ // used at start of the instruction).
+ InstructionSummary* summary = current_summary();
+#ifdef DEBUG
+ ASSERT(summary->Output() == NULL ||
+ LUnallocated::cast(summary->Output())->HasFixedPolicy() ||
+ !LUnallocated::cast(summary->Output())->HasRegisterPolicy());
+ for (int i = 0; i < summary->InputCount(); i++) {
+ ASSERT(LUnallocated::cast(summary->InputAt(i))->HasFixedPolicy() ||
+ LUnallocated::cast(summary->InputAt(i))->IsUsedAtStart() ||
+ !LUnallocated::cast(summary->InputAt(i))->HasRegisterPolicy());
+ }
+ for (int i = 0; i < summary->TempCount(); i++) {
+ ASSERT(LUnallocated::cast(summary->TempAt(i))->HasFixedPolicy() ||
+ !LUnallocated::cast(summary->TempAt(i))->HasRegisterPolicy());
+ }
+#endif
+ summary->MarkAsCall();
+}
+
+
+void LAllocator::MarkAsSaveDoubles() {
+ current_summary()->MarkAsSaveDoubles();
}
diff --git a/deps/v8/src/lithium-allocator.h b/deps/v8/src/lithium-allocator.h
index 3ec984e262..dfe1953df6 100644
--- a/deps/v8/src/lithium-allocator.h
+++ b/deps/v8/src/lithium-allocator.h
@@ -30,6 +30,7 @@
#include "v8.h"
+#include "data-flow.h"
#include "zone.h"
namespace v8 {
@@ -49,7 +50,6 @@ class LArgument;
class LChunk;
class LConstantOperand;
class LGap;
-class LInstruction;
class LParallelMove;
class LPointerMap;
class LStackSlot;
@@ -204,7 +204,6 @@ class LUnallocated: public LOperand {
MUST_HAVE_REGISTER,
WRITABLE_REGISTER,
SAME_AS_FIRST_INPUT,
- SAME_AS_ANY_INPUT,
IGNORE
};
@@ -275,7 +274,7 @@ class LUnallocated: public LOperand {
return policy() == WRITABLE_REGISTER || policy() == MUST_HAVE_REGISTER;
}
bool HasSameAsInputPolicy() const {
- return policy() == SAME_AS_FIRST_INPUT || policy() == SAME_AS_ANY_INPUT;
+ return policy() == SAME_AS_FIRST_INPUT;
}
Policy policy() const { return PolicyField::decode(value_); }
void set_policy(Policy policy) {
@@ -482,7 +481,11 @@ class LDoubleRegister: public LOperand {
class InstructionSummary: public ZoneObject {
public:
InstructionSummary()
- : output_operand_(NULL), input_count_(0), operands_(4), is_call_(false) {}
+ : output_operand_(NULL),
+ input_count_(0),
+ operands_(4),
+ is_call_(false),
+ is_save_doubles_(false) {}
// Output operands.
LOperand* Output() const { return output_operand_; }
@@ -510,11 +513,15 @@ class InstructionSummary: public ZoneObject {
void MarkAsCall() { is_call_ = true; }
bool IsCall() const { return is_call_; }
+ void MarkAsSaveDoubles() { is_save_doubles_ = true; }
+ bool IsSaveDoubles() const { return is_save_doubles_; }
+
private:
LOperand* output_operand_;
int input_count_;
ZoneList<LOperand*> operands_;
bool is_call_;
+ bool is_save_doubles_;
};
// Representation of the non-empty interval [start,end[.
@@ -698,6 +705,7 @@ class LiveRange: public ZoneObject {
bool HasAllocatedSpillOperand() const {
return spill_operand_ != NULL && !spill_operand_->IsUnallocated();
}
+
LOperand* GetSpillOperand() const { return spill_operand_; }
void SetSpillOperand(LOperand* operand) {
ASSERT(!operand->IsUnallocated());
@@ -715,7 +723,6 @@ class LiveRange: public ZoneObject {
bool Covers(LifetimePosition position);
LifetimePosition FirstIntersection(LiveRange* other);
-
// Add a new interval or a new use position to this live range.
void EnsureInterval(LifetimePosition start, LifetimePosition end);
void AddUseInterval(LifetimePosition start, LifetimePosition end);
@@ -754,6 +761,40 @@ class LiveRange: public ZoneObject {
};
+class GrowableBitVector BASE_EMBEDDED {
+ public:
+ GrowableBitVector() : bits_(NULL) { }
+
+ bool Contains(int value) const {
+ if (!InBitsRange(value)) return false;
+ return bits_->Contains(value);
+ }
+
+ void Add(int value) {
+ EnsureCapacity(value);
+ bits_->Add(value);
+ }
+
+ private:
+ static const int kInitialLength = 1024;
+
+ bool InBitsRange(int value) const {
+ return bits_ != NULL && bits_->length() > value;
+ }
+
+ void EnsureCapacity(int value) {
+ if (InBitsRange(value)) return;
+ int new_length = bits_ == NULL ? kInitialLength : bits_->length();
+ while (new_length <= value) new_length *= 2;
+ BitVector* new_bits = new BitVector(new_length);
+ if (bits_ != NULL) new_bits->CopyFrom(*bits_);
+ bits_ = new_bits;
+ }
+
+ BitVector* bits_;
+};
+
+
class LAllocator BASE_EMBEDDED {
public:
explicit LAllocator(int first_virtual_register, HGraph* graph)
@@ -770,6 +811,7 @@ class LAllocator BASE_EMBEDDED {
inactive_live_ranges_(8),
reusable_slots_(8),
next_virtual_register_(first_virtual_register),
+ first_artificial_register_(first_virtual_register),
mode_(NONE),
num_registers_(-1),
graph_(graph),
@@ -789,6 +831,9 @@ class LAllocator BASE_EMBEDDED {
// Marks the current instruction as a call.
void MarkAsCall();
+ // Marks the current instruction as requiring saving double registers.
+ void MarkAsSaveDoubles();
+
// Checks whether the value of a given virtual register is tagged.
bool HasTaggedValue(int virtual_register) const;
@@ -972,6 +1017,8 @@ class LAllocator BASE_EMBEDDED {
// Next virtual register number to be assigned to temporaries.
int next_virtual_register_;
+ int first_artificial_register_;
+ GrowableBitVector double_artificial_registers_;
RegisterKind mode_;
int num_registers_;
diff --git a/deps/v8/src/lithium.cc b/deps/v8/src/lithium.cc
new file mode 100644
index 0000000000..e066e7da93
--- /dev/null
+++ b/deps/v8/src/lithium.cc
@@ -0,0 +1,93 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "lithium.h"
+
+namespace v8 {
+namespace internal {
+
+bool LParallelMove::IsRedundant() const {
+ for (int i = 0; i < move_operands_.length(); ++i) {
+ if (!move_operands_[i].IsRedundant()) return false;
+ }
+ return true;
+}
+
+
+void LParallelMove::PrintDataTo(StringStream* stream) const {
+ for (int i = move_operands_.length() - 1; i >= 0; --i) {
+ if (!move_operands_[i].IsEliminated()) {
+ LOperand* from = move_operands_[i].from();
+ LOperand* to = move_operands_[i].to();
+ if (from->Equals(to)) {
+ to->PrintTo(stream);
+ } else {
+ to->PrintTo(stream);
+ stream->Add(" = ");
+ from->PrintTo(stream);
+ }
+ stream->Add("; ");
+ }
+ }
+}
+
+
+void LEnvironment::PrintTo(StringStream* stream) {
+ stream->Add("[id=%d|", ast_id());
+ stream->Add("[parameters=%d|", parameter_count());
+ stream->Add("[arguments_stack_height=%d|", arguments_stack_height());
+ for (int i = 0; i < values_.length(); ++i) {
+ if (i != 0) stream->Add(";");
+ if (values_[i] == NULL) {
+ stream->Add("[hole]");
+ } else {
+ values_[i]->PrintTo(stream);
+ }
+ }
+ stream->Add("]");
+}
+
+
+void LPointerMap::RecordPointer(LOperand* op) {
+ // Do not record arguments as pointers.
+ if (op->IsStackSlot() && op->index() < 0) return;
+ ASSERT(!op->IsDoubleRegister() && !op->IsDoubleStackSlot());
+ pointer_operands_.Add(op);
+}
+
+
+void LPointerMap::PrintTo(StringStream* stream) {
+ stream->Add("{");
+ for (int i = 0; i < pointer_operands_.length(); ++i) {
+ if (i != 0) stream->Add(";");
+ pointer_operands_[i]->PrintTo(stream);
+ }
+ stream->Add("} @%d", position());
+}
+
+
+} } // namespace v8::internal
diff --git a/deps/v8/src/lithium.h b/deps/v8/src/lithium.h
new file mode 100644
index 0000000000..f4ae2aa2c9
--- /dev/null
+++ b/deps/v8/src/lithium.h
@@ -0,0 +1,169 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef V8_LITHIUM_H_
+#define V8_LITHIUM_H_
+
+#include "hydrogen.h"
+#include "lithium-allocator.h"
+#include "safepoint-table.h"
+
+namespace v8 {
+namespace internal {
+
+class LCodeGen;
+class Translation;
+
+class LParallelMove : public ZoneObject {
+ public:
+ LParallelMove() : move_operands_(4) { }
+
+ void AddMove(LOperand* from, LOperand* to) {
+ move_operands_.Add(LMoveOperands(from, to));
+ }
+
+ bool IsRedundant() const;
+
+ const ZoneList<LMoveOperands>* move_operands() const {
+ return &move_operands_;
+ }
+
+ void PrintDataTo(StringStream* stream) const;
+
+ private:
+ ZoneList<LMoveOperands> move_operands_;
+};
+
+
+class LPointerMap: public ZoneObject {
+ public:
+ explicit LPointerMap(int position)
+ : pointer_operands_(8), position_(position), lithium_position_(-1) { }
+
+ const ZoneList<LOperand*>* operands() const { return &pointer_operands_; }
+ int position() const { return position_; }
+ int lithium_position() const { return lithium_position_; }
+
+ void set_lithium_position(int pos) {
+ ASSERT(lithium_position_ == -1);
+ lithium_position_ = pos;
+ }
+
+ void RecordPointer(LOperand* op);
+ void PrintTo(StringStream* stream);
+
+ private:
+ ZoneList<LOperand*> pointer_operands_;
+ int position_;
+ int lithium_position_;
+};
+
+
+class LEnvironment: public ZoneObject {
+ public:
+ LEnvironment(Handle<JSFunction> closure,
+ int ast_id,
+ int parameter_count,
+ int argument_count,
+ int value_count,
+ LEnvironment* outer)
+ : closure_(closure),
+ arguments_stack_height_(argument_count),
+ deoptimization_index_(Safepoint::kNoDeoptimizationIndex),
+ translation_index_(-1),
+ ast_id_(ast_id),
+ parameter_count_(parameter_count),
+ values_(value_count),
+ representations_(value_count),
+ spilled_registers_(NULL),
+ spilled_double_registers_(NULL),
+ outer_(outer) {
+ }
+
+ Handle<JSFunction> closure() const { return closure_; }
+ int arguments_stack_height() const { return arguments_stack_height_; }
+ int deoptimization_index() const { return deoptimization_index_; }
+ int translation_index() const { return translation_index_; }
+ int ast_id() const { return ast_id_; }
+ int parameter_count() const { return parameter_count_; }
+ LOperand** spilled_registers() const { return spilled_registers_; }
+ LOperand** spilled_double_registers() const {
+ return spilled_double_registers_;
+ }
+ const ZoneList<LOperand*>* values() const { return &values_; }
+ LEnvironment* outer() const { return outer_; }
+
+ void AddValue(LOperand* operand, Representation representation) {
+ values_.Add(operand);
+ representations_.Add(representation);
+ }
+
+ bool HasTaggedValueAt(int index) const {
+ return representations_[index].IsTagged();
+ }
+
+ void Register(int deoptimization_index, int translation_index) {
+ ASSERT(!HasBeenRegistered());
+ deoptimization_index_ = deoptimization_index;
+ translation_index_ = translation_index;
+ }
+ bool HasBeenRegistered() const {
+ return deoptimization_index_ != Safepoint::kNoDeoptimizationIndex;
+ }
+
+ void SetSpilledRegisters(LOperand** registers,
+ LOperand** double_registers) {
+ spilled_registers_ = registers;
+ spilled_double_registers_ = double_registers;
+ }
+
+ void PrintTo(StringStream* stream);
+
+ private:
+ Handle<JSFunction> closure_;
+ int arguments_stack_height_;
+ int deoptimization_index_;
+ int translation_index_;
+ int ast_id_;
+ int parameter_count_;
+ ZoneList<LOperand*> values_;
+ ZoneList<Representation> representations_;
+
+ // Allocation index indexed arrays of spill slot operands for registers
+ // that are also in spill slots at an OSR entry. NULL for environments
+ // that do not correspond to an OSR entry.
+ LOperand** spilled_registers_;
+ LOperand** spilled_double_registers_;
+
+ LEnvironment* outer_;
+
+ friend class LCodegen;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_LITHIUM_H_
diff --git a/deps/v8/src/liveedit-debugger.js b/deps/v8/src/liveedit-debugger.js
index 0f7c12d71b..e05c53ce18 100644
--- a/deps/v8/src/liveedit-debugger.js
+++ b/deps/v8/src/liveedit-debugger.js
@@ -144,8 +144,8 @@ Debug.LiveEdit = new function() {
replace_code_list[i].live_shared_function_infos;
if (live_shared_function_infos) {
- for (var i = 0; i < live_shared_function_infos.length; i++) {
- replaced_function_infos.push(live_shared_function_infos[i]);
+ for (var j = 0; j < live_shared_function_infos.length; j++) {
+ replaced_function_infos.push(live_shared_function_infos[j]);
}
}
}
@@ -980,15 +980,15 @@ Debug.LiveEdit = new function() {
// LiveEdit main entry point: changes a script text to a new string.
function SetScriptSource(script, new_source, preview_only, change_log) {
var old_source = script.source;
- var diff = CompareStringsLinewise(old_source, new_source);
+ var diff = CompareStrings(old_source, new_source);
return ApplyPatchMultiChunk(script, diff, new_source, preview_only,
change_log);
}
// Function is public.
this.SetScriptSource = SetScriptSource;
- function CompareStringsLinewise(s1, s2) {
- return %LiveEditCompareStringsLinewise(s1, s2);
+ function CompareStrings(s1, s2) {
+ return %LiveEditCompareStrings(s1, s2);
}
// Applies the change to the script.
@@ -1076,7 +1076,7 @@ Debug.LiveEdit = new function() {
// Functions are public for tests.
this.TestApi = {
PosTranslator: PosTranslator,
- CompareStringsLinewise: CompareStringsLinewise,
+ CompareStrings: CompareStrings,
ApplySingleChunkPatch: ApplySingleChunkPatch
}
}
diff --git a/deps/v8/src/liveedit.cc b/deps/v8/src/liveedit.cc
index c4cb68e752..b6ad4cf538 100644
--- a/deps/v8/src/liveedit.cc
+++ b/deps/v8/src/liveedit.cc
@@ -275,6 +275,82 @@ static bool CompareSubstrings(Handle<String> s1, int pos1,
}
+// A helper class that writes chunk numbers into JSArray.
+// Each chunk is stored as 3 array elements: (pos1_begin, pos1_end, pos2_end).
+class CompareOutputArrayWriter {
+ public:
+ CompareOutputArrayWriter()
+ : array_(Factory::NewJSArray(10)), current_size_(0) {}
+
+ Handle<JSArray> GetResult() {
+ return array_;
+ }
+
+ void WriteChunk(int char_pos1, int char_pos2, int char_len1, int char_len2) {
+ SetElement(array_, current_size_, Handle<Object>(Smi::FromInt(char_pos1)));
+ SetElement(array_, current_size_ + 1,
+ Handle<Object>(Smi::FromInt(char_pos1 + char_len1)));
+ SetElement(array_, current_size_ + 2,
+ Handle<Object>(Smi::FromInt(char_pos2 + char_len2)));
+ current_size_ += 3;
+ }
+
+ private:
+ Handle<JSArray> array_;
+ int current_size_;
+};
+
+
+// Represents 2 strings as 2 arrays of tokens.
+// TODO(LiveEdit): Currently it's actually an array of charactres.
+// Make array of tokens instead.
+class TokensCompareInput : public Comparator::Input {
+ public:
+ TokensCompareInput(Handle<String> s1, int offset1, int len1,
+ Handle<String> s2, int offset2, int len2)
+ : s1_(s1), offset1_(offset1), len1_(len1),
+ s2_(s2), offset2_(offset2), len2_(len2) {
+ }
+ virtual int getLength1() {
+ return len1_;
+ }
+ virtual int getLength2() {
+ return len2_;
+ }
+ bool equals(int index1, int index2) {
+ return s1_->Get(offset1_ + index1) == s2_->Get(offset2_ + index2);
+ }
+
+ private:
+ Handle<String> s1_;
+ int offset1_;
+ int len1_;
+ Handle<String> s2_;
+ int offset2_;
+ int len2_;
+};
+
+
+// Stores compare result in JSArray. Converts substring positions
+// to absolute positions.
+class TokensCompareOutput : public Comparator::Output {
+ public:
+ TokensCompareOutput(CompareOutputArrayWriter* array_writer,
+ int offset1, int offset2)
+ : array_writer_(array_writer), offset1_(offset1), offset2_(offset2) {
+ }
+
+ void AddChunk(int pos1, int pos2, int len1, int len2) {
+ array_writer_->WriteChunk(pos1 + offset1_, pos2 + offset2_, len1, len2);
+ }
+
+ private:
+ CompareOutputArrayWriter* array_writer_;
+ int offset1_;
+ int offset2_;
+};
+
+
// Wraps raw n-elements line_ends array as a list of n+1 lines. The last line
// never has terminating new line character.
class LineEndsWrapper {
@@ -350,13 +426,14 @@ class LineArrayCompareInput : public Comparator::Input {
};
-// Stores compare result in JSArray. Each chunk is stored as 3 array elements:
-// (pos1_begin, pos1_end, pos2_end).
-class LineArrayCompareOutput : public Comparator::Output {
+// Stores compare result in JSArray. For each chunk tries to conduct
+// a fine-grained nested diff token-wise.
+class TokenizingLineArrayCompareOutput : public Comparator::Output {
public:
- LineArrayCompareOutput(LineEndsWrapper line_ends1, LineEndsWrapper line_ends2)
- : array_(Factory::NewJSArray(10)), current_size_(0),
- line_ends1_(line_ends1), line_ends2_(line_ends2) {
+ TokenizingLineArrayCompareOutput(LineEndsWrapper line_ends1,
+ LineEndsWrapper line_ends2,
+ Handle<String> s1, Handle<String> s2)
+ : line_ends1_(line_ends1), line_ends2_(line_ends2), s1_(s1), s2_(s2) {
}
void AddChunk(int line_pos1, int line_pos2, int line_len1, int line_len2) {
@@ -365,33 +442,43 @@ class LineArrayCompareOutput : public Comparator::Output {
int char_len1 = line_ends1_.GetLineStart(line_pos1 + line_len1) - char_pos1;
int char_len2 = line_ends2_.GetLineStart(line_pos2 + line_len2) - char_pos2;
- SetElement(array_, current_size_, Handle<Object>(Smi::FromInt(char_pos1)));
- SetElement(array_, current_size_ + 1,
- Handle<Object>(Smi::FromInt(char_pos1 + char_len1)));
- SetElement(array_, current_size_ + 2,
- Handle<Object>(Smi::FromInt(char_pos2 + char_len2)));
- current_size_ += 3;
+ if (char_len1 < CHUNK_LEN_LIMIT && char_len2 < CHUNK_LEN_LIMIT) {
+ // Chunk is small enough to conduct a nested token-level diff.
+ HandleScope subTaskScope;
+
+ TokensCompareInput tokens_input(s1_, char_pos1, char_len1,
+ s2_, char_pos2, char_len2);
+ TokensCompareOutput tokens_output(&array_writer_, char_pos1,
+ char_pos2);
+
+ Comparator::CalculateDifference(&tokens_input, &tokens_output);
+ } else {
+ array_writer_.WriteChunk(char_pos1, char_pos2, char_len1, char_len2);
+ }
}
Handle<JSArray> GetResult() {
- return array_;
+ return array_writer_.GetResult();
}
private:
- Handle<JSArray> array_;
- int current_size_;
+ static const int CHUNK_LEN_LIMIT = 800;
+
+ CompareOutputArrayWriter array_writer_;
LineEndsWrapper line_ends1_;
LineEndsWrapper line_ends2_;
+ Handle<String> s1_;
+ Handle<String> s2_;
};
-Handle<JSArray> LiveEdit::CompareStringsLinewise(Handle<String> s1,
- Handle<String> s2) {
+Handle<JSArray> LiveEdit::CompareStrings(Handle<String> s1,
+ Handle<String> s2) {
LineEndsWrapper line_ends1(s1);
LineEndsWrapper line_ends2(s2);
LineArrayCompareInput input(s1, s2, line_ends1, line_ends2);
- LineArrayCompareOutput output(line_ends1, line_ends2);
+ TokenizingLineArrayCompareOutput output(line_ends1, line_ends2, s1, s2);
Comparator::CalculateDifference(&input, &output);
diff --git a/deps/v8/src/liveedit.h b/deps/v8/src/liveedit.h
index 3632180f99..5f2c99c3d7 100644
--- a/deps/v8/src/liveedit.h
+++ b/deps/v8/src/liveedit.h
@@ -126,10 +126,11 @@ class LiveEdit : AllStatic {
FUNCTION_REPLACED_ON_ACTIVE_STACK = 5
};
- // Compares 2 strings line-by-line and returns diff in form of array of
- // triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
- static Handle<JSArray> CompareStringsLinewise(Handle<String> s1,
- Handle<String> s2);
+ // Compares 2 strings line-by-line, then token-wise and returns diff in form
+ // of array of triplets (pos1, pos1_end, pos2_end) describing list
+ // of diff chunks.
+ static Handle<JSArray> CompareStrings(Handle<String> s1,
+ Handle<String> s2);
};
diff --git a/deps/v8/src/log.cc b/deps/v8/src/log.cc
index db9ff7a1f1..6eb3c9b0ba 100644
--- a/deps/v8/src/log.cc
+++ b/deps/v8/src/log.cc
@@ -276,7 +276,8 @@ void SlidingStateWindow::AddState(StateTag state) {
// Profiler implementation.
//
Profiler::Profiler()
- : head_(0),
+ : Thread("v8:Profiler"),
+ head_(0),
tail_(0),
overflow_(false),
buffer_semaphore_(OS::CreateSemaphore(0)),
diff --git a/deps/v8/src/macros.py b/deps/v8/src/macros.py
index 6d66defb63..69f36c09c0 100644
--- a/deps/v8/src/macros.py
+++ b/deps/v8/src/macros.py
@@ -120,11 +120,13 @@ macro IS_SPEC_OBJECT(arg) = (%_IsSpecObject(arg));
# Inline macros. Use %IS_VAR to make sure arg is evaluated only once.
macro NUMBER_IS_NAN(arg) = (!%_IsSmi(%IS_VAR(arg)) && !(arg == arg));
+macro NUMBER_IS_FINITE(arg) = (%_IsSmi(%IS_VAR(arg)) || arg - arg == 0);
macro TO_INTEGER(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : %NumberToInteger(ToNumber(arg)));
macro TO_INTEGER_MAP_MINUS_ZERO(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : %NumberToIntegerMapMinusZero(ToNumber(arg)));
macro TO_INT32(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : (arg >> 0));
macro TO_UINT32(arg) = (arg >>> 0);
macro TO_STRING_INLINE(arg) = (IS_STRING(%IS_VAR(arg)) ? arg : NonStringToString(arg));
+macro TO_NUMBER_INLINE(arg) = (IS_NUMBER(%IS_VAR(arg)) ? arg : NonNumberToNumber(arg));
# Macros implemented in Python.
diff --git a/deps/v8/src/math.js b/deps/v8/src/math.js
index 90667d76cf..02b19aba69 100644
--- a/deps/v8/src/math.js
+++ b/deps/v8/src/math.js
@@ -44,26 +44,26 @@ $Math.__proto__ = global.Object.prototype;
// ECMA 262 - 15.8.2.1
function MathAbs(x) {
if (%_IsSmi(x)) return x >= 0 ? x : -x;
- if (!IS_NUMBER(x)) x = ToNumber(x);
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
if (x === 0) return 0; // To handle -0.
return x > 0 ? x : -x;
}
// ECMA 262 - 15.8.2.2
function MathAcos(x) {
- if (!IS_NUMBER(x)) x = ToNumber(x);
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
return %Math_acos(x);
}
// ECMA 262 - 15.8.2.3
function MathAsin(x) {
- if (!IS_NUMBER(x)) x = ToNumber(x);
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
return %Math_asin(x);
}
// ECMA 262 - 15.8.2.4
function MathAtan(x) {
- if (!IS_NUMBER(x)) x = ToNumber(x);
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
return %Math_atan(x);
}
@@ -71,32 +71,32 @@ function MathAtan(x) {
// The naming of y and x matches the spec, as does the order in which
// ToNumber (valueOf) is called.
function MathAtan2(y, x) {
- if (!IS_NUMBER(y)) y = ToNumber(y);
- if (!IS_NUMBER(x)) x = ToNumber(x);
+ if (!IS_NUMBER(y)) y = NonNumberToNumber(y);
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
return %Math_atan2(y, x);
}
// ECMA 262 - 15.8.2.6
function MathCeil(x) {
- if (!IS_NUMBER(x)) x = ToNumber(x);
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
return %Math_ceil(x);
}
// ECMA 262 - 15.8.2.7
function MathCos(x) {
- if (!IS_NUMBER(x)) x = ToNumber(x);
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
return %_MathCos(x);
}
// ECMA 262 - 15.8.2.8
function MathExp(x) {
- if (!IS_NUMBER(x)) x = ToNumber(x);
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
return %Math_exp(x);
}
// ECMA 262 - 15.8.2.9
function MathFloor(x) {
- if (!IS_NUMBER(x)) x = ToNumber(x);
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
// It's more common to call this with a positive number that's out
// of range than negative numbers; check the upper bound first.
if (x < 0x80000000 && x > 0) {
@@ -112,7 +112,7 @@ function MathFloor(x) {
// ECMA 262 - 15.8.2.10
function MathLog(x) {
- if (!IS_NUMBER(x)) x = ToNumber(x);
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
return %_MathLog(x);
}
@@ -123,11 +123,11 @@ function MathMax(arg1, arg2) { // length == 2
return -1/0; // Compiler constant-folds this to -Infinity.
}
var r = arg1;
- if (!IS_NUMBER(r)) r = ToNumber(r);
+ if (!IS_NUMBER(r)) r = NonNumberToNumber(r);
if (NUMBER_IS_NAN(r)) return r;
for (var i = 1; i < length; i++) {
var n = %_Arguments(i);
- if (!IS_NUMBER(n)) n = ToNumber(n);
+ if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
if (NUMBER_IS_NAN(n)) return n;
// Make sure +0 is considered greater than -0. -0 is never a Smi, +0 can be
// a Smi or heap number.
@@ -143,11 +143,11 @@ function MathMin(arg1, arg2) { // length == 2
return 1/0; // Compiler constant-folds this to Infinity.
}
var r = arg1;
- if (!IS_NUMBER(r)) r = ToNumber(r);
+ if (!IS_NUMBER(r)) r = NonNumberToNumber(r);
if (NUMBER_IS_NAN(r)) return r;
for (var i = 1; i < length; i++) {
var n = %_Arguments(i);
- if (!IS_NUMBER(n)) n = ToNumber(n);
+ if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
if (NUMBER_IS_NAN(n)) return n;
// Make sure -0 is considered less than +0. -0 is never a Smi, +0 can b a
// Smi or a heap number.
@@ -158,8 +158,8 @@ function MathMin(arg1, arg2) { // length == 2
// ECMA 262 - 15.8.2.13
function MathPow(x, y) {
- if (!IS_NUMBER(x)) x = ToNumber(x);
- if (!IS_NUMBER(y)) y = ToNumber(y);
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
+ if (!IS_NUMBER(y)) y = NonNumberToNumber(y);
return %_MathPow(x, y);
}
@@ -170,25 +170,25 @@ function MathRandom() {
// ECMA 262 - 15.8.2.15
function MathRound(x) {
- if (!IS_NUMBER(x)) x = ToNumber(x);
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
return %RoundNumber(x);
}
// ECMA 262 - 15.8.2.16
function MathSin(x) {
- if (!IS_NUMBER(x)) x = ToNumber(x);
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
return %_MathSin(x);
}
// ECMA 262 - 15.8.2.17
function MathSqrt(x) {
- if (!IS_NUMBER(x)) x = ToNumber(x);
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
return %_MathSqrt(x);
}
// ECMA 262 - 15.8.2.18
function MathTan(x) {
- if (!IS_NUMBER(x)) x = ToNumber(x);
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
return %Math_tan(x);
}
diff --git a/deps/v8/src/messages.js b/deps/v8/src/messages.js
index c19f4a9a68..a30ef8a914 100644
--- a/deps/v8/src/messages.js
+++ b/deps/v8/src/messages.js
@@ -97,6 +97,12 @@ function ToDetailString(obj) {
var constructorName = constructor.name;
if (!constructorName) return ToString(obj);
return "#<" + GetInstanceName(constructorName) + ">";
+ } else if (obj instanceof $Error) {
+ // When formatting internally created error messages, do not
+ // invoke overwritten error toString methods but explicitly use
+ // the error to string method. This is to avoid leaking error
+ // objects between script tags in a browser setting.
+ return %_CallFunction(obj, errorToString);
} else {
return ToString(obj);
}
@@ -943,15 +949,28 @@ function DefineError(f) {
}
%FunctionSetInstanceClassName(f, 'Error');
%SetProperty(f.prototype, 'constructor', f, DONT_ENUM);
- f.prototype.name = name;
+ // The name property on the prototype of error objects is not
+ // specified as being read-one and dont-delete. However, allowing
+ // overwriting allows leaks of error objects between script blocks
+ // in the same context in a browser setting. Therefore we fix the
+ // name.
+ %SetProperty(f.prototype, "name", name, READ_ONLY | DONT_DELETE);
%SetCode(f, function(m) {
if (%_IsConstructCall()) {
+ // Define all the expected properties directly on the error
+ // object. This avoids going through getters and setters defined
+ // on prototype objects.
+ %IgnoreAttributesAndSetProperty(this, 'stack', void 0);
+ %IgnoreAttributesAndSetProperty(this, 'arguments', void 0);
+ %IgnoreAttributesAndSetProperty(this, 'type', void 0);
if (m === kAddMessageAccessorsMarker) {
+ // DefineOneShotAccessor always inserts a message property and
+ // ignores setters.
DefineOneShotAccessor(this, 'message', function (obj) {
return FormatMessage({type: obj.type, args: obj.arguments});
});
} else if (!IS_UNDEFINED(m)) {
- this.message = ToString(m);
+ %IgnoreAttributesAndSetProperty(this, 'message', ToString(m));
}
captureStackTrace(this, f);
} else {
@@ -987,14 +1006,17 @@ $Error.captureStackTrace = captureStackTrace;
// Setup extra properties of the Error.prototype object.
$Error.prototype.message = '';
-%SetProperty($Error.prototype, 'toString', function toString() {
+function errorToString() {
var type = this.type;
if (type && !this.hasOwnProperty("message")) {
return this.name + ": " + FormatMessage({ type: type, args: this.arguments });
}
- var message = this.message;
- return this.name + (message ? (": " + message) : "");
-}, DONT_ENUM);
+ var message = this.hasOwnProperty("message") ? (": " + this.message) : "";
+ return this.name + message;
+}
+
+%FunctionSetName(errorToString, 'toString');
+%SetProperty($Error.prototype, 'toString', errorToString, DONT_ENUM);
// Boilerplate for exceptions for stack overflows. Used from
diff --git a/deps/v8/src/mirror-debugger.js b/deps/v8/src/mirror-debugger.js
index 55836ce7ec..9177a6bc26 100644
--- a/deps/v8/src/mirror-debugger.js
+++ b/deps/v8/src/mirror-debugger.js
@@ -2369,7 +2369,7 @@ function NumberToJSON_(value) {
if (isNaN(value)) {
return 'NaN';
}
- if (!isFinite(value)) {
+ if (!NUMBER_IS_FINITE(value)) {
if (value > 0) {
return 'Infinity';
} else {
diff --git a/deps/v8/src/objects-debug.cc b/deps/v8/src/objects-debug.cc
index 53296d9272..7d50bfb6f6 100644
--- a/deps/v8/src/objects-debug.cc
+++ b/deps/v8/src/objects-debug.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -35,34 +35,8 @@
namespace v8 {
namespace internal {
-#ifdef OBJECT_PRINT
-
-static const char* TypeToString(InstanceType type);
-
-
-void MaybeObject::Print(FILE* out) {
- Object* this_as_object;
- if (ToObject(&this_as_object)) {
- if (this_as_object->IsSmi()) {
- Smi::cast(this_as_object)->SmiPrint(out);
- } else {
- HeapObject::cast(this_as_object)->HeapObjectPrint(out);
- }
- } else {
- Failure::cast(this)->FailurePrint(out);
- }
- Flush(out);
-}
-
-
-void MaybeObject::PrintLn(FILE* out) {
- Print(out);
- PrintF(out, "\n");
-}
-#endif // OBJECT_PRINT
-
-
#ifdef DEBUG
+
void MaybeObject::Verify() {
Object* this_as_object;
if (ToObject(&this_as_object)) {
@@ -94,120 +68,8 @@ void Smi::SmiVerify() {
void Failure::FailureVerify() {
ASSERT(IsFailure());
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void HeapObject::PrintHeader(FILE* out, const char* id) {
- PrintF(out, "%p: [%s]\n", reinterpret_cast<void*>(this), id);
-}
-
-
-void HeapObject::HeapObjectPrint(FILE* out) {
- InstanceType instance_type = map()->instance_type();
-
- HandleScope scope;
- if (instance_type < FIRST_NONSTRING_TYPE) {
- String::cast(this)->StringPrint(out);
- return;
- }
-
- switch (instance_type) {
- case MAP_TYPE:
- Map::cast(this)->MapPrint(out);
- break;
- case HEAP_NUMBER_TYPE:
- HeapNumber::cast(this)->HeapNumberPrint(out);
- break;
- case FIXED_ARRAY_TYPE:
- FixedArray::cast(this)->FixedArrayPrint(out);
- break;
- case BYTE_ARRAY_TYPE:
- ByteArray::cast(this)->ByteArrayPrint(out);
- break;
- case PIXEL_ARRAY_TYPE:
- PixelArray::cast(this)->PixelArrayPrint(out);
- break;
- case EXTERNAL_BYTE_ARRAY_TYPE:
- ExternalByteArray::cast(this)->ExternalByteArrayPrint(out);
- break;
- case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
- ExternalUnsignedByteArray::cast(this)
- ->ExternalUnsignedByteArrayPrint(out);
- break;
- case EXTERNAL_SHORT_ARRAY_TYPE:
- ExternalShortArray::cast(this)->ExternalShortArrayPrint(out);
- break;
- case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
- ExternalUnsignedShortArray::cast(this)
- ->ExternalUnsignedShortArrayPrint(out);
- break;
- case EXTERNAL_INT_ARRAY_TYPE:
- ExternalIntArray::cast(this)->ExternalIntArrayPrint(out);
- break;
- case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
- ExternalUnsignedIntArray::cast(this)->ExternalUnsignedIntArrayPrint(out);
- break;
- case EXTERNAL_FLOAT_ARRAY_TYPE:
- ExternalFloatArray::cast(this)->ExternalFloatArrayPrint(out);
- break;
- case FILLER_TYPE:
- PrintF(out, "filler");
- break;
- case JS_OBJECT_TYPE: // fall through
- case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
- case JS_ARRAY_TYPE:
- case JS_REGEXP_TYPE:
- JSObject::cast(this)->JSObjectPrint(out);
- break;
- case ODDBALL_TYPE:
- Oddball::cast(this)->to_string()->Print(out);
- break;
- case JS_FUNCTION_TYPE:
- JSFunction::cast(this)->JSFunctionPrint(out);
- break;
- case JS_GLOBAL_PROXY_TYPE:
- JSGlobalProxy::cast(this)->JSGlobalProxyPrint(out);
- break;
- case JS_GLOBAL_OBJECT_TYPE:
- JSGlobalObject::cast(this)->JSGlobalObjectPrint(out);
- break;
- case JS_BUILTINS_OBJECT_TYPE:
- JSBuiltinsObject::cast(this)->JSBuiltinsObjectPrint(out);
- break;
- case JS_VALUE_TYPE:
- PrintF(out, "Value wrapper around:");
- JSValue::cast(this)->value()->Print(out);
- break;
- case CODE_TYPE:
- Code::cast(this)->CodePrint(out);
- break;
- case PROXY_TYPE:
- Proxy::cast(this)->ProxyPrint(out);
- break;
- case SHARED_FUNCTION_INFO_TYPE:
- SharedFunctionInfo::cast(this)->SharedFunctionInfoPrint(out);
- break;
- case JS_GLOBAL_PROPERTY_CELL_TYPE:
- JSGlobalPropertyCell::cast(this)->JSGlobalPropertyCellPrint(out);
- break;
-#define MAKE_STRUCT_CASE(NAME, Name, name) \
- case NAME##_TYPE: \
- Name::cast(this)->Name##Print(out); \
- break;
- STRUCT_LIST(MAKE_STRUCT_CASE)
-#undef MAKE_STRUCT_CASE
-
- default:
- PrintF(out, "UNKNOWN TYPE %d", map()->instance_type());
- UNREACHABLE();
- break;
- }
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void HeapObject::HeapObjectVerify() {
InstanceType instance_type = map()->instance_type();
@@ -320,57 +182,8 @@ void HeapObject::VerifyHeapPointer(Object* p) {
void HeapNumber::HeapNumberVerify() {
ASSERT(IsHeapNumber());
}
-#endif // DEBUG
-#ifdef OBJECT_PRINT
-void ByteArray::ByteArrayPrint(FILE* out) {
- PrintF(out, "byte array, data starts at %p", GetDataStartAddress());
-}
-
-
-void PixelArray::PixelArrayPrint(FILE* out) {
- PrintF(out, "pixel array");
-}
-
-
-void ExternalByteArray::ExternalByteArrayPrint(FILE* out) {
- PrintF(out, "external byte array");
-}
-
-
-void ExternalUnsignedByteArray::ExternalUnsignedByteArrayPrint(FILE* out) {
- PrintF(out, "external unsigned byte array");
-}
-
-
-void ExternalShortArray::ExternalShortArrayPrint(FILE* out) {
- PrintF(out, "external short array");
-}
-
-
-void ExternalUnsignedShortArray::ExternalUnsignedShortArrayPrint(FILE* out) {
- PrintF(out, "external unsigned short array");
-}
-
-
-void ExternalIntArray::ExternalIntArrayPrint(FILE* out) {
- PrintF(out, "external int array");
-}
-
-
-void ExternalUnsignedIntArray::ExternalUnsignedIntArrayPrint(FILE* out) {
- PrintF(out, "external unsigned int array");
-}
-
-
-void ExternalFloatArray::ExternalFloatArrayPrint(FILE* out) {
- PrintF(out, "external float array");
-}
-#endif // OBJECT_PRINT
-
-
-#ifdef DEBUG
void ByteArray::ByteArrayVerify() {
ASSERT(IsByteArray());
}
@@ -414,146 +227,8 @@ void ExternalUnsignedIntArray::ExternalUnsignedIntArrayVerify() {
void ExternalFloatArray::ExternalFloatArrayVerify() {
ASSERT(IsExternalFloatArray());
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void JSObject::PrintProperties(FILE* out) {
- if (HasFastProperties()) {
- DescriptorArray* descs = map()->instance_descriptors();
- for (int i = 0; i < descs->number_of_descriptors(); i++) {
- PrintF(out, " ");
- descs->GetKey(i)->StringPrint(out);
- PrintF(out, ": ");
- switch (descs->GetType(i)) {
- case FIELD: {
- int index = descs->GetFieldIndex(i);
- FastPropertyAt(index)->ShortPrint(out);
- PrintF(out, " (field at offset %d)\n", index);
- break;
- }
- case CONSTANT_FUNCTION:
- descs->GetConstantFunction(i)->ShortPrint(out);
- PrintF(out, " (constant function)\n");
- break;
- case CALLBACKS:
- descs->GetCallbacksObject(i)->ShortPrint(out);
- PrintF(out, " (callback)\n");
- break;
- case MAP_TRANSITION:
- PrintF(out, " (map transition)\n");
- break;
- case CONSTANT_TRANSITION:
- PrintF(out, " (constant transition)\n");
- break;
- case NULL_DESCRIPTOR:
- PrintF(out, " (null descriptor)\n");
- break;
- default:
- UNREACHABLE();
- break;
- }
- }
- } else {
- property_dictionary()->Print(out);
- }
-}
-
-
-void JSObject::PrintElements(FILE* out) {
- switch (GetElementsKind()) {
- case FAST_ELEMENTS: {
- // Print in array notation for non-sparse arrays.
- FixedArray* p = FixedArray::cast(elements());
- for (int i = 0; i < p->length(); i++) {
- PrintF(out, " %d: ", i);
- p->get(i)->ShortPrint(out);
- PrintF(out, "\n");
- }
- break;
- }
- case PIXEL_ELEMENTS: {
- PixelArray* p = PixelArray::cast(elements());
- for (int i = 0; i < p->length(); i++) {
- PrintF(out, " %d: %d\n", i, p->get(i));
- }
- break;
- }
- case EXTERNAL_BYTE_ELEMENTS: {
- ExternalByteArray* p = ExternalByteArray::cast(elements());
- for (int i = 0; i < p->length(); i++) {
- PrintF(out, " %d: %d\n", i, static_cast<int>(p->get(i)));
- }
- break;
- }
- case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
- ExternalUnsignedByteArray* p =
- ExternalUnsignedByteArray::cast(elements());
- for (int i = 0; i < p->length(); i++) {
- PrintF(out, " %d: %d\n", i, static_cast<int>(p->get(i)));
- }
- break;
- }
- case EXTERNAL_SHORT_ELEMENTS: {
- ExternalShortArray* p = ExternalShortArray::cast(elements());
- for (int i = 0; i < p->length(); i++) {
- PrintF(out, " %d: %d\n", i, static_cast<int>(p->get(i)));
- }
- break;
- }
- case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
- ExternalUnsignedShortArray* p =
- ExternalUnsignedShortArray::cast(elements());
- for (int i = 0; i < p->length(); i++) {
- PrintF(out, " %d: %d\n", i, static_cast<int>(p->get(i)));
- }
- break;
- }
- case EXTERNAL_INT_ELEMENTS: {
- ExternalIntArray* p = ExternalIntArray::cast(elements());
- for (int i = 0; i < p->length(); i++) {
- PrintF(out, " %d: %d\n", i, static_cast<int>(p->get(i)));
- }
- break;
- }
- case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
- ExternalUnsignedIntArray* p =
- ExternalUnsignedIntArray::cast(elements());
- for (int i = 0; i < p->length(); i++) {
- PrintF(out, " %d: %d\n", i, static_cast<int>(p->get(i)));
- }
- break;
- }
- case EXTERNAL_FLOAT_ELEMENTS: {
- ExternalFloatArray* p = ExternalFloatArray::cast(elements());
- for (int i = 0; i < p->length(); i++) {
- PrintF(out, " %d: %f\n", i, p->get(i));
- }
- break;
- }
- case DICTIONARY_ELEMENTS:
- elements()->Print(out);
- break;
- default:
- UNREACHABLE();
- break;
- }
-}
-void JSObject::JSObjectPrint(FILE* out) {
- PrintF(out, "%p: [JSObject]\n", reinterpret_cast<void*>(this));
- PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
- PrintF(out, " - prototype = %p\n", reinterpret_cast<void*>(GetPrototype()));
- PrintF(out, " {\n");
- PrintProperties(out);
- PrintElements(out);
- PrintF(out, " }\n");
-}
-#endif // OBJECT_PRINT
-
-
-#ifdef DEBUG
void JSObject::JSObjectVerify() {
VerifyHeapPointer(properties());
VerifyHeapPointer(elements());
@@ -567,103 +242,8 @@ void JSObject::JSObjectVerify() {
elements()->map() == Heap::fixed_cow_array_map()));
ASSERT(map()->has_fast_elements() == HasFastElements());
}
-#endif // DEBUG
-#ifdef OBJECT_PRINT
-static const char* TypeToString(InstanceType type) {
- switch (type) {
- case INVALID_TYPE: return "INVALID";
- case MAP_TYPE: return "MAP";
- case HEAP_NUMBER_TYPE: return "HEAP_NUMBER";
- case SYMBOL_TYPE: return "SYMBOL";
- case ASCII_SYMBOL_TYPE: return "ASCII_SYMBOL";
- case CONS_SYMBOL_TYPE: return "CONS_SYMBOL";
- case CONS_ASCII_SYMBOL_TYPE: return "CONS_ASCII_SYMBOL";
- case EXTERNAL_ASCII_SYMBOL_TYPE:
- case EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE:
- case EXTERNAL_SYMBOL_TYPE: return "EXTERNAL_SYMBOL";
- case ASCII_STRING_TYPE: return "ASCII_STRING";
- case STRING_TYPE: return "TWO_BYTE_STRING";
- case CONS_STRING_TYPE:
- case CONS_ASCII_STRING_TYPE: return "CONS_STRING";
- case EXTERNAL_ASCII_STRING_TYPE:
- case EXTERNAL_STRING_WITH_ASCII_DATA_TYPE:
- case EXTERNAL_STRING_TYPE: return "EXTERNAL_STRING";
- case FIXED_ARRAY_TYPE: return "FIXED_ARRAY";
- case BYTE_ARRAY_TYPE: return "BYTE_ARRAY";
- case PIXEL_ARRAY_TYPE: return "PIXEL_ARRAY";
- case EXTERNAL_BYTE_ARRAY_TYPE: return "EXTERNAL_BYTE_ARRAY";
- case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
- return "EXTERNAL_UNSIGNED_BYTE_ARRAY";
- case EXTERNAL_SHORT_ARRAY_TYPE: return "EXTERNAL_SHORT_ARRAY";
- case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
- return "EXTERNAL_UNSIGNED_SHORT_ARRAY";
- case EXTERNAL_INT_ARRAY_TYPE: return "EXTERNAL_INT_ARRAY";
- case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
- return "EXTERNAL_UNSIGNED_INT_ARRAY";
- case EXTERNAL_FLOAT_ARRAY_TYPE: return "EXTERNAL_FLOAT_ARRAY";
- case FILLER_TYPE: return "FILLER";
- case JS_OBJECT_TYPE: return "JS_OBJECT";
- case JS_CONTEXT_EXTENSION_OBJECT_TYPE: return "JS_CONTEXT_EXTENSION_OBJECT";
- case ODDBALL_TYPE: return "ODDBALL";
- case JS_GLOBAL_PROPERTY_CELL_TYPE: return "JS_GLOBAL_PROPERTY_CELL";
- case SHARED_FUNCTION_INFO_TYPE: return "SHARED_FUNCTION_INFO";
- case JS_FUNCTION_TYPE: return "JS_FUNCTION";
- case CODE_TYPE: return "CODE";
- case JS_ARRAY_TYPE: return "JS_ARRAY";
- case JS_REGEXP_TYPE: return "JS_REGEXP";
- case JS_VALUE_TYPE: return "JS_VALUE";
- case JS_GLOBAL_OBJECT_TYPE: return "JS_GLOBAL_OBJECT";
- case JS_BUILTINS_OBJECT_TYPE: return "JS_BUILTINS_OBJECT";
- case JS_GLOBAL_PROXY_TYPE: return "JS_GLOBAL_PROXY";
- case PROXY_TYPE: return "PROXY";
-#define MAKE_STRUCT_CASE(NAME, Name, name) case NAME##_TYPE: return #NAME;
- STRUCT_LIST(MAKE_STRUCT_CASE)
-#undef MAKE_STRUCT_CASE
- }
- return "UNKNOWN";
-}
-
-
-void Map::MapPrint(FILE* out) {
- HeapObject::PrintHeader(out, "Map");
- PrintF(out, " - type: %s\n", TypeToString(instance_type()));
- PrintF(out, " - instance size: %d\n", instance_size());
- PrintF(out, " - inobject properties: %d\n", inobject_properties());
- PrintF(out, " - pre-allocated property fields: %d\n",
- pre_allocated_property_fields());
- PrintF(out, " - unused property fields: %d\n", unused_property_fields());
- if (is_hidden_prototype()) {
- PrintF(out, " - hidden_prototype\n");
- }
- if (has_named_interceptor()) {
- PrintF(out, " - named_interceptor\n");
- }
- if (has_indexed_interceptor()) {
- PrintF(out, " - indexed_interceptor\n");
- }
- if (is_undetectable()) {
- PrintF(out, " - undetectable\n");
- }
- if (has_instance_call_handler()) {
- PrintF(out, " - instance_call_handler\n");
- }
- if (is_access_check_needed()) {
- PrintF(out, " - access_check_needed\n");
- }
- PrintF(out, " - instance descriptors: ");
- instance_descriptors()->ShortPrint(out);
- PrintF(out, "\n - prototype: ");
- prototype()->ShortPrint(out);
- PrintF(out, "\n - constructor: ");
- constructor()->ShortPrint(out);
- PrintF(out, "\n");
-}
-#endif // OBJECT_PRINT
-
-
-#ifdef DEBUG
void Map::MapVerify() {
ASSERT(!Heap::InNewSpace(this));
ASSERT(FIRST_TYPE <= instance_type() && instance_type() <= LAST_TYPE);
@@ -685,21 +265,8 @@ void Map::SharedMapVerify() {
ASSERT_EQ(StaticVisitorBase::GetVisitorId(instance_type(), instance_size()),
visitor_id());
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void CodeCache::CodeCachePrint(FILE* out) {
- HeapObject::PrintHeader(out, "CodeCache");
- PrintF(out, "\n - default_cache: ");
- default_cache()->ShortPrint(out);
- PrintF(out, "\n - normal_type_cache: ");
- normal_type_cache()->ShortPrint(out);
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void CodeCache::CodeCacheVerify() {
VerifyHeapPointer(default_cache());
VerifyHeapPointer(normal_type_cache());
@@ -707,23 +274,8 @@ void CodeCache::CodeCacheVerify() {
ASSERT(normal_type_cache()->IsUndefined()
|| normal_type_cache()->IsCodeCacheHashTable());
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void FixedArray::FixedArrayPrint(FILE* out) {
- HeapObject::PrintHeader(out, "FixedArray");
- PrintF(out, " - length: %d", length());
- for (int i = 0; i < length(); i++) {
- PrintF(out, "\n [%d]: ", i);
- get(i)->ShortPrint(out);
- }
- PrintF(out, "\n");
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void FixedArray::FixedArrayVerify() {
for (int i = 0; i < length(); i++) {
Object* e = get(i);
@@ -734,57 +286,16 @@ void FixedArray::FixedArrayVerify() {
}
}
}
-#endif // DEBUG
-#ifdef OBJECT_PRINT
-void JSValue::JSValuePrint(FILE* out) {
- HeapObject::PrintHeader(out, "ValueObject");
- value()->Print(out);
-}
-#endif // OBJECT_PRINT
-
-
-#ifdef DEBUG
void JSValue::JSValueVerify() {
Object* v = value();
if (v->IsHeapObject()) {
VerifyHeapPointer(v);
}
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void String::StringPrint(FILE* out) {
- if (StringShape(this).IsSymbol()) {
- PrintF(out, "#");
- } else if (StringShape(this).IsCons()) {
- PrintF(out, "c\"");
- } else {
- PrintF(out, "\"");
- }
-
- const char truncated_epilogue[] = "...<truncated>";
- int len = length();
- if (!FLAG_use_verbose_printer) {
- if (len > 100) {
- len = 100 - sizeof(truncated_epilogue);
- }
- }
- for (int i = 0; i < len; i++) {
- PrintF(out, "%c", Get(i));
- }
- if (len != length()) {
- PrintF(out, "%s", truncated_epilogue);
- }
-
- if (!StringShape(this).IsSymbol()) PrintF(out, "\"");
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void String::StringVerify() {
CHECK(IsString());
CHECK(length() >= 0 && length() <= Smi::kMaxValue);
@@ -792,36 +303,8 @@ void String::StringVerify() {
CHECK(!Heap::InNewSpace(this));
}
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void JSFunction::JSFunctionPrint(FILE* out) {
- HeapObject::PrintHeader(out, "Function");
- PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map()));
- PrintF(out, " - initial_map = ");
- if (has_initial_map()) {
- initial_map()->ShortPrint(out);
- }
- PrintF(out, "\n - shared_info = ");
- shared()->ShortPrint(out);
- PrintF(out, "\n - name = ");
- shared()->name()->Print(out);
- PrintF(out, "\n - context = ");
- unchecked_context()->ShortPrint(out);
- PrintF(out, "\n - code = ");
- code()->ShortPrint(out);
- PrintF(out, "\n");
-
- PrintProperties(out);
- PrintElements(out);
-
- PrintF(out, "\n");
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void JSFunction::JSFunctionVerify() {
CHECK(IsJSFunction());
VerifyObjectField(kPrototypeOrInitialMapOffset);
@@ -829,41 +312,8 @@ void JSFunction::JSFunctionVerify() {
CHECK(next_function_link()->IsUndefined() ||
next_function_link()->IsJSFunction());
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void SharedFunctionInfo::SharedFunctionInfoPrint(FILE* out) {
- HeapObject::PrintHeader(out, "SharedFunctionInfo");
- PrintF(out, " - name: ");
- name()->ShortPrint(out);
- PrintF(out, "\n - expected_nof_properties: %d", expected_nof_properties());
- PrintF(out, "\n - instance class name = ");
- instance_class_name()->Print(out);
- PrintF(out, "\n - code = ");
- code()->ShortPrint(out);
- PrintF(out, "\n - source code = ");
- GetSourceCode()->ShortPrint(out);
- // Script files are often large, hard to read.
- // PrintF(out, "\n - script =");
- // script()->Print(out);
- PrintF(out, "\n - function token position = %d", function_token_position());
- PrintF(out, "\n - start position = %d", start_position());
- PrintF(out, "\n - end position = %d", end_position());
- PrintF(out, "\n - is expression = %d", is_expression());
- PrintF(out, "\n - debug info = ");
- debug_info()->ShortPrint(out);
- PrintF(out, "\n - length = %d", length());
- PrintF(out, "\n - has_only_simple_this_property_assignments = %d",
- has_only_simple_this_property_assignments());
- PrintF(out, "\n - this_property_assignments = ");
- this_property_assignments()->ShortPrint(out);
- PrintF(out, "\n");
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void SharedFunctionInfo::SharedFunctionInfoVerify() {
CHECK(IsSharedFunctionInfo());
VerifyObjectField(kNameOffset);
@@ -874,21 +324,8 @@ void SharedFunctionInfo::SharedFunctionInfoVerify() {
VerifyObjectField(kScriptOffset);
VerifyObjectField(kDebugInfoOffset);
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void JSGlobalProxy::JSGlobalProxyPrint(FILE* out) {
- PrintF(out, "global_proxy");
- JSObjectPrint(out);
- PrintF(out, "context : ");
- context()->ShortPrint(out);
- PrintF(out, "\n");
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void JSGlobalProxy::JSGlobalProxyVerify() {
CHECK(IsJSGlobalProxy());
JSObjectVerify();
@@ -898,21 +335,8 @@ void JSGlobalProxy::JSGlobalProxyVerify() {
CHECK(HasFastElements());
CHECK_EQ(0, FixedArray::cast(elements())->length());
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void JSGlobalObject::JSGlobalObjectPrint(FILE* out) {
- PrintF(out, "global ");
- JSObjectPrint(out);
- PrintF(out, "global context : ");
- global_context()->ShortPrint(out);
- PrintF(out, "\n");
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void JSGlobalObject::JSGlobalObjectVerify() {
CHECK(IsJSGlobalObject());
JSObjectVerify();
@@ -922,18 +346,8 @@ void JSGlobalObject::JSGlobalObjectVerify() {
VerifyObjectField(i);
}
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void JSBuiltinsObject::JSBuiltinsObjectPrint(FILE* out) {
- PrintF(out, "builtins ");
- JSObjectPrint(out);
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void JSBuiltinsObject::JSBuiltinsObjectVerify() {
CHECK(IsJSBuiltinsObject());
JSObjectVerify();
@@ -954,8 +368,10 @@ void Oddball::OddballVerify() {
} else {
ASSERT(number->IsSmi());
int value = Smi::cast(number)->value();
- ASSERT(value == 0 || value == 1 || value == -1 ||
- value == -2 || value == -3);
+ // Hidden oddballs have negative smis.
+ const int kLeastHiddenOddballNumber = -4;
+ ASSERT(value <= 1);
+ ASSERT(value >= kLeastHiddenOddballNumber);
}
}
@@ -964,27 +380,8 @@ void JSGlobalPropertyCell::JSGlobalPropertyCellVerify() {
CHECK(IsJSGlobalPropertyCell());
VerifyObjectField(kValueOffset);
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void JSGlobalPropertyCell::JSGlobalPropertyCellPrint(FILE* out) {
- HeapObject::PrintHeader(out, "JSGlobalPropertyCell");
-}
-
-
-void Code::CodePrint(FILE* out) {
- HeapObject::PrintHeader(out, "Code");
-#ifdef ENABLE_DISASSEMBLER
- if (FLAG_use_verbose_printer) {
- Disassemble(NULL, out);
- }
-#endif
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void Code::CodeVerify() {
CHECK(IsAligned(reinterpret_cast<intptr_t>(instruction_start()),
kCodeAlignment));
@@ -1039,17 +436,8 @@ void JSRegExp::JSRegExpVerify() {
break;
}
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void Proxy::ProxyPrint(FILE* out) {
- PrintF(out, "proxy to %p", proxy());
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void Proxy::ProxyVerify() {
ASSERT(IsProxy());
}
@@ -1063,50 +451,16 @@ void AccessorInfo::AccessorInfoVerify() {
VerifyPointer(data());
VerifyPointer(flag());
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void AccessorInfo::AccessorInfoPrint(FILE* out) {
- HeapObject::PrintHeader(out, "AccessorInfo");
- PrintF(out, "\n - getter: ");
- getter()->ShortPrint(out);
- PrintF(out, "\n - setter: ");
- setter()->ShortPrint(out);
- PrintF(out, "\n - name: ");
- name()->ShortPrint(out);
- PrintF(out, "\n - data: ");
- data()->ShortPrint(out);
- PrintF(out, "\n - flag: ");
- flag()->ShortPrint(out);
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void AccessCheckInfo::AccessCheckInfoVerify() {
CHECK(IsAccessCheckInfo());
VerifyPointer(named_callback());
VerifyPointer(indexed_callback());
VerifyPointer(data());
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void AccessCheckInfo::AccessCheckInfoPrint(FILE* out) {
- HeapObject::PrintHeader(out, "AccessCheckInfo");
- PrintF(out, "\n - named_callback: ");
- named_callback()->ShortPrint(out);
- PrintF(out, "\n - indexed_callback: ");
- indexed_callback()->ShortPrint(out);
- PrintF(out, "\n - data: ");
- data()->ShortPrint(out);
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void InterceptorInfo::InterceptorInfoVerify() {
CHECK(IsInterceptorInfo());
VerifyPointer(getter());
@@ -1116,50 +470,15 @@ void InterceptorInfo::InterceptorInfoVerify() {
VerifyPointer(enumerator());
VerifyPointer(data());
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void InterceptorInfo::InterceptorInfoPrint(FILE* out) {
- HeapObject::PrintHeader(out, "InterceptorInfo");
- PrintF(out, "\n - getter: ");
- getter()->ShortPrint(out);
- PrintF(out, "\n - setter: ");
- setter()->ShortPrint(out);
- PrintF(out, "\n - query: ");
- query()->ShortPrint(out);
- PrintF(out, "\n - deleter: ");
- deleter()->ShortPrint(out);
- PrintF(out, "\n - enumerator: ");
- enumerator()->ShortPrint(out);
- PrintF(out, "\n - data: ");
- data()->ShortPrint(out);
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void CallHandlerInfo::CallHandlerInfoVerify() {
CHECK(IsCallHandlerInfo());
VerifyPointer(callback());
VerifyPointer(data());
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void CallHandlerInfo::CallHandlerInfoPrint(FILE* out) {
- HeapObject::PrintHeader(out, "CallHandlerInfo");
- PrintF(out, "\n - callback: ");
- callback()->ShortPrint(out);
- PrintF(out, "\n - data: ");
- data()->ShortPrint(out);
- PrintF(out, "\n - call_stub_cache: ");
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void TemplateInfo::TemplateInfoVerify() {
VerifyPointer(tag());
VerifyPointer(property_list());
@@ -1179,106 +498,29 @@ void FunctionTemplateInfo::FunctionTemplateInfoVerify() {
VerifyPointer(signature());
VerifyPointer(access_check_info());
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void FunctionTemplateInfo::FunctionTemplateInfoPrint(FILE* out) {
- HeapObject::PrintHeader(out, "FunctionTemplateInfo");
- PrintF(out, "\n - class name: ");
- class_name()->ShortPrint(out);
- PrintF(out, "\n - tag: ");
- tag()->ShortPrint(out);
- PrintF(out, "\n - property_list: ");
- property_list()->ShortPrint(out);
- PrintF(out, "\n - serial_number: ");
- serial_number()->ShortPrint(out);
- PrintF(out, "\n - call_code: ");
- call_code()->ShortPrint(out);
- PrintF(out, "\n - property_accessors: ");
- property_accessors()->ShortPrint(out);
- PrintF(out, "\n - prototype_template: ");
- prototype_template()->ShortPrint(out);
- PrintF(out, "\n - parent_template: ");
- parent_template()->ShortPrint(out);
- PrintF(out, "\n - named_property_handler: ");
- named_property_handler()->ShortPrint(out);
- PrintF(out, "\n - indexed_property_handler: ");
- indexed_property_handler()->ShortPrint(out);
- PrintF(out, "\n - instance_template: ");
- instance_template()->ShortPrint(out);
- PrintF(out, "\n - signature: ");
- signature()->ShortPrint(out);
- PrintF(out, "\n - access_check_info: ");
- access_check_info()->ShortPrint(out);
- PrintF(out, "\n - hidden_prototype: %s",
- hidden_prototype() ? "true" : "false");
- PrintF(out, "\n - undetectable: %s", undetectable() ? "true" : "false");
- PrintF(out, "\n - need_access_check: %s",
- needs_access_check() ? "true" : "false");
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void ObjectTemplateInfo::ObjectTemplateInfoVerify() {
CHECK(IsObjectTemplateInfo());
TemplateInfoVerify();
VerifyPointer(constructor());
VerifyPointer(internal_field_count());
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void ObjectTemplateInfo::ObjectTemplateInfoPrint(FILE* out) {
- HeapObject::PrintHeader(out, "ObjectTemplateInfo");
- PrintF(out, "\n - constructor: ");
- constructor()->ShortPrint(out);
- PrintF(out, "\n - internal_field_count: ");
- internal_field_count()->ShortPrint(out);
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void SignatureInfo::SignatureInfoVerify() {
CHECK(IsSignatureInfo());
VerifyPointer(receiver());
VerifyPointer(args());
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void SignatureInfo::SignatureInfoPrint(FILE* out) {
- HeapObject::PrintHeader(out, "SignatureInfo");
- PrintF(out, "\n - receiver: ");
- receiver()->ShortPrint(out);
- PrintF(out, "\n - args: ");
- args()->ShortPrint(out);
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void TypeSwitchInfo::TypeSwitchInfoVerify() {
CHECK(IsTypeSwitchInfo());
VerifyPointer(types());
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void TypeSwitchInfo::TypeSwitchInfoPrint(FILE* out) {
- HeapObject::PrintHeader(out, "TypeSwitchInfo");
- PrintF(out, "\n - types: ");
- types()->ShortPrint(out);
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void Script::ScriptVerify() {
CHECK(IsScript());
VerifyPointer(source());
@@ -1291,45 +533,9 @@ void Script::ScriptVerify() {
VerifyPointer(line_ends());
VerifyPointer(id());
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void Script::ScriptPrint(FILE* out) {
- HeapObject::PrintHeader(out, "Script");
- PrintF(out, "\n - source: ");
- source()->ShortPrint(out);
- PrintF(out, "\n - name: ");
- name()->ShortPrint(out);
- PrintF(out, "\n - line_offset: ");
- line_offset()->ShortPrint(out);
- PrintF(out, "\n - column_offset: ");
- column_offset()->ShortPrint(out);
- PrintF(out, "\n - type: ");
- type()->ShortPrint(out);
- PrintF(out, "\n - id: ");
- id()->ShortPrint(out);
- PrintF(out, "\n - data: ");
- data()->ShortPrint(out);
- PrintF(out, "\n - context data: ");
- context_data()->ShortPrint(out);
- PrintF(out, "\n - wrapper: ");
- wrapper()->ShortPrint(out);
- PrintF(out, "\n - compilation type: ");
- compilation_type()->ShortPrint(out);
- PrintF(out, "\n - line ends: ");
- line_ends()->ShortPrint(out);
- PrintF(out, "\n - eval from shared: ");
- eval_from_shared()->ShortPrint(out);
- PrintF(out, "\n - eval from instructions offset: ");
- eval_from_instructions_offset()->ShortPrint(out);
- PrintF(out, "\n");
-}
-#endif // OBJECT_PRINT
#ifdef ENABLE_DEBUGGER_SUPPORT
-#ifdef DEBUG
void DebugInfo::DebugInfoVerify() {
CHECK(IsDebugInfo());
VerifyPointer(shared());
@@ -1337,25 +543,8 @@ void DebugInfo::DebugInfoVerify() {
VerifyPointer(code());
VerifyPointer(break_points());
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void DebugInfo::DebugInfoPrint(FILE* out) {
- HeapObject::PrintHeader(out, "DebugInfo");
- PrintF(out, "\n - shared: ");
- shared()->ShortPrint(out);
- PrintF(out, "\n - original_code: ");
- original_code()->ShortPrint(out);
- PrintF(out, "\n - code: ");
- code()->ShortPrint(out);
- PrintF(out, "\n - break_points: ");
- break_points()->Print(out);
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
void BreakPointInfo::BreakPointInfoVerify() {
CHECK(IsBreakPointInfo());
code_position()->SmiVerify();
@@ -1363,23 +552,9 @@ void BreakPointInfo::BreakPointInfoVerify() {
statement_position()->SmiVerify();
VerifyPointer(break_point_objects());
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void BreakPointInfo::BreakPointInfoPrint(FILE* out) {
- HeapObject::PrintHeader(out, "BreakPointInfo");
- PrintF(out, "\n - code_position: %d", code_position()->value());
- PrintF(out, "\n - source_position: %d", source_position()->value());
- PrintF(out, "\n - statement_position: %d", statement_position()->value());
- PrintF(out, "\n - break_point_objects: ");
- break_point_objects()->ShortPrint(out);
-}
-#endif // OBJECT_PRINT
#endif // ENABLE_DEBUGGER_SUPPORT
-#ifdef DEBUG
void JSObject::IncrementSpillStatistics(SpillInformation* info) {
info->number_of_objects_++;
// Named properties
@@ -1462,24 +637,8 @@ void JSObject::SpillInformation::Print() {
PrintF("\n");
}
-#endif // DEBUG
-
-
-#ifdef OBJECT_PRINT
-void DescriptorArray::PrintDescriptors(FILE* out) {
- PrintF(out, "Descriptor array %d\n", number_of_descriptors());
- for (int i = 0; i < number_of_descriptors(); i++) {
- PrintF(out, " %d: ", i);
- Descriptor desc;
- Get(i, &desc);
- desc.Print(out);
- }
- PrintF(out, "\n");
-}
-#endif // OBJECT_PRINT
-#ifdef DEBUG
bool DescriptorArray::IsSortedNoDuplicates() {
String* current_key = NULL;
uint32_t current = 0;
@@ -1515,13 +674,12 @@ void JSFunctionResultCache::JSFunctionResultCacheVerify() {
ASSERT_EQ(0, finger % kEntrySize);
if (FLAG_enable_slow_asserts) {
- for (int i = kEntriesIndex; i < size; i++) {
- ASSERT(!get(i)->IsTheHole());
- get(i)->Verify();
- }
- for (int i = size; i < length(); i++) {
- ASSERT(get(i)->IsTheHole());
+ STATIC_ASSERT(2 == kEntrySize);
+ for (int i = kEntriesIndex; i < length(); i += kEntrySize) {
get(i)->Verify();
+ get(i + 1)->Verify();
+ // Key and value must be either both the holes, or not.
+ ASSERT(get(i)->IsTheHole() == get(i + 1)->IsTheHole());
}
}
}
diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h
index 7935912478..abfd4436df 100644
--- a/deps/v8/src/objects-inl.h
+++ b/deps/v8/src/objects-inl.h
@@ -730,6 +730,11 @@ bool Object::IsFalse() {
}
+bool Object::IsArgumentsMarker() {
+ return this == Heap::arguments_marker();
+}
+
+
double Object::Number() {
ASSERT(IsNumber());
return IsSmi()
@@ -2233,7 +2238,6 @@ InstanceType Map::instance_type() {
void Map::set_instance_type(InstanceType value) {
- ASSERT(0 <= value && value < 256);
WRITE_BYTE_FIELD(this, kInstanceTypeOffset, value);
}
@@ -2985,13 +2989,6 @@ Code* SharedFunctionInfo::unchecked_code() {
void SharedFunctionInfo::set_code(Code* value, WriteBarrierMode mode) {
- // If optimization has been disabled for the shared function info,
- // reflect that in the code object so it will not be counted as
- // optimizable code.
- ASSERT(value->kind() != Code::FUNCTION ||
- !value->optimizable() ||
- this->code() == Builtins::builtin(Builtins::Illegal) ||
- this->allows_lazy_compilation());
WRITE_FIELD(this, kCodeOffset, value);
CONDITIONAL_WRITE_BARRIER(this, kCodeOffset, mode);
}
@@ -3211,28 +3208,28 @@ int JSFunction::NumberOfLiterals() {
Object* JSBuiltinsObject::javascript_builtin(Builtins::JavaScript id) {
- ASSERT(0 <= id && id < kJSBuiltinsCount);
+ ASSERT(id < kJSBuiltinsCount); // id is unsigned.
return READ_FIELD(this, OffsetOfFunctionWithId(id));
}
void JSBuiltinsObject::set_javascript_builtin(Builtins::JavaScript id,
Object* value) {
- ASSERT(0 <= id && id < kJSBuiltinsCount);
+ ASSERT(id < kJSBuiltinsCount); // id is unsigned.
WRITE_FIELD(this, OffsetOfFunctionWithId(id), value);
WRITE_BARRIER(this, OffsetOfFunctionWithId(id));
}
Code* JSBuiltinsObject::javascript_builtin_code(Builtins::JavaScript id) {
- ASSERT(0 <= id && id < kJSBuiltinsCount);
+ ASSERT(id < kJSBuiltinsCount); // id is unsigned.
return Code::cast(READ_FIELD(this, OffsetOfCodeWithId(id)));
}
void JSBuiltinsObject::set_javascript_builtin_code(Builtins::JavaScript id,
Code* value) {
- ASSERT(0 <= id && id < kJSBuiltinsCount);
+ ASSERT(id < kJSBuiltinsCount); // id is unsigned.
WRITE_FIELD(this, OffsetOfCodeWithId(id), value);
ASSERT(!Heap::InNewSpace(value));
}
diff --git a/deps/v8/src/objects-printer.cc b/deps/v8/src/objects-printer.cc
new file mode 100644
index 0000000000..9879da25d6
--- /dev/null
+++ b/deps/v8/src/objects-printer.cc
@@ -0,0 +1,778 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "v8.h"
+
+#include "disassembler.h"
+#include "disasm.h"
+#include "jsregexp.h"
+#include "objects-visiting.h"
+
+namespace v8 {
+namespace internal {
+
+#ifdef OBJECT_PRINT
+
+static const char* TypeToString(InstanceType type);
+
+
+void MaybeObject::Print(FILE* out) {
+ Object* this_as_object;
+ if (ToObject(&this_as_object)) {
+ if (this_as_object->IsSmi()) {
+ Smi::cast(this_as_object)->SmiPrint(out);
+ } else {
+ HeapObject::cast(this_as_object)->HeapObjectPrint(out);
+ }
+ } else {
+ Failure::cast(this)->FailurePrint(out);
+ }
+ Flush(out);
+}
+
+
+void MaybeObject::PrintLn(FILE* out) {
+ Print(out);
+ PrintF(out, "\n");
+}
+
+
+void HeapObject::PrintHeader(FILE* out, const char* id) {
+ PrintF(out, "%p: [%s]\n", reinterpret_cast<void*>(this), id);
+}
+
+
+void HeapObject::HeapObjectPrint(FILE* out) {
+ InstanceType instance_type = map()->instance_type();
+
+ HandleScope scope;
+ if (instance_type < FIRST_NONSTRING_TYPE) {
+ String::cast(this)->StringPrint(out);
+ return;
+ }
+
+ switch (instance_type) {
+ case MAP_TYPE:
+ Map::cast(this)->MapPrint(out);
+ break;
+ case HEAP_NUMBER_TYPE:
+ HeapNumber::cast(this)->HeapNumberPrint(out);
+ break;
+ case FIXED_ARRAY_TYPE:
+ FixedArray::cast(this)->FixedArrayPrint(out);
+ break;
+ case BYTE_ARRAY_TYPE:
+ ByteArray::cast(this)->ByteArrayPrint(out);
+ break;
+ case PIXEL_ARRAY_TYPE:
+ PixelArray::cast(this)->PixelArrayPrint(out);
+ break;
+ case EXTERNAL_BYTE_ARRAY_TYPE:
+ ExternalByteArray::cast(this)->ExternalByteArrayPrint(out);
+ break;
+ case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
+ ExternalUnsignedByteArray::cast(this)
+ ->ExternalUnsignedByteArrayPrint(out);
+ break;
+ case EXTERNAL_SHORT_ARRAY_TYPE:
+ ExternalShortArray::cast(this)->ExternalShortArrayPrint(out);
+ break;
+ case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
+ ExternalUnsignedShortArray::cast(this)
+ ->ExternalUnsignedShortArrayPrint(out);
+ break;
+ case EXTERNAL_INT_ARRAY_TYPE:
+ ExternalIntArray::cast(this)->ExternalIntArrayPrint(out);
+ break;
+ case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
+ ExternalUnsignedIntArray::cast(this)->ExternalUnsignedIntArrayPrint(out);
+ break;
+ case EXTERNAL_FLOAT_ARRAY_TYPE:
+ ExternalFloatArray::cast(this)->ExternalFloatArrayPrint(out);
+ break;
+ case FILLER_TYPE:
+ PrintF(out, "filler");
+ break;
+ case JS_OBJECT_TYPE: // fall through
+ case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
+ case JS_ARRAY_TYPE:
+ case JS_REGEXP_TYPE:
+ JSObject::cast(this)->JSObjectPrint(out);
+ break;
+ case ODDBALL_TYPE:
+ Oddball::cast(this)->to_string()->Print(out);
+ break;
+ case JS_FUNCTION_TYPE:
+ JSFunction::cast(this)->JSFunctionPrint(out);
+ break;
+ case JS_GLOBAL_PROXY_TYPE:
+ JSGlobalProxy::cast(this)->JSGlobalProxyPrint(out);
+ break;
+ case JS_GLOBAL_OBJECT_TYPE:
+ JSGlobalObject::cast(this)->JSGlobalObjectPrint(out);
+ break;
+ case JS_BUILTINS_OBJECT_TYPE:
+ JSBuiltinsObject::cast(this)->JSBuiltinsObjectPrint(out);
+ break;
+ case JS_VALUE_TYPE:
+ PrintF(out, "Value wrapper around:");
+ JSValue::cast(this)->value()->Print(out);
+ break;
+ case CODE_TYPE:
+ Code::cast(this)->CodePrint(out);
+ break;
+ case PROXY_TYPE:
+ Proxy::cast(this)->ProxyPrint(out);
+ break;
+ case SHARED_FUNCTION_INFO_TYPE:
+ SharedFunctionInfo::cast(this)->SharedFunctionInfoPrint(out);
+ break;
+ case JS_GLOBAL_PROPERTY_CELL_TYPE:
+ JSGlobalPropertyCell::cast(this)->JSGlobalPropertyCellPrint(out);
+ break;
+#define MAKE_STRUCT_CASE(NAME, Name, name) \
+ case NAME##_TYPE: \
+ Name::cast(this)->Name##Print(out); \
+ break;
+ STRUCT_LIST(MAKE_STRUCT_CASE)
+#undef MAKE_STRUCT_CASE
+
+ default:
+ PrintF(out, "UNKNOWN TYPE %d", map()->instance_type());
+ UNREACHABLE();
+ break;
+ }
+}
+
+
+void ByteArray::ByteArrayPrint(FILE* out) {
+ PrintF(out, "byte array, data starts at %p", GetDataStartAddress());
+}
+
+
+void PixelArray::PixelArrayPrint(FILE* out) {
+ PrintF(out, "pixel array");
+}
+
+
+void ExternalByteArray::ExternalByteArrayPrint(FILE* out) {
+ PrintF(out, "external byte array");
+}
+
+
+void ExternalUnsignedByteArray::ExternalUnsignedByteArrayPrint(FILE* out) {
+ PrintF(out, "external unsigned byte array");
+}
+
+
+void ExternalShortArray::ExternalShortArrayPrint(FILE* out) {
+ PrintF(out, "external short array");
+}
+
+
+void ExternalUnsignedShortArray::ExternalUnsignedShortArrayPrint(FILE* out) {
+ PrintF(out, "external unsigned short array");
+}
+
+
+void ExternalIntArray::ExternalIntArrayPrint(FILE* out) {
+ PrintF(out, "external int array");
+}
+
+
+void ExternalUnsignedIntArray::ExternalUnsignedIntArrayPrint(FILE* out) {
+ PrintF(out, "external unsigned int array");
+}
+
+
+void ExternalFloatArray::ExternalFloatArrayPrint(FILE* out) {
+ PrintF(out, "external float array");
+}
+
+
+void JSObject::PrintProperties(FILE* out) {
+ if (HasFastProperties()) {
+ DescriptorArray* descs = map()->instance_descriptors();
+ for (int i = 0; i < descs->number_of_descriptors(); i++) {
+ PrintF(out, " ");
+ descs->GetKey(i)->StringPrint(out);
+ PrintF(out, ": ");
+ switch (descs->GetType(i)) {
+ case FIELD: {
+ int index = descs->GetFieldIndex(i);
+ FastPropertyAt(index)->ShortPrint(out);
+ PrintF(out, " (field at offset %d)\n", index);
+ break;
+ }
+ case CONSTANT_FUNCTION:
+ descs->GetConstantFunction(i)->ShortPrint(out);
+ PrintF(out, " (constant function)\n");
+ break;
+ case CALLBACKS:
+ descs->GetCallbacksObject(i)->ShortPrint(out);
+ PrintF(out, " (callback)\n");
+ break;
+ case MAP_TRANSITION:
+ PrintF(out, " (map transition)\n");
+ break;
+ case CONSTANT_TRANSITION:
+ PrintF(out, " (constant transition)\n");
+ break;
+ case NULL_DESCRIPTOR:
+ PrintF(out, " (null descriptor)\n");
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ } else {
+ property_dictionary()->Print(out);
+ }
+}
+
+
+void JSObject::PrintElements(FILE* out) {
+ switch (GetElementsKind()) {
+ case FAST_ELEMENTS: {
+ // Print in array notation for non-sparse arrays.
+ FixedArray* p = FixedArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: ", i);
+ p->get(i)->ShortPrint(out);
+ PrintF(out, "\n");
+ }
+ break;
+ }
+ case PIXEL_ELEMENTS: {
+ PixelArray* p = PixelArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: %d\n", i, p->get(i));
+ }
+ break;
+ }
+ case EXTERNAL_BYTE_ELEMENTS: {
+ ExternalByteArray* p = ExternalByteArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: %d\n", i, static_cast<int>(p->get(i)));
+ }
+ break;
+ }
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
+ ExternalUnsignedByteArray* p =
+ ExternalUnsignedByteArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: %d\n", i, static_cast<int>(p->get(i)));
+ }
+ break;
+ }
+ case EXTERNAL_SHORT_ELEMENTS: {
+ ExternalShortArray* p = ExternalShortArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: %d\n", i, static_cast<int>(p->get(i)));
+ }
+ break;
+ }
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
+ ExternalUnsignedShortArray* p =
+ ExternalUnsignedShortArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: %d\n", i, static_cast<int>(p->get(i)));
+ }
+ break;
+ }
+ case EXTERNAL_INT_ELEMENTS: {
+ ExternalIntArray* p = ExternalIntArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: %d\n", i, static_cast<int>(p->get(i)));
+ }
+ break;
+ }
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
+ ExternalUnsignedIntArray* p =
+ ExternalUnsignedIntArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: %d\n", i, static_cast<int>(p->get(i)));
+ }
+ break;
+ }
+ case EXTERNAL_FLOAT_ELEMENTS: {
+ ExternalFloatArray* p = ExternalFloatArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: %f\n", i, p->get(i));
+ }
+ break;
+ }
+ case DICTIONARY_ELEMENTS:
+ elements()->Print(out);
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+}
+
+
+void JSObject::JSObjectPrint(FILE* out) {
+ PrintF(out, "%p: [JSObject]\n", reinterpret_cast<void*>(this));
+ PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
+ PrintF(out, " - prototype = %p\n", reinterpret_cast<void*>(GetPrototype()));
+ PrintF(out, " {\n");
+ PrintProperties(out);
+ PrintElements(out);
+ PrintF(out, " }\n");
+}
+
+
+static const char* TypeToString(InstanceType type) {
+ switch (type) {
+ case INVALID_TYPE: return "INVALID";
+ case MAP_TYPE: return "MAP";
+ case HEAP_NUMBER_TYPE: return "HEAP_NUMBER";
+ case SYMBOL_TYPE: return "SYMBOL";
+ case ASCII_SYMBOL_TYPE: return "ASCII_SYMBOL";
+ case CONS_SYMBOL_TYPE: return "CONS_SYMBOL";
+ case CONS_ASCII_SYMBOL_TYPE: return "CONS_ASCII_SYMBOL";
+ case EXTERNAL_ASCII_SYMBOL_TYPE:
+ case EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE:
+ case EXTERNAL_SYMBOL_TYPE: return "EXTERNAL_SYMBOL";
+ case ASCII_STRING_TYPE: return "ASCII_STRING";
+ case STRING_TYPE: return "TWO_BYTE_STRING";
+ case CONS_STRING_TYPE:
+ case CONS_ASCII_STRING_TYPE: return "CONS_STRING";
+ case EXTERNAL_ASCII_STRING_TYPE:
+ case EXTERNAL_STRING_WITH_ASCII_DATA_TYPE:
+ case EXTERNAL_STRING_TYPE: return "EXTERNAL_STRING";
+ case FIXED_ARRAY_TYPE: return "FIXED_ARRAY";
+ case BYTE_ARRAY_TYPE: return "BYTE_ARRAY";
+ case PIXEL_ARRAY_TYPE: return "PIXEL_ARRAY";
+ case EXTERNAL_BYTE_ARRAY_TYPE: return "EXTERNAL_BYTE_ARRAY";
+ case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
+ return "EXTERNAL_UNSIGNED_BYTE_ARRAY";
+ case EXTERNAL_SHORT_ARRAY_TYPE: return "EXTERNAL_SHORT_ARRAY";
+ case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
+ return "EXTERNAL_UNSIGNED_SHORT_ARRAY";
+ case EXTERNAL_INT_ARRAY_TYPE: return "EXTERNAL_INT_ARRAY";
+ case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
+ return "EXTERNAL_UNSIGNED_INT_ARRAY";
+ case EXTERNAL_FLOAT_ARRAY_TYPE: return "EXTERNAL_FLOAT_ARRAY";
+ case FILLER_TYPE: return "FILLER";
+ case JS_OBJECT_TYPE: return "JS_OBJECT";
+ case JS_CONTEXT_EXTENSION_OBJECT_TYPE: return "JS_CONTEXT_EXTENSION_OBJECT";
+ case ODDBALL_TYPE: return "ODDBALL";
+ case JS_GLOBAL_PROPERTY_CELL_TYPE: return "JS_GLOBAL_PROPERTY_CELL";
+ case SHARED_FUNCTION_INFO_TYPE: return "SHARED_FUNCTION_INFO";
+ case JS_FUNCTION_TYPE: return "JS_FUNCTION";
+ case CODE_TYPE: return "CODE";
+ case JS_ARRAY_TYPE: return "JS_ARRAY";
+ case JS_REGEXP_TYPE: return "JS_REGEXP";
+ case JS_VALUE_TYPE: return "JS_VALUE";
+ case JS_GLOBAL_OBJECT_TYPE: return "JS_GLOBAL_OBJECT";
+ case JS_BUILTINS_OBJECT_TYPE: return "JS_BUILTINS_OBJECT";
+ case JS_GLOBAL_PROXY_TYPE: return "JS_GLOBAL_PROXY";
+ case PROXY_TYPE: return "PROXY";
+#define MAKE_STRUCT_CASE(NAME, Name, name) case NAME##_TYPE: return #NAME;
+ STRUCT_LIST(MAKE_STRUCT_CASE)
+#undef MAKE_STRUCT_CASE
+ }
+ return "UNKNOWN";
+}
+
+
+void Map::MapPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "Map");
+ PrintF(out, " - type: %s\n", TypeToString(instance_type()));
+ PrintF(out, " - instance size: %d\n", instance_size());
+ PrintF(out, " - inobject properties: %d\n", inobject_properties());
+ PrintF(out, " - pre-allocated property fields: %d\n",
+ pre_allocated_property_fields());
+ PrintF(out, " - unused property fields: %d\n", unused_property_fields());
+ if (is_hidden_prototype()) {
+ PrintF(out, " - hidden_prototype\n");
+ }
+ if (has_named_interceptor()) {
+ PrintF(out, " - named_interceptor\n");
+ }
+ if (has_indexed_interceptor()) {
+ PrintF(out, " - indexed_interceptor\n");
+ }
+ if (is_undetectable()) {
+ PrintF(out, " - undetectable\n");
+ }
+ if (has_instance_call_handler()) {
+ PrintF(out, " - instance_call_handler\n");
+ }
+ if (is_access_check_needed()) {
+ PrintF(out, " - access_check_needed\n");
+ }
+ PrintF(out, " - instance descriptors: ");
+ instance_descriptors()->ShortPrint(out);
+ PrintF(out, "\n - prototype: ");
+ prototype()->ShortPrint(out);
+ PrintF(out, "\n - constructor: ");
+ constructor()->ShortPrint(out);
+ PrintF(out, "\n");
+}
+
+
+void CodeCache::CodeCachePrint(FILE* out) {
+ HeapObject::PrintHeader(out, "CodeCache");
+ PrintF(out, "\n - default_cache: ");
+ default_cache()->ShortPrint(out);
+ PrintF(out, "\n - normal_type_cache: ");
+ normal_type_cache()->ShortPrint(out);
+}
+
+
+void FixedArray::FixedArrayPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "FixedArray");
+ PrintF(out, " - length: %d", length());
+ for (int i = 0; i < length(); i++) {
+ PrintF(out, "\n [%d]: ", i);
+ get(i)->ShortPrint(out);
+ }
+ PrintF(out, "\n");
+}
+
+
+void JSValue::JSValuePrint(FILE* out) {
+ HeapObject::PrintHeader(out, "ValueObject");
+ value()->Print(out);
+}
+
+
+void String::StringPrint(FILE* out) {
+ if (StringShape(this).IsSymbol()) {
+ PrintF(out, "#");
+ } else if (StringShape(this).IsCons()) {
+ PrintF(out, "c\"");
+ } else {
+ PrintF(out, "\"");
+ }
+
+ const char truncated_epilogue[] = "...<truncated>";
+ int len = length();
+ if (!FLAG_use_verbose_printer) {
+ if (len > 100) {
+ len = 100 - sizeof(truncated_epilogue);
+ }
+ }
+ for (int i = 0; i < len; i++) {
+ PrintF(out, "%c", Get(i));
+ }
+ if (len != length()) {
+ PrintF(out, "%s", truncated_epilogue);
+ }
+
+ if (!StringShape(this).IsSymbol()) PrintF(out, "\"");
+}
+
+
+void JSFunction::JSFunctionPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "Function");
+ PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map()));
+ PrintF(out, " - initial_map = ");
+ if (has_initial_map()) {
+ initial_map()->ShortPrint(out);
+ }
+ PrintF(out, "\n - shared_info = ");
+ shared()->ShortPrint(out);
+ PrintF(out, "\n - name = ");
+ shared()->name()->Print(out);
+ PrintF(out, "\n - context = ");
+ unchecked_context()->ShortPrint(out);
+ PrintF(out, "\n - code = ");
+ code()->ShortPrint(out);
+ PrintF(out, "\n");
+
+ PrintProperties(out);
+ PrintElements(out);
+
+ PrintF(out, "\n");
+}
+
+
+void SharedFunctionInfo::SharedFunctionInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "SharedFunctionInfo");
+ PrintF(out, " - name: ");
+ name()->ShortPrint(out);
+ PrintF(out, "\n - expected_nof_properties: %d", expected_nof_properties());
+ PrintF(out, "\n - instance class name = ");
+ instance_class_name()->Print(out);
+ PrintF(out, "\n - code = ");
+ code()->ShortPrint(out);
+ PrintF(out, "\n - source code = ");
+ GetSourceCode()->ShortPrint(out);
+ // Script files are often large, hard to read.
+ // PrintF(out, "\n - script =");
+ // script()->Print(out);
+ PrintF(out, "\n - function token position = %d", function_token_position());
+ PrintF(out, "\n - start position = %d", start_position());
+ PrintF(out, "\n - end position = %d", end_position());
+ PrintF(out, "\n - is expression = %d", is_expression());
+ PrintF(out, "\n - debug info = ");
+ debug_info()->ShortPrint(out);
+ PrintF(out, "\n - length = %d", length());
+ PrintF(out, "\n - has_only_simple_this_property_assignments = %d",
+ has_only_simple_this_property_assignments());
+ PrintF(out, "\n - this_property_assignments = ");
+ this_property_assignments()->ShortPrint(out);
+ PrintF(out, "\n");
+}
+
+
+void JSGlobalProxy::JSGlobalProxyPrint(FILE* out) {
+ PrintF(out, "global_proxy");
+ JSObjectPrint(out);
+ PrintF(out, "context : ");
+ context()->ShortPrint(out);
+ PrintF(out, "\n");
+}
+
+
+void JSGlobalObject::JSGlobalObjectPrint(FILE* out) {
+ PrintF(out, "global ");
+ JSObjectPrint(out);
+ PrintF(out, "global context : ");
+ global_context()->ShortPrint(out);
+ PrintF(out, "\n");
+}
+
+
+void JSBuiltinsObject::JSBuiltinsObjectPrint(FILE* out) {
+ PrintF(out, "builtins ");
+ JSObjectPrint(out);
+}
+
+
+void JSGlobalPropertyCell::JSGlobalPropertyCellPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "JSGlobalPropertyCell");
+}
+
+
+void Code::CodePrint(FILE* out) {
+ HeapObject::PrintHeader(out, "Code");
+#ifdef ENABLE_DISASSEMBLER
+ if (FLAG_use_verbose_printer) {
+ Disassemble(NULL, out);
+ }
+#endif
+}
+
+
+void Proxy::ProxyPrint(FILE* out) {
+ PrintF(out, "proxy to %p", proxy());
+}
+
+
+void AccessorInfo::AccessorInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "AccessorInfo");
+ PrintF(out, "\n - getter: ");
+ getter()->ShortPrint(out);
+ PrintF(out, "\n - setter: ");
+ setter()->ShortPrint(out);
+ PrintF(out, "\n - name: ");
+ name()->ShortPrint(out);
+ PrintF(out, "\n - data: ");
+ data()->ShortPrint(out);
+ PrintF(out, "\n - flag: ");
+ flag()->ShortPrint(out);
+}
+
+
+void AccessCheckInfo::AccessCheckInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "AccessCheckInfo");
+ PrintF(out, "\n - named_callback: ");
+ named_callback()->ShortPrint(out);
+ PrintF(out, "\n - indexed_callback: ");
+ indexed_callback()->ShortPrint(out);
+ PrintF(out, "\n - data: ");
+ data()->ShortPrint(out);
+}
+
+
+void InterceptorInfo::InterceptorInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "InterceptorInfo");
+ PrintF(out, "\n - getter: ");
+ getter()->ShortPrint(out);
+ PrintF(out, "\n - setter: ");
+ setter()->ShortPrint(out);
+ PrintF(out, "\n - query: ");
+ query()->ShortPrint(out);
+ PrintF(out, "\n - deleter: ");
+ deleter()->ShortPrint(out);
+ PrintF(out, "\n - enumerator: ");
+ enumerator()->ShortPrint(out);
+ PrintF(out, "\n - data: ");
+ data()->ShortPrint(out);
+}
+
+
+void CallHandlerInfo::CallHandlerInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "CallHandlerInfo");
+ PrintF(out, "\n - callback: ");
+ callback()->ShortPrint(out);
+ PrintF(out, "\n - data: ");
+ data()->ShortPrint(out);
+ PrintF(out, "\n - call_stub_cache: ");
+}
+
+
+void FunctionTemplateInfo::FunctionTemplateInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "FunctionTemplateInfo");
+ PrintF(out, "\n - class name: ");
+ class_name()->ShortPrint(out);
+ PrintF(out, "\n - tag: ");
+ tag()->ShortPrint(out);
+ PrintF(out, "\n - property_list: ");
+ property_list()->ShortPrint(out);
+ PrintF(out, "\n - serial_number: ");
+ serial_number()->ShortPrint(out);
+ PrintF(out, "\n - call_code: ");
+ call_code()->ShortPrint(out);
+ PrintF(out, "\n - property_accessors: ");
+ property_accessors()->ShortPrint(out);
+ PrintF(out, "\n - prototype_template: ");
+ prototype_template()->ShortPrint(out);
+ PrintF(out, "\n - parent_template: ");
+ parent_template()->ShortPrint(out);
+ PrintF(out, "\n - named_property_handler: ");
+ named_property_handler()->ShortPrint(out);
+ PrintF(out, "\n - indexed_property_handler: ");
+ indexed_property_handler()->ShortPrint(out);
+ PrintF(out, "\n - instance_template: ");
+ instance_template()->ShortPrint(out);
+ PrintF(out, "\n - signature: ");
+ signature()->ShortPrint(out);
+ PrintF(out, "\n - access_check_info: ");
+ access_check_info()->ShortPrint(out);
+ PrintF(out, "\n - hidden_prototype: %s",
+ hidden_prototype() ? "true" : "false");
+ PrintF(out, "\n - undetectable: %s", undetectable() ? "true" : "false");
+ PrintF(out, "\n - need_access_check: %s",
+ needs_access_check() ? "true" : "false");
+}
+
+
+void ObjectTemplateInfo::ObjectTemplateInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "ObjectTemplateInfo");
+ PrintF(out, "\n - constructor: ");
+ constructor()->ShortPrint(out);
+ PrintF(out, "\n - internal_field_count: ");
+ internal_field_count()->ShortPrint(out);
+}
+
+
+void SignatureInfo::SignatureInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "SignatureInfo");
+ PrintF(out, "\n - receiver: ");
+ receiver()->ShortPrint(out);
+ PrintF(out, "\n - args: ");
+ args()->ShortPrint(out);
+}
+
+
+void TypeSwitchInfo::TypeSwitchInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "TypeSwitchInfo");
+ PrintF(out, "\n - types: ");
+ types()->ShortPrint(out);
+}
+
+
+void Script::ScriptPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "Script");
+ PrintF(out, "\n - source: ");
+ source()->ShortPrint(out);
+ PrintF(out, "\n - name: ");
+ name()->ShortPrint(out);
+ PrintF(out, "\n - line_offset: ");
+ line_offset()->ShortPrint(out);
+ PrintF(out, "\n - column_offset: ");
+ column_offset()->ShortPrint(out);
+ PrintF(out, "\n - type: ");
+ type()->ShortPrint(out);
+ PrintF(out, "\n - id: ");
+ id()->ShortPrint(out);
+ PrintF(out, "\n - data: ");
+ data()->ShortPrint(out);
+ PrintF(out, "\n - context data: ");
+ context_data()->ShortPrint(out);
+ PrintF(out, "\n - wrapper: ");
+ wrapper()->ShortPrint(out);
+ PrintF(out, "\n - compilation type: ");
+ compilation_type()->ShortPrint(out);
+ PrintF(out, "\n - line ends: ");
+ line_ends()->ShortPrint(out);
+ PrintF(out, "\n - eval from shared: ");
+ eval_from_shared()->ShortPrint(out);
+ PrintF(out, "\n - eval from instructions offset: ");
+ eval_from_instructions_offset()->ShortPrint(out);
+ PrintF(out, "\n");
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+void DebugInfo::DebugInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "DebugInfo");
+ PrintF(out, "\n - shared: ");
+ shared()->ShortPrint(out);
+ PrintF(out, "\n - original_code: ");
+ original_code()->ShortPrint(out);
+ PrintF(out, "\n - code: ");
+ code()->ShortPrint(out);
+ PrintF(out, "\n - break_points: ");
+ break_points()->Print(out);
+}
+
+
+void BreakPointInfo::BreakPointInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "BreakPointInfo");
+ PrintF(out, "\n - code_position: %d", code_position()->value());
+ PrintF(out, "\n - source_position: %d", source_position()->value());
+ PrintF(out, "\n - statement_position: %d", statement_position()->value());
+ PrintF(out, "\n - break_point_objects: ");
+ break_point_objects()->ShortPrint(out);
+}
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+
+void DescriptorArray::PrintDescriptors(FILE* out) {
+ PrintF(out, "Descriptor array %d\n", number_of_descriptors());
+ for (int i = 0; i < number_of_descriptors(); i++) {
+ PrintF(out, " %d: ", i);
+ Descriptor desc;
+ Get(i, &desc);
+ desc.Print(out);
+ }
+ PrintF(out, "\n");
+}
+
+
+#endif // OBJECT_PRINT
+
+
+} } // namespace v8::internal
diff --git a/deps/v8/src/objects-visiting.h b/deps/v8/src/objects-visiting.h
index 55a0a53aff..ea6d7954ed 100644
--- a/deps/v8/src/objects-visiting.h
+++ b/deps/v8/src/objects-visiting.h
@@ -146,7 +146,7 @@ class VisitorDispatchTable {
}
void Register(StaticVisitorBase::VisitorId id, Callback callback) {
- ASSERT((0 <= id) && (id < StaticVisitorBase::kVisitorIdCount));
+ ASSERT(id < StaticVisitorBase::kVisitorIdCount); // id is unsigned.
callbacks_[id] = callback;
}
@@ -186,9 +186,9 @@ class VisitorDispatchTable {
template<typename StaticVisitor>
class BodyVisitorBase : public AllStatic {
public:
- static inline void IteratePointers(HeapObject* object,
+ INLINE(static void IteratePointers(HeapObject* object,
int start_offset,
- int end_offset) {
+ int end_offset)) {
Object** start_slot = reinterpret_cast<Object**>(object->address() +
start_offset);
Object** end_slot = reinterpret_cast<Object**>(object->address() +
diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc
index ab2f964470..36a8e5c2aa 100644
--- a/deps/v8/src/objects.cc
+++ b/deps/v8/src/objects.cc
@@ -1823,8 +1823,9 @@ void JSObject::LookupRealNamedPropertyInPrototypes(String* name,
// We only need to deal with CALLBACKS and INTERCEPTORS
MaybeObject* JSObject::SetPropertyWithFailedAccessCheck(LookupResult* result,
String* name,
- Object* value) {
- if (!result->IsProperty()) {
+ Object* value,
+ bool check_prototype) {
+ if (check_prototype && !result->IsProperty()) {
LookupCallbackSetterInPrototypes(name, result);
}
@@ -1850,7 +1851,8 @@ MaybeObject* JSObject::SetPropertyWithFailedAccessCheck(LookupResult* result,
LookupResult r;
LookupRealNamedProperty(name, &r);
if (r.IsProperty()) {
- return SetPropertyWithFailedAccessCheck(&r, name, value);
+ return SetPropertyWithFailedAccessCheck(&r, name, value,
+ check_prototype);
}
break;
}
@@ -1891,7 +1893,7 @@ MaybeObject* JSObject::SetProperty(LookupResult* result,
// Check access rights if needed.
if (IsAccessCheckNeeded()
&& !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
- return SetPropertyWithFailedAccessCheck(result, name, value);
+ return SetPropertyWithFailedAccessCheck(result, name, value, true);
}
if (IsJSGlobalProxy()) {
@@ -1981,7 +1983,7 @@ MaybeObject* JSObject::SetProperty(LookupResult* result,
// callback setter removed. The two lines looking up the LookupResult
// result are also added. If one of the functions is changed, the other
// should be.
-MaybeObject* JSObject::IgnoreAttributesAndSetLocalProperty(
+MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
String* name,
Object* value,
PropertyAttributes attributes) {
@@ -1993,14 +1995,14 @@ MaybeObject* JSObject::IgnoreAttributesAndSetLocalProperty(
// Check access rights if needed.
if (IsAccessCheckNeeded()
&& !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
- return SetPropertyWithFailedAccessCheck(&result, name, value);
+ return SetPropertyWithFailedAccessCheck(&result, name, value, false);
}
if (IsJSGlobalProxy()) {
Object* proto = GetPrototype();
if (proto->IsNull()) return value;
ASSERT(proto->IsJSGlobalObject());
- return JSObject::cast(proto)->IgnoreAttributesAndSetLocalProperty(
+ return JSObject::cast(proto)->SetLocalPropertyIgnoreAttributes(
name,
value,
attributes);
@@ -2932,7 +2934,6 @@ MaybeObject* JSObject::DefineGetterSetter(String* name,
uint32_t index = 0;
bool is_element = name->AsArrayIndex(&index);
- if (is_element && IsJSArray()) return Heap::undefined_value();
if (is_element) {
switch (GetElementsKind()) {
@@ -5143,6 +5144,26 @@ bool String::IsEqualTo(Vector<const char> str) {
}
+bool String::IsAsciiEqualTo(Vector<const char> str) {
+ int slen = length();
+ if (str.length() != slen) return false;
+ for (int i = 0; i < slen; i++) {
+ if (Get(i) != static_cast<uint16_t>(str[i])) return false;
+ }
+ return true;
+}
+
+
+bool String::IsTwoByteEqualTo(Vector<const uc16> str) {
+ int slen = length();
+ if (str.length() != slen) return false;
+ for (int i = 0; i < slen; i++) {
+ if (Get(i) != str[i]) return false;
+ }
+ return true;
+}
+
+
template <typename schar>
static inline uint32_t HashSequentialString(const schar* chars, int length) {
StringHasher hasher(length);
@@ -5378,7 +5399,8 @@ void JSFunction::JSFunctionIterateBody(int object_size, ObjectVisitor* v) {
void JSFunction::MarkForLazyRecompilation() {
ASSERT(is_compiled() && !IsOptimized());
- ASSERT(shared()->allows_lazy_compilation());
+ ASSERT(shared()->allows_lazy_compilation() ||
+ code()->optimizable());
ReplaceCode(Builtins::builtin(Builtins::LazyRecompile));
}
@@ -5966,14 +5988,9 @@ int Code::SourceStatementPosition(Address pc) {
}
-uint8_t* Code::GetSafepointEntry(Address pc) {
+SafepointEntry Code::GetSafepointEntry(Address pc) {
SafepointTable table(this);
- unsigned pc_offset = static_cast<unsigned>(pc - instruction_start());
- for (unsigned i = 0; i < table.length(); i++) {
- // TODO(kasperl): Replace the linear search with binary search.
- if (table.GetPcOffset(i) == pc_offset) return table.GetEntry(i);
- }
- return NULL;
+ return table.FindEntry(pc);
}
@@ -6244,12 +6261,15 @@ void Code::Disassemble(const char* name, FILE* out) {
PrintF(out, "%p %4d ", (instruction_start() + pc_offset), pc_offset);
table.PrintEntry(i);
PrintF(out, " (sp -> fp)");
- int deoptimization_index = table.GetDeoptimizationIndex(i);
- if (deoptimization_index != Safepoint::kNoDeoptimizationIndex) {
- PrintF(out, " %6d", deoptimization_index);
+ SafepointEntry entry = table.GetEntry(i);
+ if (entry.deoptimization_index() != Safepoint::kNoDeoptimizationIndex) {
+ PrintF(out, " %6d", entry.deoptimization_index());
} else {
PrintF(out, " <none>");
}
+ if (entry.argument_count() > 0) {
+ PrintF(out, " argc: %d", entry.argument_count());
+ }
PrintF(out, "\n");
}
PrintF(out, "\n");
@@ -6775,7 +6795,8 @@ bool JSObject::HasElementWithReceiver(JSObject* receiver, uint32_t index) {
MaybeObject* JSObject::SetElementWithInterceptor(uint32_t index,
- Object* value) {
+ Object* value,
+ bool check_prototype) {
// Make sure that the top context does not change when doing
// callbacks or interceptor calls.
AssertNoContextChange ncc;
@@ -6799,7 +6820,9 @@ MaybeObject* JSObject::SetElementWithInterceptor(uint32_t index,
if (!result.IsEmpty()) return *value_handle;
}
MaybeObject* raw_result =
- this_handle->SetElementWithoutInterceptor(index, *value_handle);
+ this_handle->SetElementWithoutInterceptor(index,
+ *value_handle,
+ check_prototype);
RETURN_IF_SCHEDULED_EXCEPTION();
return raw_result;
}
@@ -6910,7 +6933,9 @@ MaybeObject* JSObject::SetElementWithCallback(Object* structure,
// Adding n elements in fast case is O(n*n).
// Note: revisit design to have dual undefined values to capture absent
// elements.
-MaybeObject* JSObject::SetFastElement(uint32_t index, Object* value) {
+MaybeObject* JSObject::SetFastElement(uint32_t index,
+ Object* value,
+ bool check_prototype) {
ASSERT(HasFastElements());
Object* elms_obj;
@@ -6920,12 +6945,13 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, Object* value) {
FixedArray* elms = FixedArray::cast(elms_obj);
uint32_t elms_length = static_cast<uint32_t>(elms->length());
- if (!IsJSArray() && (index >= elms_length || elms->get(index)->IsTheHole())) {
- if (SetElementWithCallbackSetterInPrototypes(index, value)) {
- return value;
- }
+ if (check_prototype &&
+ (index >= elms_length || elms->get(index)->IsTheHole()) &&
+ SetElementWithCallbackSetterInPrototypes(index, value)) {
+ return value;
}
+
// Check whether there is extra space in fixed array..
if (index < elms_length) {
elms->set(index, value);
@@ -6963,11 +6989,13 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, Object* value) {
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
ASSERT(HasDictionaryElements());
- return SetElement(index, value);
+ return SetElement(index, value, check_prototype);
}
-MaybeObject* JSObject::SetElement(uint32_t index, Object* value) {
+MaybeObject* JSObject::SetElement(uint32_t index,
+ Object* value,
+ bool check_prototype) {
// Check access rights if needed.
if (IsAccessCheckNeeded() &&
!Top::MayIndexedAccess(this, index, v8::ACCESS_SET)) {
@@ -6981,24 +7009,25 @@ MaybeObject* JSObject::SetElement(uint32_t index, Object* value) {
Object* proto = GetPrototype();
if (proto->IsNull()) return value;
ASSERT(proto->IsJSGlobalObject());
- return JSObject::cast(proto)->SetElement(index, value);
+ return JSObject::cast(proto)->SetElement(index, value, check_prototype);
}
// Check for lookup interceptor
if (HasIndexedInterceptor()) {
- return SetElementWithInterceptor(index, value);
+ return SetElementWithInterceptor(index, value, check_prototype);
}
- return SetElementWithoutInterceptor(index, value);
+ return SetElementWithoutInterceptor(index, value, check_prototype);
}
MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index,
- Object* value) {
+ Object* value,
+ bool check_prototype) {
switch (GetElementsKind()) {
case FAST_ELEMENTS:
// Fast case.
- return SetFastElement(index, value);
+ return SetFastElement(index, value, check_prototype);
case PIXEL_ELEMENTS: {
PixelArray* pixels = PixelArray::cast(elements());
return pixels->SetValue(index, value);
@@ -7051,10 +7080,9 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index,
}
} else {
// Index not already used. Look for an accessor in the prototype chain.
- if (!IsJSArray()) {
- if (SetElementWithCallbackSetterInPrototypes(index, value)) {
- return value;
- }
+ if (check_prototype &&
+ SetElementWithCallbackSetterInPrototypes(index, value)) {
+ return value;
}
// When we set the is_extensible flag to false we always force
// the element into dictionary mode (and force them to stay there).
@@ -8086,6 +8114,85 @@ class Utf8SymbolKey : public HashTableKey {
};
+template <typename Char>
+class SequentialSymbolKey : public HashTableKey {
+ public:
+ explicit SequentialSymbolKey(Vector<const Char> string)
+ : string_(string), hash_field_(0) { }
+
+ uint32_t Hash() {
+ StringHasher hasher(string_.length());
+
+ // Very long strings have a trivial hash that doesn't inspect the
+ // string contents.
+ if (hasher.has_trivial_hash()) {
+ hash_field_ = hasher.GetHashField();
+ } else {
+ int i = 0;
+ // Do the iterative array index computation as long as there is a
+ // chance this is an array index.
+ while (i < string_.length() && hasher.is_array_index()) {
+ hasher.AddCharacter(static_cast<uc32>(string_[i]));
+ i++;
+ }
+
+ // Process the remaining characters without updating the array
+ // index.
+ while (i < string_.length()) {
+ hasher.AddCharacterNoIndex(static_cast<uc32>(string_[i]));
+ i++;
+ }
+ hash_field_ = hasher.GetHashField();
+ }
+
+ uint32_t result = hash_field_ >> String::kHashShift;
+ ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
+ return result;
+ }
+
+
+ uint32_t HashForObject(Object* other) {
+ return String::cast(other)->Hash();
+ }
+
+ Vector<const Char> string_;
+ uint32_t hash_field_;
+};
+
+
+
+class AsciiSymbolKey : public SequentialSymbolKey<char> {
+ public:
+ explicit AsciiSymbolKey(Vector<const char> str)
+ : SequentialSymbolKey<char>(str) { }
+
+ bool IsMatch(Object* string) {
+ return String::cast(string)->IsAsciiEqualTo(string_);
+ }
+
+ MaybeObject* AsObject() {
+ if (hash_field_ == 0) Hash();
+ return Heap::AllocateAsciiSymbol(string_, hash_field_);
+ }
+};
+
+
+class TwoByteSymbolKey : public SequentialSymbolKey<uc16> {
+ public:
+ explicit TwoByteSymbolKey(Vector<const uc16> str)
+ : SequentialSymbolKey<uc16>(str) { }
+
+ bool IsMatch(Object* string) {
+ return String::cast(string)->IsTwoByteEqualTo(string_);
+ }
+
+ MaybeObject* AsObject() {
+ if (hash_field_ == 0) Hash();
+ return Heap::AllocateTwoByteSymbol(string_, hash_field_);
+ }
+};
+
+
// SymbolKey carries a string/symbol object as key.
class SymbolKey : public HashTableKey {
public:
@@ -8830,6 +8937,19 @@ MaybeObject* SymbolTable::LookupSymbol(Vector<const char> str, Object** s) {
}
+MaybeObject* SymbolTable::LookupAsciiSymbol(Vector<const char> str,
+ Object** s) {
+ AsciiSymbolKey key(str);
+ return LookupKey(&key, s);
+}
+
+
+MaybeObject* SymbolTable::LookupTwoByteSymbol(Vector<const uc16> str,
+ Object** s) {
+ TwoByteSymbolKey key(str);
+ return LookupKey(&key, s);
+}
+
MaybeObject* SymbolTable::LookupKey(HashTableKey* key, Object** s) {
int entry = FindEntry(key);
diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h
index c5fda7d038..c136dc59b5 100644
--- a/deps/v8/src/objects.h
+++ b/deps/v8/src/objects.h
@@ -709,6 +709,7 @@ class Object : public MaybeObject {
INLINE(bool IsNull());
INLINE(bool IsTrue());
INLINE(bool IsFalse());
+ inline bool IsArgumentsMarker();
// Extract the number.
inline double Number();
@@ -1341,7 +1342,8 @@ class JSObject: public HeapObject {
MUST_USE_RESULT MaybeObject* SetPropertyWithFailedAccessCheck(
LookupResult* result,
String* name,
- Object* value);
+ Object* value,
+ bool check_prototype);
MUST_USE_RESULT MaybeObject* SetPropertyWithCallback(Object* structure,
String* name,
Object* value,
@@ -1356,7 +1358,7 @@ class JSObject: public HeapObject {
String* name,
Object* value,
PropertyAttributes attributes);
- MUST_USE_RESULT MaybeObject* IgnoreAttributesAndSetLocalProperty(
+ MUST_USE_RESULT MaybeObject* SetLocalPropertyIgnoreAttributes(
String* key,
Object* value,
PropertyAttributes attributes);
@@ -1505,11 +1507,15 @@ class JSObject: public HeapObject {
bool HasElementWithInterceptor(JSObject* receiver, uint32_t index);
bool HasElementPostInterceptor(JSObject* receiver, uint32_t index);
- MUST_USE_RESULT MaybeObject* SetFastElement(uint32_t index, Object* value);
+ MUST_USE_RESULT MaybeObject* SetFastElement(uint32_t index,
+ Object* value,
+ bool check_prototype = true);
// Set the index'th array element.
// A Failure object is returned if GC is needed.
- MUST_USE_RESULT MaybeObject* SetElement(uint32_t index, Object* value);
+ MUST_USE_RESULT MaybeObject* SetElement(uint32_t index,
+ Object* value,
+ bool check_prototype = true);
// Returns the index'th element.
// The undefined object if index is out of bounds.
@@ -1763,9 +1769,12 @@ class JSObject: public HeapObject {
Object* value,
JSObject* holder);
MUST_USE_RESULT MaybeObject* SetElementWithInterceptor(uint32_t index,
- Object* value);
- MUST_USE_RESULT MaybeObject* SetElementWithoutInterceptor(uint32_t index,
- Object* value);
+ Object* value,
+ bool check_prototype);
+ MUST_USE_RESULT MaybeObject* SetElementWithoutInterceptor(
+ uint32_t index,
+ Object* value,
+ bool check_prototype);
MaybeObject* GetElementPostInterceptor(JSObject* receiver, uint32_t index);
@@ -2327,6 +2336,10 @@ class SymbolTable: public HashTable<SymbolTableShape, HashTableKey*> {
// been enlarged. If the return value is not a failure, the symbol
// pointer *s is set to the symbol found.
MUST_USE_RESULT MaybeObject* LookupSymbol(Vector<const char> str, Object** s);
+ MUST_USE_RESULT MaybeObject* LookupAsciiSymbol(Vector<const char> str,
+ Object** s);
+ MUST_USE_RESULT MaybeObject* LookupTwoByteSymbol(Vector<const uc16> str,
+ Object** s);
MUST_USE_RESULT MaybeObject* LookupString(String* key, Object** s);
// Looks up a symbol that is equal to the given string and returns
@@ -3108,6 +3121,9 @@ class DeoptimizationOutputData: public FixedArray {
};
+class SafepointEntry;
+
+
// Code describes objects with on-the-fly generated machine code.
class Code: public HeapObject {
public:
@@ -3255,9 +3271,8 @@ class Code: public HeapObject {
inline byte compare_state();
inline void set_compare_state(byte value);
- // Get the safepoint entry for the given pc. Returns NULL for
- // non-safepoint pcs.
- uint8_t* GetSafepointEntry(Address pc);
+ // Get the safepoint entry for the given pc.
+ SafepointEntry GetSafepointEntry(Address pc);
// Mark this code object as not having a stack check table. Assumes kind
// is FUNCTION.
@@ -5074,6 +5089,8 @@ class String: public HeapObject {
// String equality operations.
inline bool Equals(String* other);
bool IsEqualTo(Vector<const char> str);
+ bool IsAsciiEqualTo(Vector<const char> str);
+ bool IsTwoByteEqualTo(Vector<const uc16> str);
// Return a UTF8 representation of the string. The string is null
// terminated but may optionally contain nulls. Length is returned
@@ -5245,6 +5262,34 @@ class String: public HeapObject {
int from,
int to);
+ static inline bool IsAscii(const char* chars, int length) {
+ const char* limit = chars + length;
+#ifdef V8_HOST_CAN_READ_UNALIGNED
+ ASSERT(kMaxAsciiCharCode == 0x7F);
+ const uintptr_t non_ascii_mask = kUintptrAllBitsSet / 0xFF * 0x80;
+ while (chars <= limit - sizeof(uintptr_t)) {
+ if (*reinterpret_cast<const uintptr_t*>(chars) & non_ascii_mask) {
+ return false;
+ }
+ chars += sizeof(uintptr_t);
+ }
+#endif
+ while (chars < limit) {
+ if (static_cast<uint8_t>(*chars) > kMaxAsciiCharCodeU) return false;
+ ++chars;
+ }
+ return true;
+ }
+
+ static inline bool IsAscii(const uc16* chars, int length) {
+ const uc16* limit = chars + length;
+ while (chars < limit) {
+ if (*chars > kMaxAsciiCharCodeU) return false;
+ ++chars;
+ }
+ return true;
+ }
+
protected:
class ReadBlockBuffer {
public:
diff --git a/deps/v8/src/parser.cc b/deps/v8/src/parser.cc
index 08f77b8f30..6ad9ab3162 100644
--- a/deps/v8/src/parser.cc
+++ b/deps/v8/src/parser.cc
@@ -323,22 +323,24 @@ TemporaryScope::~TemporaryScope() {
}
-Handle<String> Parser::LookupSymbol(int symbol_id,
- Vector<const char> string) {
+Handle<String> Parser::LookupSymbol(int symbol_id) {
// Length of symbol cache is the number of identified symbols.
// If we are larger than that, or negative, it's not a cached symbol.
// This might also happen if there is no preparser symbol data, even
// if there is some preparser data.
if (static_cast<unsigned>(symbol_id)
>= static_cast<unsigned>(symbol_cache_.length())) {
- return Factory::LookupSymbol(string);
+ if (scanner().is_literal_ascii()) {
+ return Factory::LookupAsciiSymbol(scanner().literal_ascii_string());
+ } else {
+ return Factory::LookupTwoByteSymbol(scanner().literal_uc16_string());
+ }
}
- return LookupCachedSymbol(symbol_id, string);
+ return LookupCachedSymbol(symbol_id);
}
-Handle<String> Parser::LookupCachedSymbol(int symbol_id,
- Vector<const char> string) {
+Handle<String> Parser::LookupCachedSymbol(int symbol_id) {
// Make sure the cache is large enough to hold the symbol identifier.
if (symbol_cache_.length() <= symbol_id) {
// Increase length to index + 1.
@@ -347,7 +349,11 @@ Handle<String> Parser::LookupCachedSymbol(int symbol_id,
}
Handle<String> result = symbol_cache_.at(symbol_id);
if (result.is_null()) {
- result = Factory::LookupSymbol(string);
+ if (scanner().is_literal_ascii()) {
+ result = Factory::LookupAsciiSymbol(scanner().literal_ascii_string());
+ } else {
+ result = Factory::LookupTwoByteSymbol(scanner().literal_uc16_string());
+ }
symbol_cache_.at(symbol_id) = result;
return result;
}
@@ -594,7 +600,8 @@ Parser::Parser(Handle<Script> script,
extension_(extension),
pre_data_(pre_data),
fni_(NULL),
- stack_overflow_(false) {
+ stack_overflow_(false),
+ parenthesized_function_(false) {
AstNode::ResetIds();
}
@@ -615,11 +622,11 @@ FunctionLiteral* Parser::ParseProgram(Handle<String> source,
// identical calls.
ExternalTwoByteStringUC16CharacterStream stream(
Handle<ExternalTwoByteString>::cast(source), 0, source->length());
- scanner_.Initialize(&stream, JavaScriptScanner::kAllLiterals);
+ scanner_.Initialize(&stream);
return DoParseProgram(source, in_global_context, &zone_scope);
} else {
GenericStringUC16CharacterStream stream(source, 0, source->length());
- scanner_.Initialize(&stream, JavaScriptScanner::kAllLiterals);
+ scanner_.Initialize(&stream);
return DoParseProgram(source, in_global_context, &zone_scope);
}
}
@@ -705,7 +712,7 @@ FunctionLiteral* Parser::ParseLazy(Handle<SharedFunctionInfo> info) {
FunctionLiteral* Parser::ParseLazy(Handle<SharedFunctionInfo> info,
UC16CharacterStream* source,
ZoneScope* zone_scope) {
- scanner_.Initialize(source, JavaScriptScanner::kAllLiterals);
+ scanner_.Initialize(source);
ASSERT(target_stack_ == NULL);
Handle<String> name(String::cast(info->name()));
@@ -757,7 +764,7 @@ Handle<String> Parser::GetSymbol(bool* ok) {
if (pre_data() != NULL) {
symbol_id = pre_data()->GetSymbolIdentifier();
}
- return LookupSymbol(symbol_id, scanner().literal());
+ return LookupSymbol(symbol_id);
}
@@ -771,7 +778,8 @@ void Parser::ReportMessageAt(Scanner::Location source_location,
const char* type,
Vector<const char*> args) {
MessageLocation location(script_,
- source_location.beg_pos, source_location.end_pos);
+ source_location.beg_pos,
+ source_location.end_pos);
Handle<JSArray> array = Factory::NewJSArray(args.length());
for (int i = 0; i < args.length(); i++) {
SetElement(array, i, Factory::NewStringFromUtf8(CStrVector(args[i])));
@@ -781,6 +789,21 @@ void Parser::ReportMessageAt(Scanner::Location source_location,
}
+void Parser::ReportMessageAt(Scanner::Location source_location,
+ const char* type,
+ Vector<Handle<String> > args) {
+ MessageLocation location(script_,
+ source_location.beg_pos,
+ source_location.end_pos);
+ Handle<JSArray> array = Factory::NewJSArray(args.length());
+ for (int i = 0; i < args.length(); i++) {
+ SetElement(array, i, args[i]);
+ }
+ Handle<Object> result = Factory::NewSyntaxError(type, array);
+ Top::Throw(*result, &location);
+}
+
+
// Base class containing common code for the different finder classes used by
// the parser.
class ParserFinder {
@@ -1686,12 +1709,16 @@ Statement* Parser::ParseContinueStatement(bool* ok) {
IterationStatement* target = NULL;
target = LookupContinueTarget(label, CHECK_OK);
if (target == NULL) {
- // Illegal continue statement. To be consistent with KJS we delay
- // reporting of the syntax error until runtime.
- Handle<String> error_type = Factory::illegal_continue_symbol();
- if (!label.is_null()) error_type = Factory::unknown_label_symbol();
- Expression* throw_error = NewThrowSyntaxError(error_type, label);
- return new ExpressionStatement(throw_error);
+ // Illegal continue statement.
+ const char* message = "illegal_continue";
+ Vector<Handle<String> > args;
+ if (!label.is_null()) {
+ message = "unknown_label";
+ args = Vector<Handle<String> >(&label, 1);
+ }
+ ReportMessageAt(scanner().location(), message, args);
+ *ok = false;
+ return NULL;
}
ExpectSemicolon(CHECK_OK);
return new ContinueStatement(target);
@@ -1717,12 +1744,16 @@ Statement* Parser::ParseBreakStatement(ZoneStringList* labels, bool* ok) {
BreakableStatement* target = NULL;
target = LookupBreakTarget(label, CHECK_OK);
if (target == NULL) {
- // Illegal break statement. To be consistent with KJS we delay
- // reporting of the syntax error until runtime.
- Handle<String> error_type = Factory::illegal_break_symbol();
- if (!label.is_null()) error_type = Factory::unknown_label_symbol();
- Expression* throw_error = NewThrowSyntaxError(error_type, label);
- return new ExpressionStatement(throw_error);
+ // Illegal break statement.
+ const char* message = "illegal_break";
+ Vector<Handle<String> > args;
+ if (!label.is_null()) {
+ message = "unknown_label";
+ args = Vector<Handle<String> >(&label, 1);
+ }
+ ReportMessageAt(scanner().location(), message, args);
+ *ok = false;
+ return NULL;
}
ExpectSemicolon(CHECK_OK);
return new BreakStatement(target);
@@ -2317,26 +2348,6 @@ Expression* Parser::ParseBinaryExpression(int prec, bool accept_IN, bool* ok) {
}
}
- // Convert constant divisions to multiplications for speed.
- if (op == Token::DIV &&
- y && y->AsLiteral() && y->AsLiteral()->handle()->IsNumber()) {
- double y_val = y->AsLiteral()->handle()->Number();
- int64_t y_int = static_cast<int64_t>(y_val);
- // There are rounding issues with this optimization, but they don't
- // apply if the number to be divided with has a reciprocal that can be
- // precisely represented as a floating point number. This is the case
- // if the number is an integer power of 2. Negative integer powers of
- // 2 work too, but for -2, -1, 1 and 2 we don't do the strength
- // reduction because the inlined optimistic idiv has a reasonable
- // chance of succeeding by producing a Smi answer with no remainder.
- if (static_cast<double>(y_int) == y_val &&
- (IsPowerOf2(y_int) || IsPowerOf2(-y_int)) &&
- (y_int > 2 || y_int < -2)) {
- y = NewNumberLiteral(1 / y_val);
- op = Token::MUL;
- }
- }
-
// For now we distinguish between comparisons and other binary
// operations. (We could combine the two and get rid of this
// code and AST node eventually.)
@@ -2496,9 +2507,13 @@ Expression* Parser::ParseLeftHandSideExpression(bool* ok) {
// The calls that need special treatment are the
// direct (i.e. not aliased) eval calls. These calls are all of the
// form eval(...) with no explicit receiver object where eval is not
- // declared in the current scope chain. These calls are marked as
- // potentially direct eval calls. Whether they are actually direct calls
- // to eval is determined at run time.
+ // declared in the current scope chain.
+ // These calls are marked as potentially direct eval calls. Whether
+ // they are actually direct calls to eval is determined at run time.
+ // TODO(994): In ES5, it doesn't matter if the "eval" var is declared
+ // in the local scope chain. It only matters that it's called "eval",
+ // is called without a receiver and it refers to the original eval
+ // function.
VariableProxy* callee = result->AsVariableProxy();
if (callee != NULL && callee->IsVariable(Factory::eval_symbol())) {
Handle<String> name = callee->name();
@@ -2715,8 +2730,9 @@ Expression* Parser::ParsePrimaryExpression(bool* ok) {
case Token::NUMBER: {
Consume(Token::NUMBER);
- double value =
- StringToDouble(scanner().literal(), ALLOW_HEX | ALLOW_OCTALS);
+ ASSERT(scanner().is_literal_ascii());
+ double value = StringToDouble(scanner().literal_ascii_string(),
+ ALLOW_HEX | ALLOW_OCTALS);
result = NewNumberLiteral(value);
break;
}
@@ -2747,6 +2763,9 @@ Expression* Parser::ParsePrimaryExpression(bool* ok) {
case Token::LPAREN:
Consume(Token::LPAREN);
+ // Heuristically try to detect immediately called functions before
+ // seeing the call parentheses.
+ parenthesized_function_ = (peek() == Token::FUNCTION);
result = ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
break;
@@ -2988,14 +3007,22 @@ ObjectLiteral::Property* Parser::ParseObjectLiteralGetSet(bool is_getter,
// { ... , get foo() { ... }, ... , set foo(v) { ... v ... } , ... }
// We have already read the "get" or "set" keyword.
Token::Value next = Next();
- // TODO(820): Allow NUMBER and STRING as well (and handle array indices).
- if (next == Token::IDENTIFIER || Token::IsKeyword(next)) {
- Handle<String> name = GetSymbol(CHECK_OK);
+ bool is_keyword = Token::IsKeyword(next);
+ if (next == Token::IDENTIFIER || next == Token::NUMBER ||
+ next == Token::STRING || is_keyword) {
+ Handle<String> name;
+ if (is_keyword) {
+ name = Factory::LookupAsciiSymbol(Token::String(next));
+ } else {
+ name = GetSymbol(CHECK_OK);
+ }
FunctionLiteral* value =
ParseFunctionLiteral(name,
RelocInfo::kNoPosition,
DECLARATION,
CHECK_OK);
+ // Allow any number of parameters for compatiabilty with JSC.
+ // Specification only allows zero parameters for get and one for set.
ObjectLiteral::Property* property =
new ObjectLiteral::Property(is_getter, value);
return property;
@@ -3066,8 +3093,9 @@ Expression* Parser::ParseObjectLiteral(bool* ok) {
}
case Token::NUMBER: {
Consume(Token::NUMBER);
- double value =
- StringToDouble(scanner().literal(), ALLOW_HEX | ALLOW_OCTALS);
+ ASSERT(scanner().is_literal_ascii());
+ double value = StringToDouble(scanner().literal_ascii_string(),
+ ALLOW_HEX | ALLOW_OCTALS);
key = NewNumberLiteral(value);
break;
}
@@ -3137,11 +3165,9 @@ Expression* Parser::ParseRegExpLiteral(bool seen_equal, bool* ok) {
int literal_index = temp_scope_->NextMaterializedLiteralIndex();
- Handle<String> js_pattern =
- Factory::NewStringFromUtf8(scanner().next_literal(), TENURED);
+ Handle<String> js_pattern = NextLiteralString(TENURED);
scanner().ScanRegExpFlags();
- Handle<String> js_flags =
- Factory::NewStringFromUtf8(scanner().next_literal(), TENURED);
+ Handle<String> js_flags = NextLiteralString(TENURED);
Next();
return new RegExpLiteral(js_pattern, js_flags, literal_index);
@@ -3231,8 +3257,11 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
// Determine if the function will be lazily compiled. The mode can
// only be PARSE_LAZILY if the --lazy flag is true.
- bool is_lazily_compiled =
- mode() == PARSE_LAZILY && top_scope_->HasTrivialOuterContext();
+ bool is_lazily_compiled = (mode() == PARSE_LAZILY &&
+ top_scope_->outer_scope()->is_global_scope() &&
+ top_scope_->HasTrivialOuterContext() &&
+ !parenthesized_function_);
+ parenthesized_function_ = false; // The bit was set for this function only.
int function_block_pos = scanner().location().beg_pos;
int materialized_literal_count;
@@ -3423,10 +3452,10 @@ Handle<String> Parser::ParseIdentifierOrGetOrSet(bool* is_get,
bool* ok) {
Expect(Token::IDENTIFIER, ok);
if (!*ok) return Handle<String>();
- if (scanner().literal_length() == 3) {
- const char* token = scanner().literal_string();
- *is_get = strcmp(token, "get") == 0;
- *is_set = !*is_get && strcmp(token, "set") == 0;
+ if (scanner().is_literal_ascii() && scanner().literal_length() == 3) {
+ const char* token = scanner().literal_ascii_string().start();
+ *is_get = strncmp(token, "get", 3) == 0;
+ *is_set = !*is_get && strncmp(token, "set", 3) == 0;
}
return GetSymbol(ok);
}
@@ -3604,9 +3633,11 @@ Handle<String> JsonParser::GetString() {
if (literal_length == 0) {
return Factory::empty_string();
}
- const char* literal_string = scanner_.literal_string();
- Vector<const char> literal(literal_string, literal_length);
- return Factory::NewStringFromUtf8(literal);
+ if (scanner_.is_literal_ascii()) {
+ return Factory::NewStringFromAscii(scanner_.literal_ascii_string());
+ } else {
+ return Factory::NewStringFromTwoByte(scanner_.literal_uc16_string());
+ }
}
@@ -3618,7 +3649,8 @@ Handle<Object> JsonParser::ParseJsonValue() {
return GetString();
}
case Token::NUMBER: {
- double value = StringToDouble(scanner_.literal(),
+ ASSERT(scanner_.is_literal_ascii());
+ double value = StringToDouble(scanner_.literal_ascii_string(),
NO_FLAGS, // Hex, octal or trailing junk.
OS::nan_value());
return Factory::NewNumber(value);
@@ -3663,9 +3695,9 @@ Handle<Object> JsonParser::ParseJsonObject() {
if (value.is_null()) return Handle<Object>::null();
uint32_t index;
if (key->AsArrayIndex(&index)) {
- SetElement(json_object, index, value);
+ SetOwnElement(json_object, index, value);
} else {
- SetProperty(json_object, key, value, NONE);
+ SetLocalPropertyIgnoreAttributes(json_object, key, value, NONE);
}
} while (scanner_.Next() == Token::COMMA);
if (scanner_.current_token() != Token::RBRACE) {
@@ -4025,9 +4057,21 @@ RegExpTree* RegExpParser::ParseDisjunction() {
builder->AddCharacter('\v');
break;
case 'c': {
- Advance(2);
- uc32 control = ParseControlLetterEscape();
- builder->AddCharacter(control);
+ Advance();
+ uc32 controlLetter = Next();
+ // Special case if it is an ASCII letter.
+ // Convert lower case letters to uppercase.
+ uc32 letter = controlLetter & ~('a' ^ 'A');
+ if (letter < 'A' || 'Z' < letter) {
+ // controlLetter is not in range 'A'-'Z' or 'a'-'z'.
+ // This is outside the specification. We match JSC in
+ // reading the backslash as a literal character instead
+ // of as starting an escape.
+ builder->AddCharacter('\\');
+ } else {
+ Advance(2);
+ builder->AddCharacter(controlLetter & 0x1f);
+ }
break;
}
case 'x': {
@@ -4302,23 +4346,6 @@ bool RegExpParser::ParseIntervalQuantifier(int* min_out, int* max_out) {
}
-// Upper and lower case letters differ by one bit.
-STATIC_CHECK(('a' ^ 'A') == 0x20);
-
-uc32 RegExpParser::ParseControlLetterEscape() {
- if (!has_more())
- return 'c';
- uc32 letter = current() & ~(0x20); // Collapse upper and lower case letters.
- if (letter < 'A' || 'Z' < letter) {
- // Non-spec error-correction: "\c" followed by non-control letter is
- // interpreted as an IdentityEscape of 'c'.
- return 'c';
- }
- Advance();
- return letter & 0x1f; // Remainder modulo 32, per specification.
-}
-
-
uc32 RegExpParser::ParseOctalLiteral() {
ASSERT('0' <= current() && current() <= '7');
// For compatibility with some other browsers (not all), we parse
@@ -4384,9 +4411,23 @@ uc32 RegExpParser::ParseClassCharacterEscape() {
case 'v':
Advance();
return '\v';
- case 'c':
- Advance();
- return ParseControlLetterEscape();
+ case 'c': {
+ uc32 controlLetter = Next();
+ uc32 letter = controlLetter & ~('A' ^ 'a');
+ // For compatibility with JSC, inside a character class
+ // we also accept digits and underscore as control characters.
+ if ((controlLetter >= '0' && controlLetter <= '9') ||
+ controlLetter == '_' ||
+ (letter >= 'A' && letter <= 'Z')) {
+ Advance(2);
+ // Control letters mapped to ASCII control characters in the range
+ // 0x00-0x1f.
+ return controlLetter & 0x1f;
+ }
+ // We match JSC in reading the backslash as a literal
+ // character instead of as starting an escape.
+ return '\\';
+ }
case '0': case '1': case '2': case '3': case '4': case '5':
case '6': case '7':
// For compatibility, we interpret a decimal escape that isn't
@@ -4597,10 +4638,9 @@ int ScriptDataImpl::ReadNumber(byte** source) {
// Create a Scanner for the preparser to use as input, and preparse the source.
static ScriptDataImpl* DoPreParse(UC16CharacterStream* source,
bool allow_lazy,
- ParserRecorder* recorder,
- int literal_flags) {
+ ParserRecorder* recorder) {
V8JavaScriptScanner scanner;
- scanner.Initialize(source, literal_flags);
+ scanner.Initialize(source);
intptr_t stack_limit = StackGuard::real_climit();
if (!preparser::PreParser::PreParseProgram(&scanner,
recorder,
@@ -4628,8 +4668,7 @@ ScriptDataImpl* ParserApi::PartialPreParse(UC16CharacterStream* source,
return NULL;
}
PartialParserRecorder recorder;
- return DoPreParse(source, allow_lazy, &recorder,
- JavaScriptScanner::kNoLiterals);
+ return DoPreParse(source, allow_lazy, &recorder);
}
@@ -4638,9 +4677,7 @@ ScriptDataImpl* ParserApi::PreParse(UC16CharacterStream* source,
Handle<Script> no_script;
bool allow_lazy = FLAG_lazy && (extension == NULL);
CompleteParserRecorder recorder;
- int kPreParseLiteralsFlags =
- JavaScriptScanner::kLiteralString | JavaScriptScanner::kLiteralIdentifier;
- return DoPreParse(source, allow_lazy, &recorder, kPreParseLiteralsFlags);
+ return DoPreParse(source, allow_lazy, &recorder);
}
diff --git a/deps/v8/src/parser.h b/deps/v8/src/parser.h
index 70d0e18fdb..0613a8de99 100644
--- a/deps/v8/src/parser.h
+++ b/deps/v8/src/parser.h
@@ -321,7 +321,6 @@ class RegExpParser {
// and sets the value if it is.
bool ParseHexEscape(int length, uc32* value);
- uc32 ParseControlLetterEscape();
uc32 ParseOctalLiteral();
// Tries to parse the input as a back reference. If successful it
@@ -431,6 +430,9 @@ class Parser {
void ReportMessageAt(Scanner::Location loc,
const char* message,
Vector<const char*> args);
+ void ReportMessageAt(Scanner::Location loc,
+ const char* message,
+ Vector<Handle<String> > args);
protected:
FunctionLiteral* ParseLazy(Handle<SharedFunctionInfo> info,
@@ -578,6 +580,26 @@ class Parser {
bool Check(Token::Value token);
void ExpectSemicolon(bool* ok);
+ Handle<String> LiteralString(PretenureFlag tenured) {
+ if (scanner().is_literal_ascii()) {
+ return Factory::NewStringFromAscii(scanner().literal_ascii_string(),
+ tenured);
+ } else {
+ return Factory::NewStringFromTwoByte(scanner().literal_uc16_string(),
+ tenured);
+ }
+ }
+
+ Handle<String> NextLiteralString(PretenureFlag tenured) {
+ if (scanner().is_next_literal_ascii()) {
+ return Factory::NewStringFromAscii(scanner().next_literal_ascii_string(),
+ tenured);
+ } else {
+ return Factory::NewStringFromTwoByte(scanner().next_literal_uc16_string(),
+ tenured);
+ }
+ }
+
Handle<String> GetSymbol(bool* ok);
// Get odd-ball literals.
@@ -612,11 +634,9 @@ class Parser {
Scope* NewScope(Scope* parent, Scope::Type type, bool inside_with);
- Handle<String> LookupSymbol(int symbol_id,
- Vector<const char> string);
+ Handle<String> LookupSymbol(int symbol_id);
- Handle<String> LookupCachedSymbol(int symbol_id,
- Vector<const char> string);
+ Handle<String> LookupCachedSymbol(int symbol_id);
Expression* NewCall(Expression* expression,
ZoneList<Expression*>* arguments,
@@ -665,6 +685,11 @@ class Parser {
ScriptDataImpl* pre_data_;
FuncNameInferrer* fni_;
bool stack_overflow_;
+ // If true, the next (and immediately following) function literal is
+ // preceded by a parenthesis.
+ // Heuristically that means that the function will be called immediately,
+ // so never lazily compile it.
+ bool parenthesized_function_;
};
diff --git a/deps/v8/src/platform-cygwin.cc b/deps/v8/src/platform-cygwin.cc
deleted file mode 100644
index ad0ad8c33d..0000000000
--- a/deps/v8/src/platform-cygwin.cc
+++ /dev/null
@@ -1,865 +0,0 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// Platform specific code for Cygwin goes here. For the POSIX comaptible parts
-// the implementation is in platform-posix.cc.
-
-#include <pthread.h>
-#include <semaphore.h>
-#include <signal.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sys/types.h>
-#include <stdlib.h>
-
-// Ubuntu Dapper requires memory pages to be marked as
-// executable. Otherwise, OS raises an exception when executing code
-// in that page.
-#include <sys/types.h> // mmap & munmap
-#include <sys/mman.h> // mmap & munmap
-#include <sys/stat.h> // open
-#include <fcntl.h> // open
-#include <unistd.h> // sysconf
-#ifdef __GLIBC__
-#include <execinfo.h> // backtrace, backtrace_symbols
-#endif // def __GLIBC__
-#include <strings.h> // index
-#include <errno.h>
-#include <stdarg.h>
-
-#undef MAP_TYPE
-
-#include "v8.h"
-
-#include "platform.h"
-#include "top.h"
-#include "v8threads.h"
-
-
-namespace v8 {
-namespace internal {
-
-// 0 is never a valid thread id on Linux since tids and pids share a
-// name space and pid 0 is reserved (see man 2 kill).
-static const pthread_t kNoThread = (pthread_t) 0;
-
-
-double ceiling(double x) {
- return ceil(x);
-}
-
-
-void OS::Setup() {
- // Seed the random number generator.
- // Convert the current time to a 64-bit integer first, before converting it
- // to an unsigned. Going directly can cause an overflow and the seed to be
- // set to all ones. The seed will be identical for different instances that
- // call this setup code within the same millisecond.
- uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
- srandom(static_cast<unsigned int>(seed));
-}
-
-
-uint64_t OS::CpuFeaturesImpliedByPlatform() {
-#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
- // Here gcc is telling us that we are on an ARM and gcc is assuming that we
- // have VFP3 instructions. If gcc can assume it then so can we.
- return 1u << VFP3;
-#elif CAN_USE_ARMV7_INSTRUCTIONS
- return 1u << ARMv7;
-#else
- return 0; // Linux runs on anything.
-#endif
-}
-
-
-#ifdef __arm__
-bool OS::ArmCpuHasFeature(CpuFeature feature) {
- const char* search_string = NULL;
- const char* file_name = "/proc/cpuinfo";
- // Simple detection of VFP at runtime for Linux.
- // It is based on /proc/cpuinfo, which reveals hardware configuration
- // to user-space applications. According to ARM (mid 2009), no similar
- // facility is universally available on the ARM architectures,
- // so it's up to individual OSes to provide such.
- //
- // This is written as a straight shot one pass parser
- // and not using STL string and ifstream because,
- // on Linux, it's reading from a (non-mmap-able)
- // character special device.
- switch (feature) {
- case VFP3:
- search_string = "vfp";
- break;
- case ARMv7:
- search_string = "ARMv7";
- break;
- default:
- UNREACHABLE();
- }
-
- FILE* f = NULL;
- const char* what = search_string;
-
- if (NULL == (f = fopen(file_name, "r")))
- return false;
-
- int k;
- while (EOF != (k = fgetc(f))) {
- if (k == *what) {
- ++what;
- while ((*what != '\0') && (*what == fgetc(f))) {
- ++what;
- }
- if (*what == '\0') {
- fclose(f);
- return true;
- } else {
- what = search_string;
- }
- }
- }
- fclose(f);
-
- // Did not find string in the proc file.
- return false;
-}
-#endif // def __arm__
-
-
-int OS::ActivationFrameAlignment() {
-#ifdef V8_TARGET_ARCH_ARM
- // On EABI ARM targets this is required for fp correctness in the
- // runtime system.
- return 8;
-#elif V8_TARGET_ARCH_MIPS
- return 8;
-#endif
- // With gcc 4.4 the tree vectorization optimizer can generate code
- // that requires 16 byte alignment such as movdqa on x86.
- return 16;
-}
-
-void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
- __asm__ __volatile__("" : : : "memory");
- // An x86 store acts as a release barrier.
- *ptr = value;
-}
-
-const char* OS::LocalTimezone(double time) {
- if (isnan(time)) return "";
- time_t tv = static_cast<time_t>(floor(time/msPerSecond));
- struct tm* t = localtime(&tv);
- if (NULL == t) return "";
- return tzname[0]; // The location of the timezone string on Cywin.
-}
-
-
-double OS::LocalTimeOffset() {
- //
- // On Cygwin, struct tm does not contain a tm_gmtoff field.
- time_t utc = time(NULL);
- ASSERT(utc != -1);
- struct tm* loc = localtime(&utc);
- ASSERT(loc != NULL);
- return static_cast<double>((mktime(loc) - utc) * msPerSecond);
-}
-
-
-// We keep the lowest and highest addresses mapped as a quick way of
-// determining that pointers are outside the heap (used mostly in assertions
-// and verification). The estimate is conservative, ie, not all addresses in
-// 'allocated' space are actually allocated to our heap. The range is
-// [lowest, highest), inclusive on the low and and exclusive on the high end.
-static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
-static void* highest_ever_allocated = reinterpret_cast<void*>(0);
-
-
-static void UpdateAllocatedSpaceLimits(void* address, int size) {
- lowest_ever_allocated = Min(lowest_ever_allocated, address);
- highest_ever_allocated =
- Max(highest_ever_allocated,
- reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
-}
-
-
-bool OS::IsOutsideAllocatedSpace(void* address) {
- return address < lowest_ever_allocated || address >= highest_ever_allocated;
-}
-
-
-size_t OS::AllocateAlignment() {
- return sysconf(_SC_PAGESIZE);
-}
-
-
-void* OS::Allocate(const size_t requested,
- size_t* allocated,
- bool is_executable) {
- const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE));
- int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
- void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- if (mbase == MAP_FAILED) {
- LOG(StringEvent("OS::Allocate", "mmap failed"));
- return NULL;
- }
- *allocated = msize;
- UpdateAllocatedSpaceLimits(mbase, msize);
- return mbase;
-}
-
-
-void OS::Free(void* address, const size_t size) {
- // TODO(1240712): munmap has a return value which is ignored here.
- int result = munmap(address, size);
- USE(result);
- ASSERT(result == 0);
-}
-
-
-#ifdef ENABLE_HEAP_PROTECTION
-
-void OS::Protect(void* address, size_t size) {
- // TODO(1240712): mprotect has a return value which is ignored here.
- mprotect(address, size, PROT_READ);
-}
-
-
-void OS::Unprotect(void* address, size_t size, bool is_executable) {
- // TODO(1240712): mprotect has a return value which is ignored here.
- int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
- mprotect(address, size, prot);
-}
-
-#endif
-
-
-void OS::Sleep(int milliseconds) {
- unsigned int ms = static_cast<unsigned int>(milliseconds);
- usleep(1000 * ms);
-}
-
-
-void OS::Abort() {
- // Redirect to std abort to signal abnormal program termination.
- abort();
-}
-
-
-void OS::DebugBreak() {
-// TODO(lrn): Introduce processor define for runtime system (!= V8_ARCH_x,
-// which is the architecture of generated code).
-#if (defined(__arm__) || defined(__thumb__)) && \
- defined(CAN_USE_ARMV5_INSTRUCTIONS)
- asm("bkpt 0");
-#elif defined(__mips__)
- asm("break");
-#else
- asm("int $3");
-#endif
-}
-
-
-class PosixMemoryMappedFile : public OS::MemoryMappedFile {
- public:
- PosixMemoryMappedFile(FILE* file, void* memory, int size)
- : file_(file), memory_(memory), size_(size) { }
- virtual ~PosixMemoryMappedFile();
- virtual void* memory() { return memory_; }
- private:
- FILE* file_;
- void* memory_;
- int size_;
-};
-
-
-OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
- void* initial) {
- FILE* file = fopen(name, "w+");
- if (file == NULL) return NULL;
- int result = fwrite(initial, size, 1, file);
- if (result < 1) {
- fclose(file);
- return NULL;
- }
- void* memory =
- mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
- return new PosixMemoryMappedFile(file, memory, size);
-}
-
-
-PosixMemoryMappedFile::~PosixMemoryMappedFile() {
- if (memory_) munmap(memory_, size_);
- fclose(file_);
-}
-
-
-void OS::LogSharedLibraryAddresses() {
-#ifdef ENABLE_LOGGING_AND_PROFILING
- // This function assumes that the layout of the file is as follows:
- // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name]
- // If we encounter an unexpected situation we abort scanning further entries.
- FILE* fp = fopen("/proc/self/maps", "r");
- if (fp == NULL) return;
-
- // Allocate enough room to be able to store a full file name.
- const int kLibNameLen = FILENAME_MAX + 1;
- char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen));
-
- // This loop will terminate once the scanning hits an EOF.
- while (true) {
- uintptr_t start, end;
- char attr_r, attr_w, attr_x, attr_p;
- // Parse the addresses and permission bits at the beginning of the line.
- if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break;
- if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break;
-
- int c;
- if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') {
- // Found a read-only executable entry. Skip characters until we reach
- // the beginning of the filename or the end of the line.
- do {
- c = getc(fp);
- } while ((c != EOF) && (c != '\n') && (c != '/'));
- if (c == EOF) break; // EOF: Was unexpected, just exit.
-
- // Process the filename if found.
- if (c == '/') {
- ungetc(c, fp); // Push the '/' back into the stream to be read below.
-
- // Read to the end of the line. Exit if the read fails.
- if (fgets(lib_name, kLibNameLen, fp) == NULL) break;
-
- // Drop the newline character read by fgets. We do not need to check
- // for a zero-length string because we know that we at least read the
- // '/' character.
- lib_name[strlen(lib_name) - 1] = '\0';
- } else {
- // No library name found, just record the raw address range.
- snprintf(lib_name, kLibNameLen,
- "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end);
- }
- LOG(SharedLibraryEvent(lib_name, start, end));
- } else {
- // Entry not describing executable data. Skip to end of line to setup
- // reading the next entry.
- do {
- c = getc(fp);
- } while ((c != EOF) && (c != '\n'));
- if (c == EOF) break;
- }
- }
- free(lib_name);
- fclose(fp);
-#endif
-}
-
-
-void OS::SignalCodeMovingGC() {
-}
-
-
-int OS::StackWalk(Vector<OS::StackFrame> frames) {
- // backtrace is a glibc extension.
-#ifdef __GLIBC__
- int frames_size = frames.length();
- ScopedVector<void*> addresses(frames_size);
-
- int frames_count = backtrace(addresses.start(), frames_size);
-
- char** symbols = backtrace_symbols(addresses.start(), frames_count);
- if (symbols == NULL) {
- return kStackWalkError;
- }
-
- for (int i = 0; i < frames_count; i++) {
- frames[i].address = addresses[i];
- // Format a text representation of the frame based on the information
- // available.
- SNPrintF(MutableCStrVector(frames[i].text, kStackWalkMaxTextLen),
- "%s",
- symbols[i]);
- // Make sure line termination is in place.
- frames[i].text[kStackWalkMaxTextLen - 1] = '\0';
- }
-
- free(symbols);
-
- return frames_count;
-#else // ndef __GLIBC__
- return 0;
-#endif // ndef __GLIBC__
-}
-
-
-// Constants used for mmap.
-static const int kMmapFd = -1;
-static const int kMmapFdOffset = 0;
-
-
-VirtualMemory::VirtualMemory(size_t size) {
- address_ = mmap(NULL, size, PROT_NONE,
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
- kMmapFd, kMmapFdOffset);
- size_ = size;
-}
-
-
-VirtualMemory::~VirtualMemory() {
- if (IsReserved()) {
- if (0 == munmap(address(), size())) address_ = MAP_FAILED;
- }
-}
-
-
-bool VirtualMemory::IsReserved() {
- return address_ != MAP_FAILED;
-}
-
-
-bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
- int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
-
-#ifdef HAS_MAP_FIXED
- if (MAP_FAILED == mmap(address, size, prot,
- MAP_PRIVATE | MAP_ANONYMOUS, // | MAP_FIXED, - Cygwin doesn't have MAP_FIXED
- kMmapFd, kMmapFdOffset)) {
- return false;
- }
-#else
- if (mprotect(address, size, prot) != 0) {
- return false;
- }
-#endif
-
- UpdateAllocatedSpaceLimits(address, size);
- return true;
-}
-
-
-bool VirtualMemory::Uncommit(void* address, size_t size) {
- return mmap(address, size, PROT_NONE,
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, // | MAP_FIXED, - Cygwin doesn't have MAP_FIXED
- kMmapFd, kMmapFdOffset) != MAP_FAILED;
-}
-
-
-class ThreadHandle::PlatformData : public Malloced {
- public:
- explicit PlatformData(ThreadHandle::Kind kind) {
- Initialize(kind);
- }
-
- void Initialize(ThreadHandle::Kind kind) {
- switch (kind) {
- case ThreadHandle::SELF: thread_ = pthread_self(); break;
- case ThreadHandle::INVALID: thread_ = kNoThread; break;
- }
- }
-
- pthread_t thread_; // Thread handle for pthread.
-};
-
-
-ThreadHandle::ThreadHandle(Kind kind) {
- data_ = new PlatformData(kind);
-}
-
-
-void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
- data_->Initialize(kind);
-}
-
-
-ThreadHandle::~ThreadHandle() {
- delete data_;
-}
-
-
-bool ThreadHandle::IsSelf() const {
- return pthread_equal(data_->thread_, pthread_self());
-}
-
-
-bool ThreadHandle::IsValid() const {
- return data_->thread_ != kNoThread;
-}
-
-
-Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
-}
-
-
-Thread::~Thread() {
-}
-
-
-static void* ThreadEntry(void* arg) {
- Thread* thread = reinterpret_cast<Thread*>(arg);
- // This is also initialized by the first argument to pthread_create() but we
- // don't know which thread will run first (the original thread or the new
- // one) so we initialize it here too.
- thread->thread_handle_data()->thread_ = pthread_self();
- ASSERT(thread->IsValid());
- thread->Run();
- return NULL;
-}
-
-
-void Thread::Start() {
- pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
- ASSERT(IsValid());
-}
-
-
-void Thread::Join() {
- pthread_join(thread_handle_data()->thread_, NULL);
-}
-
-
-Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
- pthread_key_t key;
- int result = pthread_key_create(&key, NULL);
- USE(result);
- ASSERT(result == 0);
- return static_cast<LocalStorageKey>(key);
-}
-
-
-void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
- pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
- int result = pthread_key_delete(pthread_key);
- USE(result);
- ASSERT(result == 0);
-}
-
-
-void* Thread::GetThreadLocal(LocalStorageKey key) {
- pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
- return pthread_getspecific(pthread_key);
-}
-
-
-void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
- pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
- pthread_setspecific(pthread_key, value);
-}
-
-
-void Thread::YieldCPU() {
- sched_yield();
-}
-
-
-class CygwinMutex : public Mutex {
- public:
-
- CygwinMutex() {
- pthread_mutexattr_t attrs;
- memset(&attrs, 0, sizeof(attrs));
-
- int result = pthread_mutexattr_init(&attrs);
- ASSERT(result == 0);
- result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
- ASSERT(result == 0);
- result = pthread_mutex_init(&mutex_, &attrs);
- ASSERT(result == 0);
- }
-
- virtual ~CygwinMutex() { pthread_mutex_destroy(&mutex_); }
-
- virtual int Lock() {
- int result = pthread_mutex_lock(&mutex_);
- return result;
- }
-
- virtual int Unlock() {
- int result = pthread_mutex_unlock(&mutex_);
- return result;
- }
-
- private:
- pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
-};
-
-
-Mutex* OS::CreateMutex() {
- return new CygwinMutex();
-}
-
-
-class CygwinSemaphore : public Semaphore {
- public:
- explicit CygwinSemaphore(int count) { sem_init(&sem_, 0, count); }
- virtual ~CygwinSemaphore() { sem_destroy(&sem_); }
-
- virtual void Wait();
- virtual bool Wait(int timeout);
- virtual void Signal() { sem_post(&sem_); }
- private:
- sem_t sem_;
-};
-
-
-void CygwinSemaphore::Wait() {
- while (true) {
- int result = sem_wait(&sem_);
- if (result == 0) return; // Successfully got semaphore.
- CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
- }
-}
-
-
-#ifndef TIMEVAL_TO_TIMESPEC
-#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
- (ts)->tv_sec = (tv)->tv_sec; \
- (ts)->tv_nsec = (tv)->tv_usec * 1000; \
-} while (false)
-#endif
-
-
-bool CygwinSemaphore::Wait(int timeout) {
- const long kOneSecondMicros = 1000000; // NOLINT
-
- // Split timeout into second and nanosecond parts.
- struct timeval delta;
- delta.tv_usec = timeout % kOneSecondMicros;
- delta.tv_sec = timeout / kOneSecondMicros;
-
- struct timeval current_time;
- // Get the current time.
- if (gettimeofday(&current_time, NULL) == -1) {
- return false;
- }
-
- // Calculate time for end of timeout.
- struct timeval end_time;
- timeradd(&current_time, &delta, &end_time);
-
- struct timespec ts;
- TIMEVAL_TO_TIMESPEC(&end_time, &ts);
- // Wait for semaphore signalled or timeout.
- while (true) {
- int result = sem_timedwait(&sem_, &ts);
- if (result == 0) return true; // Successfully got semaphore.
- if (result > 0) {
- // For glibc prior to 2.3.4 sem_timedwait returns the error instead of -1.
- errno = result;
- result = -1;
- }
- if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
- CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
- }
-}
-
-
-Semaphore* OS::CreateSemaphore(int count) {
- return new CygwinSemaphore(count);
-}
-
-
-#ifdef ENABLE_LOGGING_AND_PROFILING
-
-static Sampler* active_sampler_ = NULL;
-static pthread_t vm_thread_ = 0;
-
-
-#if !defined(__GLIBC__) && (defined(__arm__) || defined(__thumb__))
-// Android runs a fairly new Linux kernel, so signal info is there,
-// but the C library doesn't have the structs defined.
-
-struct sigcontext {
- uint32_t trap_no;
- uint32_t error_code;
- uint32_t oldmask;
- uint32_t gregs[16];
- uint32_t arm_cpsr;
- uint32_t fault_address;
-};
-typedef uint32_t __sigset_t;
-typedef struct sigcontext mcontext_t;
-typedef struct ucontext {
- uint32_t uc_flags;
- struct ucontext* uc_link;
- stack_t uc_stack;
- mcontext_t uc_mcontext;
- __sigset_t uc_sigmask;
-} ucontext_t;
-enum ArmRegisters {R15 = 15, R13 = 13, R11 = 11};
-
-#endif
-
-
-// A function that determines if a signal handler is called in the context
-// of a VM thread.
-//
-// The problem is that SIGPROF signal can be delivered to an arbitrary thread
-// (see http://code.google.com/p/google-perftools/issues/detail?id=106#c2)
-// So, if the signal is being handled in the context of a non-VM thread,
-// it means that the VM thread is running, and trying to sample its stack can
-// cause a crash.
-static inline bool IsVmThread() {
- // In the case of a single VM thread, this check is enough.
- if (pthread_equal(pthread_self(), vm_thread_)) return true;
- // If there are multiple threads that use VM, they must have a thread id
- // stored in TLS. To verify that the thread is really executing VM,
- // we check Top's data. Having that ThreadManager::RestoreThread first
- // restores ThreadLocalTop from TLS, and only then erases the TLS value,
- // reading Top::thread_id() should not be affected by races.
- if (ThreadManager::HasId() && !ThreadManager::IsArchived() &&
- ThreadManager::CurrentId() == Top::thread_id()) {
- return true;
- }
- return false;
-}
-
-
-static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
-#ifndef V8_HOST_ARCH_MIPS
- USE(info);
- if (signal != SIGPROF) return;
- if (active_sampler_ == NULL) return;
-
- TickSample sample_obj;
- TickSample* sample = CpuProfiler::TickSampleEvent();
- if (sample == NULL) sample = &sample_obj;
-
- // We always sample the VM state.
- sample->state = VMState::current_state();
-
-#if 0
- // If profiling, we extract the current pc and sp.
- if (active_sampler_->IsProfiling()) {
- // Extracting the sample from the context is extremely machine dependent.
- ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
- mcontext_t& mcontext = ucontext->uc_mcontext;
-#if V8_HOST_ARCH_IA32
- sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
- sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
- sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
-#elif V8_HOST_ARCH_X64
- sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
- sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
- sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
-#elif V8_HOST_ARCH_ARM
-// An undefined macro evaluates to 0, so this applies to Android's Bionic also.
-#if (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
- sample->pc = reinterpret_cast<Address>(mcontext.gregs[R15]);
- sample->sp = reinterpret_cast<Address>(mcontext.gregs[R13]);
- sample->fp = reinterpret_cast<Address>(mcontext.gregs[R11]);
-#else
- sample->pc = reinterpret_cast<Address>(mcontext.arm_pc);
- sample->sp = reinterpret_cast<Address>(mcontext.arm_sp);
- sample->fp = reinterpret_cast<Address>(mcontext.arm_fp);
-#endif
-#elif V8_HOST_ARCH_MIPS
- // Implement this on MIPS.
- UNIMPLEMENTED();
-#endif
- if (IsVmThread()) {
- active_sampler_->SampleStack(sample);
- }
- }
-#endif
-
- active_sampler_->Tick(sample);
-#endif
-}
-
-
-class Sampler::PlatformData : public Malloced {
- public:
- PlatformData() {
- signal_handler_installed_ = false;
- }
-
- bool signal_handler_installed_;
- struct sigaction old_signal_handler_;
- struct itimerval old_timer_value_;
-};
-
-
-Sampler::Sampler(int interval, bool profiling)
- : interval_(interval),
- profiling_(profiling),
- synchronous_(profiling),
- active_(false) {
- data_ = new PlatformData();
-}
-
-
-Sampler::~Sampler() {
- delete data_;
-}
-
-
-void Sampler::Start() {
- // There can only be one active sampler at the time on POSIX
- // platforms.
- if (active_sampler_ != NULL) return;
-
- vm_thread_ = pthread_self();
-
- // Request profiling signals.
- struct sigaction sa;
- sa.sa_sigaction = ProfilerSignalHandler;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_SIGINFO;
- if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return;
- data_->signal_handler_installed_ = true;
-
- // Set the itimer to generate a tick for each interval.
- itimerval itimer;
- itimer.it_interval.tv_sec = interval_ / 1000;
- itimer.it_interval.tv_usec = (interval_ % 1000) * 1000;
- itimer.it_value.tv_sec = itimer.it_interval.tv_sec;
- itimer.it_value.tv_usec = itimer.it_interval.tv_usec;
- setitimer(ITIMER_PROF, &itimer, &data_->old_timer_value_);
-
- // Set this sampler as the active sampler.
- active_sampler_ = this;
- active_ = true;
-}
-
-
-void Sampler::Stop() {
- // Restore old signal handler
- if (data_->signal_handler_installed_) {
- setitimer(ITIMER_PROF, &data_->old_timer_value_, NULL);
- sigaction(SIGPROF, &data_->old_signal_handler_, 0);
- data_->signal_handler_installed_ = false;
- }
-
- // This sampler is no longer the active sampler.
- active_sampler_ = NULL;
- active_ = false;
-}
-
-
-#endif // ENABLE_LOGGING_AND_PROFILING
-
-} } // namespace v8::internal
diff --git a/deps/v8/src/platform-freebsd.cc b/deps/v8/src/platform-freebsd.cc
index b58d0662fb..ad1e499ac5 100644
--- a/deps/v8/src/platform-freebsd.cc
+++ b/deps/v8/src/platform-freebsd.cc
@@ -411,6 +411,12 @@ bool ThreadHandle::IsValid() const {
Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
+ set_name("v8:<unknown>");
+}
+
+
+Thread::Thread(const char* name) : ThreadHandle(ThreadHandle::INVALID) {
+ set_names(name);
}
@@ -430,6 +436,12 @@ static void* ThreadEntry(void* arg) {
}
+void Thread::set_name(const char* name) {
+ strncpy(name_, name, sizeof(name_));
+ name_[sizeof(name_) - 1] = '\0';
+}
+
+
void Thread::Start() {
pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
ASSERT(IsValid());
diff --git a/deps/v8/src/platform-linux.cc b/deps/v8/src/platform-linux.cc
index 7efb25de94..755e8cdaf6 100644
--- a/deps/v8/src/platform-linux.cc
+++ b/deps/v8/src/platform-linux.cc
@@ -31,6 +31,7 @@
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
+#include <sys/prctl.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/syscall.h>
@@ -551,6 +552,12 @@ bool ThreadHandle::IsValid() const {
Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
+ set_name("v8:<unknown>");
+}
+
+
+Thread::Thread(const char* name) : ThreadHandle(ThreadHandle::INVALID) {
+ set_name(name);
}
@@ -563,6 +570,7 @@ static void* ThreadEntry(void* arg) {
// This is also initialized by the first argument to pthread_create() but we
// don't know which thread will run first (the original thread or the new
// one) so we initialize it here too.
+ prctl(PR_SET_NAME, thread->name(), 0, 0, 0);
thread->thread_handle_data()->thread_ = pthread_self();
ASSERT(thread->IsValid());
thread->Run();
@@ -570,6 +578,12 @@ static void* ThreadEntry(void* arg) {
}
+void Thread::set_name(const char* name) {
+ strncpy(name_, name, sizeof(name_));
+ name_[sizeof(name_) - 1] = '\0';
+}
+
+
void Thread::Start() {
pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
ASSERT(IsValid());
diff --git a/deps/v8/src/platform-macos.cc b/deps/v8/src/platform-macos.cc
index 85c7088242..ce53305173 100644
--- a/deps/v8/src/platform-macos.cc
+++ b/deps/v8/src/platform-macos.cc
@@ -28,6 +28,7 @@
// Platform specific code for MacOS goes here. For the POSIX comaptible parts
// the implementation is in platform-posix.cc.
+#include <dlfcn.h>
#include <unistd.h>
#include <sys/mman.h>
#include <mach/mach_init.h>
@@ -49,7 +50,6 @@
#include <sys/types.h>
#include <stdarg.h>
#include <stdlib.h>
-
#include <errno.h>
#undef MAP_TYPE
@@ -411,6 +411,12 @@ bool ThreadHandle::IsValid() const {
Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
+ set_name("v8:<unknown>");
+}
+
+
+Thread::Thread(const char* name) : ThreadHandle(ThreadHandle::INVALID) {
+ set_name(name);
}
@@ -418,18 +424,43 @@ Thread::~Thread() {
}
+
+static void SetThreadName(const char* name) {
+ // pthread_setname_np is only available in 10.6 or later, so test
+ // for it at runtime.
+ int (*dynamic_pthread_setname_np)(const char*);
+ *reinterpret_cast<void**>(&dynamic_pthread_setname_np) =
+ dlsym(RTLD_DEFAULT, "pthread_setname_np");
+ if (!dynamic_pthread_setname_np)
+ return;
+
+ // Mac OS X does not expose the length limit of the name, so hardcode it.
+ static const int kMaxNameLength = 63;
+ USE(kMaxNameLength);
+ ASSERT(Thread::kMaxThreadNameLength <= kMaxNameLength);
+ dynamic_pthread_setname_np(name);
+}
+
+
static void* ThreadEntry(void* arg) {
Thread* thread = reinterpret_cast<Thread*>(arg);
// This is also initialized by the first argument to pthread_create() but we
// don't know which thread will run first (the original thread or the new
// one) so we initialize it here too.
thread->thread_handle_data()->thread_ = pthread_self();
+ SetThreadName(thread->name());
ASSERT(thread->IsValid());
thread->Run();
return NULL;
}
+void Thread::set_name(const char* name) {
+ strncpy(name_, name, sizeof(name_));
+ name_[sizeof(name_) - 1] = '\0';
+}
+
+
void Thread::Start() {
pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
}
diff --git a/deps/v8/src/platform-nullos.cc b/deps/v8/src/platform-nullos.cc
index 72ea0e5767..f1b7695981 100644
--- a/deps/v8/src/platform-nullos.cc
+++ b/deps/v8/src/platform-nullos.cc
@@ -335,6 +335,13 @@ bool ThreadHandle::IsValid() const {
Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
+ set_name("v8:<unknown>");
+ UNIMPLEMENTED();
+}
+
+
+Thread::Thread(const char* name) : ThreadHandle(ThreadHandle::INVALID) {
+ set_name(name);
UNIMPLEMENTED();
}
@@ -344,6 +351,12 @@ Thread::~Thread() {
}
+void Thread::set_name(const char* name) {
+ strncpy(name_, name, sizeof(name_));
+ name_[sizeof(name_) - 1] = '\0';
+}
+
+
void Thread::Start() {
UNIMPLEMENTED();
}
diff --git a/deps/v8/src/platform-openbsd.cc b/deps/v8/src/platform-openbsd.cc
index b698d16b9a..5de6081907 100644
--- a/deps/v8/src/platform-openbsd.cc
+++ b/deps/v8/src/platform-openbsd.cc
@@ -387,6 +387,12 @@ bool ThreadHandle::IsValid() const {
Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
+ set_name("v8:<unknown>");
+}
+
+
+Thread::Thread(const char* name) : ThreadHandle(ThreadHandle::INVALID) {
+ set_name(name);
}
@@ -406,6 +412,12 @@ static void* ThreadEntry(void* arg) {
}
+void Thread::set_name(const char* name) {
+ strncpy(name_, name, sizeof(name_));
+ name_[sizeof(name_) - 1] = '\0';
+}
+
+
void Thread::Start() {
pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
ASSERT(IsValid());
diff --git a/deps/v8/src/platform-solaris.cc b/deps/v8/src/platform-solaris.cc
index b302c596cf..dc4493aab9 100644
--- a/deps/v8/src/platform-solaris.cc
+++ b/deps/v8/src/platform-solaris.cc
@@ -402,6 +402,12 @@ bool ThreadHandle::IsValid() const {
Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
+ set_name("v8:<unknown>");
+}
+
+
+Thread::Thread(const char* name) : ThreadHandle(ThreadHandle::INVALID) {
+ set_name(name);
}
@@ -421,6 +427,12 @@ static void* ThreadEntry(void* arg) {
}
+void Thread::set_name(const char* name) {
+ strncpy(name_, name, sizeof(name_));
+ name_[sizeof(name_) - 1] = '\0';
+}
+
+
void Thread::Start() {
pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
ASSERT(IsValid());
diff --git a/deps/v8/src/platform-win32.cc b/deps/v8/src/platform-win32.cc
index 4438045eaf..81216e190d 100644
--- a/deps/v8/src/platform-win32.cc
+++ b/deps/v8/src/platform-win32.cc
@@ -1463,6 +1463,19 @@ class Thread::PlatformData : public Malloced {
Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
data_ = new PlatformData(kNoThread);
+ set_name("v8:<unknown>");
+}
+
+
+Thread::Thread(const char* name) : ThreadHandle(ThreadHandle::INVALID) {
+ data_ = new PlatformData(kNoThread);
+ set_name(name);
+}
+
+
+void Thread::set_name(const char* name) {
+ strncpy_s(name_, name, sizeof(name_));
+ name_[sizeof(name_) - 1] = '\0';
}
diff --git a/deps/v8/src/platform.h b/deps/v8/src/platform.h
index bdfbbab698..7b17067477 100644
--- a/deps/v8/src/platform.h
+++ b/deps/v8/src/platform.h
@@ -376,7 +376,6 @@ class ThreadHandle {
class Thread: public ThreadHandle {
public:
-#ifndef __CYGWIN__
// Opaque data type for thread-local storage keys.
// LOCAL_STORAGE_KEY_MIN_VALUE and LOCAL_STORAGE_KEY_MAX_VALUE are specified
// to ensure that enumeration type has correct value range (see Issue 830 for
@@ -385,13 +384,10 @@ class Thread: public ThreadHandle {
LOCAL_STORAGE_KEY_MIN_VALUE = kMinInt,
LOCAL_STORAGE_KEY_MAX_VALUE = kMaxInt
};
-#else
- typedef void *LocalStorageKey;
-#endif
-
// Create new thread.
Thread();
+ explicit Thread(const char* name);
virtual ~Thread();
// Start new thread by calling the Run() method in the new thread.
@@ -400,6 +396,10 @@ class Thread: public ThreadHandle {
// Wait until thread terminates.
void Join();
+ inline const char* name() const {
+ return name_;
+ }
+
// Abstract method for run handler.
virtual void Run() = 0;
@@ -421,9 +421,17 @@ class Thread: public ThreadHandle {
// A hint to the scheduler to let another thread run.
static void YieldCPU();
+ // The thread name length is limited to 16 based on Linux's implementation of
+ // prctl().
+ static const int kMaxThreadNameLength = 16;
private:
+ void set_name(const char *name);
+
class PlatformData;
PlatformData* data_;
+
+ char name_[kMaxThreadNameLength];
+
DISALLOW_COPY_AND_ASSIGN(Thread);
};
diff --git a/deps/v8/src/preparse-data.cc b/deps/v8/src/preparse-data.cc
index 9a3677183e..7c9d8a6109 100644
--- a/deps/v8/src/preparse-data.cc
+++ b/deps/v8/src/preparse-data.cc
@@ -110,26 +110,29 @@ Vector<unsigned> PartialParserRecorder::ExtractData() {
CompleteParserRecorder::CompleteParserRecorder()
: FunctionLoggingParserRecorder(),
+ literal_chars_(0),
symbol_store_(0),
- symbol_entries_(0),
+ symbol_keys_(0),
symbol_table_(vector_compare),
symbol_id_(0) {
}
-void CompleteParserRecorder::LogSymbol(
- int start, const char* literal_chars, int length) {
- if (!is_recording_) return;
-
- Vector<const char> literal(literal_chars, length);
- int hash = vector_hash(literal);
- HashMap::Entry* entry = symbol_table_.Lookup(&literal, hash, true);
+void CompleteParserRecorder::LogSymbol(int start,
+ int hash,
+ bool is_ascii,
+ Vector<const byte> literal_bytes) {
+ Key key = { is_ascii, literal_bytes };
+ HashMap::Entry* entry = symbol_table_.Lookup(&key, hash, true);
int id = static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
if (id == 0) {
+ // Copy literal contents for later comparison.
+ key.literal_bytes =
+ Vector<const byte>::cast(literal_chars_.AddBlock(literal_bytes));
// Put (symbol_id_ + 1) into entry and increment it.
id = ++symbol_id_;
entry->value = reinterpret_cast<void*>(id);
- Vector<Vector<const char> > symbol = symbol_entries_.AddBlock(1, literal);
+ Vector<Key> symbol = symbol_keys_.AddBlock(1, key);
entry->key = &symbol[0];
}
WriteNumber(id - 1);
diff --git a/deps/v8/src/preparse-data.h b/deps/v8/src/preparse-data.h
index a96e50fa10..cc82bcc62c 100644
--- a/deps/v8/src/preparse-data.h
+++ b/deps/v8/src/preparse-data.h
@@ -75,7 +75,8 @@ class ParserRecorder {
int properties) = 0;
// Logs a symbol creation of a literal or identifier.
- virtual void LogSymbol(int start, const char* symbol, int length) = 0;
+ virtual void LogAsciiSymbol(int start, Vector<const char> literal) { }
+ virtual void LogUC16Symbol(int start, Vector<const uc16> literal) { }
// Logs an error message and marks the log as containing an error.
// Further logging will be ignored, and ExtractData will return a vector
@@ -165,7 +166,8 @@ class FunctionLoggingParserRecorder : public ParserRecorder {
class PartialParserRecorder : public FunctionLoggingParserRecorder {
public:
PartialParserRecorder() : FunctionLoggingParserRecorder() { }
- virtual void LogSymbol(int start, const char* symbol, int length) { }
+ virtual void LogAsciiSymbol(int start, Vector<const char> literal) { }
+ virtual void LogUC16Symbol(int start, Vector<const uc16> literal) { }
virtual ~PartialParserRecorder() { }
virtual Vector<unsigned> ExtractData();
virtual int symbol_position() { return 0; }
@@ -181,7 +183,17 @@ class CompleteParserRecorder: public FunctionLoggingParserRecorder {
CompleteParserRecorder();
virtual ~CompleteParserRecorder() { }
- virtual void LogSymbol(int start, const char* symbol, int length);
+ virtual void LogAsciiSymbol(int start, Vector<const char> literal) {
+ if (!is_recording_) return;
+ int hash = vector_hash(literal);
+ LogSymbol(start, hash, true, Vector<const byte>::cast(literal));
+ }
+
+ virtual void LogUC16Symbol(int start, Vector<const uc16> literal) {
+ if (!is_recording_) return;
+ int hash = vector_hash(literal);
+ LogSymbol(start, hash, false, Vector<const byte>::cast(literal));
+ }
virtual Vector<unsigned> ExtractData();
@@ -189,10 +201,21 @@ class CompleteParserRecorder: public FunctionLoggingParserRecorder {
virtual int symbol_ids() { return symbol_id_; }
private:
- static int vector_hash(Vector<const char> string) {
+ struct Key {
+ bool is_ascii;
+ Vector<const byte> literal_bytes;
+ };
+
+ virtual void LogSymbol(int start,
+ int hash,
+ bool is_ascii,
+ Vector<const byte> literal);
+
+ template <typename Char>
+ static int vector_hash(Vector<const Char> string) {
int hash = 0;
for (int i = 0; i < string.length(); i++) {
- int c = string[i];
+ int c = static_cast<int>(string[i]);
hash += c;
hash += (hash << 10);
hash ^= (hash >> 6);
@@ -201,18 +224,21 @@ class CompleteParserRecorder: public FunctionLoggingParserRecorder {
}
static bool vector_compare(void* a, void* b) {
- Vector<const char>* string1 = reinterpret_cast<Vector<const char>* >(a);
- Vector<const char>* string2 = reinterpret_cast<Vector<const char>* >(b);
- int length = string1->length();
- if (string2->length() != length) return false;
- return memcmp(string1->start(), string2->start(), length) == 0;
+ Key* string1 = reinterpret_cast<Key*>(a);
+ Key* string2 = reinterpret_cast<Key*>(b);
+ if (string1->is_ascii != string2->is_ascii) return false;
+ int length = string1->literal_bytes.length();
+ if (string2->literal_bytes.length() != length) return false;
+ return memcmp(string1->literal_bytes.start(),
+ string2->literal_bytes.start(), length) == 0;
}
// Write a non-negative number to the symbol store.
void WriteNumber(int number);
+ Collector<byte> literal_chars_;
Collector<byte> symbol_store_;
- Collector<Vector<const char> > symbol_entries_;
+ Collector<Key> symbol_keys_;
HashMap symbol_table_;
int symbol_id_;
};
diff --git a/deps/v8/src/preparser-api.cc b/deps/v8/src/preparser-api.cc
index cbec9b7096..3817935f8f 100644
--- a/deps/v8/src/preparser-api.cc
+++ b/deps/v8/src/preparser-api.cc
@@ -69,8 +69,12 @@ class InputStreamUTF16Buffer : public UC16CharacterStream {
}
}
- virtual void PushBack(uc16 ch) {
+ virtual void PushBack(uc32 ch) {
ASSERT(pos_ > 0);
+ if (ch == kEndOfInput) {
+ pos_--;
+ return;
+ }
if (buffer_cursor_ <= pushback_buffer_) {
// No more room in the current buffer to do pushbacks.
if (pushback_buffer_end_cache_ == NULL) {
@@ -98,7 +102,8 @@ class InputStreamUTF16Buffer : public UC16CharacterStream {
buffer_end_ = pushback_buffer_backing_ + pushback_buffer_backing_size_;
}
}
- pushback_buffer_[buffer_cursor_ - pushback_buffer_- 1] = ch;
+ pushback_buffer_[buffer_cursor_ - pushback_buffer_- 1] =
+ static_cast<uc16>(ch);
pos_--;
}
@@ -155,7 +160,6 @@ class StandAloneJavaScriptScanner : public JavaScriptScanner {
public:
void Initialize(UC16CharacterStream* source) {
source_ = source;
- literal_flags_ = kLiteralString | kLiteralIdentifier;
Init();
// Skip initial whitespace allowing HTML comment ends just like
// after a newline and scan first token.
diff --git a/deps/v8/src/preparser.cc b/deps/v8/src/preparser.cc
index 7cce685eee..c0dcc0b4a1 100644
--- a/deps/v8/src/preparser.cc
+++ b/deps/v8/src/preparser.cc
@@ -1,3 +1,4 @@
+
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
@@ -894,6 +895,7 @@ PreParser::Expression PreParser::ParsePrimaryExpression(bool* ok) {
case i::Token::LPAREN:
Consume(i::Token::LPAREN);
+ parenthesized_function_ = (peek() == i::Token::FUNCTION);
result = ParseExpression(true, CHECK_OK);
Expect(i::Token::RPAREN, CHECK_OK);
if (result == kIdentifierExpression) result = kUnknownExpression;
@@ -950,13 +952,17 @@ PreParser::Expression PreParser::ParseObjectLiteral(bool* ok) {
ParseIdentifierOrGetOrSet(&is_getter, &is_setter, CHECK_OK);
if ((is_getter || is_setter) && peek() != i::Token::COLON) {
i::Token::Value name = Next();
+ bool is_keyword = i::Token::IsKeyword(name);
if (name != i::Token::IDENTIFIER &&
name != i::Token::NUMBER &&
name != i::Token::STRING &&
- !i::Token::IsKeyword(name)) {
+ !is_keyword) {
*ok = false;
return kUnknownExpression;
}
+ if (!is_keyword) {
+ LogSymbol();
+ }
ParseFunctionLiteral(CHECK_OK);
if (peek() != i::Token::RBRACE) {
Expect(i::Token::COMMA, CHECK_OK);
@@ -1067,8 +1073,10 @@ PreParser::Expression PreParser::ParseFunctionLiteral(bool* ok) {
// Determine if the function will be lazily compiled.
// Currently only happens to top-level functions.
// Optimistically assume that all top-level functions are lazily compiled.
- bool is_lazily_compiled =
- (outer_scope_type == kTopLevelScope && !inside_with && allow_lazy_);
+ bool is_lazily_compiled = (outer_scope_type == kTopLevelScope &&
+ !inside_with && allow_lazy_ &&
+ !parenthesized_function_);
+ parenthesized_function_ = false;
if (is_lazily_compiled) {
log_->PauseRecording();
@@ -1120,24 +1128,24 @@ void PreParser::ExpectSemicolon(bool* ok) {
}
-PreParser::Identifier PreParser::GetIdentifierSymbol() {
- const char* literal_chars = scanner_->literal_string();
- int literal_length = scanner_->literal_length();
+void PreParser::LogSymbol() {
int identifier_pos = scanner_->location().beg_pos;
+ if (scanner_->is_literal_ascii()) {
+ log_->LogAsciiSymbol(identifier_pos, scanner_->literal_ascii_string());
+ } else {
+ log_->LogUC16Symbol(identifier_pos, scanner_->literal_uc16_string());
+ }
+}
- log_->LogSymbol(identifier_pos, literal_chars, literal_length);
- return kUnknownExpression;
+PreParser::Identifier PreParser::GetIdentifierSymbol() {
+ LogSymbol();
+ return kUnknownIdentifier;
}
PreParser::Expression PreParser::GetStringSymbol() {
- const char* literal_chars = scanner_->literal_string();
- int literal_length = scanner_->literal_length();
-
- int literal_position = scanner_->location().beg_pos;
- log_->LogSymbol(literal_position, literal_chars, literal_length);
-
+ LogSymbol();
return kUnknownExpression;
}
@@ -1154,7 +1162,8 @@ PreParser::Identifier PreParser::ParseIdentifierName(bool* ok) {
if (i::Token::IsKeyword(next)) {
int pos = scanner_->location().beg_pos;
const char* keyword = i::Token::String(next);
- log_->LogSymbol(pos, keyword, i::StrLength(keyword));
+ log_->LogAsciiSymbol(pos, i::Vector<const char>(keyword,
+ i::StrLength(keyword)));
return kUnknownExpression;
}
if (next == i::Token::IDENTIFIER) {
@@ -1173,8 +1182,8 @@ PreParser::Identifier PreParser::ParseIdentifierOrGetOrSet(bool* is_get,
bool* is_set,
bool* ok) {
Expect(i::Token::IDENTIFIER, CHECK_OK);
- if (scanner_->literal_length() == 3) {
- const char* token = scanner_->literal_string();
+ if (scanner_->is_literal_ascii() && scanner_->literal_length() == 3) {
+ const char* token = scanner_->literal_ascii_string().start();
*is_get = strncmp(token, "get", 3) == 0;
*is_set = !*is_get && strncmp(token, "set", 3) == 0;
}
diff --git a/deps/v8/src/preparser.h b/deps/v8/src/preparser.h
index 893b575198..66fad3bcbf 100644
--- a/deps/v8/src/preparser.h
+++ b/deps/v8/src/preparser.h
@@ -144,7 +144,8 @@ class PreParser {
scope_(NULL),
stack_limit_(stack_limit),
stack_overflow_(false),
- allow_lazy_(true) { }
+ allow_lazy_(true),
+ parenthesized_function_(false) { }
// Preparse the program. Only called in PreParseProgram after creating
// the instance.
@@ -216,8 +217,11 @@ class PreParser {
Identifier ParseIdentifierName(bool* ok);
Identifier ParseIdentifierOrGetOrSet(bool* is_get, bool* is_set, bool* ok);
+ // Logs the currently parsed literal as a symbol in the preparser data.
+ void LogSymbol();
+ // Log the currently parsed identifier.
Identifier GetIdentifierSymbol();
- unsigned int HexDigitValue(char digit);
+ // Log the currently parsed string literal.
Expression GetStringSymbol();
i::Token::Value peek() {
@@ -265,6 +269,7 @@ class PreParser {
uintptr_t stack_limit_;
bool stack_overflow_;
bool allow_lazy_;
+ bool parenthesized_function_;
};
} } // v8::preparser
diff --git a/deps/v8/src/profile-generator.cc b/deps/v8/src/profile-generator.cc
index 34d18771cf..4476cb876c 100644
--- a/deps/v8/src/profile-generator.cc
+++ b/deps/v8/src/profile-generator.cc
@@ -2385,7 +2385,7 @@ bool HeapSnapshotGenerator::IterateAndExtractReferences() {
if (interrupted) return false;
SetRootGcRootsReference();
RootsReferencesExtractor extractor(this);
- Heap::IterateRoots(&extractor, VISIT_ONLY_STRONG);
+ Heap::IterateRoots(&extractor, VISIT_ALL);
return ReportProgress();
}
diff --git a/deps/v8/src/regexp-macro-assembler-irregexp.h b/deps/v8/src/regexp-macro-assembler-irregexp.h
index 6c9c2eb090..9deea86f44 100644
--- a/deps/v8/src/regexp-macro-assembler-irregexp.h
+++ b/deps/v8/src/regexp-macro-assembler-irregexp.h
@@ -76,18 +76,18 @@ class RegExpMacroAssemblerIrregexp: public RegExpMacroAssembler {
Label* on_end_of_input,
bool check_bounds = true,
int characters = 1);
- virtual void CheckCharacter(uint32_t c, Label* on_equal);
- virtual void CheckCharacterAfterAnd(uint32_t c,
- uint32_t mask,
+ virtual void CheckCharacter(unsigned c, Label* on_equal);
+ virtual void CheckCharacterAfterAnd(unsigned c,
+ unsigned mask,
Label* on_equal);
virtual void CheckCharacterGT(uc16 limit, Label* on_greater);
virtual void CheckCharacterLT(uc16 limit, Label* on_less);
virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
virtual void CheckAtStart(Label* on_at_start);
virtual void CheckNotAtStart(Label* on_not_at_start);
- virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal);
- virtual void CheckNotCharacterAfterAnd(uint32_t c,
- uint32_t mask,
+ virtual void CheckNotCharacter(unsigned c, Label* on_not_equal);
+ virtual void CheckNotCharacterAfterAnd(unsigned c,
+ unsigned mask,
Label* on_not_equal);
virtual void CheckNotCharacterAfterMinusAnd(uc16 c,
uc16 minus,
diff --git a/deps/v8/src/regexp-macro-assembler-tracer.cc b/deps/v8/src/regexp-macro-assembler-tracer.cc
index 463c1a81c5..fa2c65790a 100644
--- a/deps/v8/src/regexp-macro-assembler-tracer.cc
+++ b/deps/v8/src/regexp-macro-assembler-tracer.cc
@@ -213,7 +213,7 @@ void RegExpMacroAssemblerTracer::CheckCharacterGT(uc16 limit,
}
-void RegExpMacroAssemblerTracer::CheckCharacter(uint32_t c, Label* on_equal) {
+void RegExpMacroAssemblerTracer::CheckCharacter(unsigned c, Label* on_equal) {
PrintF(" CheckCharacter(c='u%04x', label[%08x]);\n",
c, LabelToInt(on_equal));
assembler_->CheckCharacter(c, on_equal);
@@ -232,7 +232,7 @@ void RegExpMacroAssemblerTracer::CheckNotAtStart(Label* on_not_at_start) {
}
-void RegExpMacroAssemblerTracer::CheckNotCharacter(uint32_t c,
+void RegExpMacroAssemblerTracer::CheckNotCharacter(unsigned c,
Label* on_not_equal) {
PrintF(" CheckNotCharacter(c='u%04x', label[%08x]);\n",
c, LabelToInt(on_not_equal));
@@ -241,8 +241,8 @@ void RegExpMacroAssemblerTracer::CheckNotCharacter(uint32_t c,
void RegExpMacroAssemblerTracer::CheckCharacterAfterAnd(
- uint32_t c,
- uint32_t mask,
+ unsigned c,
+ unsigned mask,
Label* on_equal) {
PrintF(" CheckCharacterAfterAnd(c='u%04x', mask=0x%04x, label[%08x]);\n",
c,
@@ -253,8 +253,8 @@ void RegExpMacroAssemblerTracer::CheckCharacterAfterAnd(
void RegExpMacroAssemblerTracer::CheckNotCharacterAfterAnd(
- uint32_t c,
- uint32_t mask,
+ unsigned c,
+ unsigned mask,
Label* on_not_equal) {
PrintF(" CheckNotCharacterAfterAnd(c='u%04x', mask=0x%04x, label[%08x]);\n",
c,
diff --git a/deps/v8/src/regexp-macro-assembler-tracer.h b/deps/v8/src/regexp-macro-assembler-tracer.h
index 6a8f4d47f8..1fb6d54420 100644
--- a/deps/v8/src/regexp-macro-assembler-tracer.h
+++ b/deps/v8/src/regexp-macro-assembler-tracer.h
@@ -43,9 +43,9 @@ class RegExpMacroAssemblerTracer: public RegExpMacroAssembler {
virtual void Backtrack();
virtual void Bind(Label* label);
virtual void CheckAtStart(Label* on_at_start);
- virtual void CheckCharacter(uint32_t c, Label* on_equal);
- virtual void CheckCharacterAfterAnd(uint32_t c,
- uint32_t and_with,
+ virtual void CheckCharacter(unsigned c, Label* on_equal);
+ virtual void CheckCharacterAfterAnd(unsigned c,
+ unsigned and_with,
Label* on_equal);
virtual void CheckCharacterGT(uc16 limit, Label* on_greater);
virtual void CheckCharacterLT(uc16 limit, Label* on_less);
@@ -60,9 +60,9 @@ class RegExpMacroAssemblerTracer: public RegExpMacroAssembler {
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
Label* on_no_match);
virtual void CheckNotRegistersEqual(int reg1, int reg2, Label* on_not_equal);
- virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal);
- virtual void CheckNotCharacterAfterAnd(uint32_t c,
- uint32_t and_with,
+ virtual void CheckNotCharacter(unsigned c, Label* on_not_equal);
+ virtual void CheckNotCharacterAfterAnd(unsigned c,
+ unsigned and_with,
Label* on_not_equal);
virtual void CheckNotCharacterAfterMinusAnd(uc16 c,
uc16 minus,
diff --git a/deps/v8/src/regexp-macro-assembler.h b/deps/v8/src/regexp-macro-assembler.h
index dc3bd824ed..ef85d27e52 100644
--- a/deps/v8/src/regexp-macro-assembler.h
+++ b/deps/v8/src/regexp-macro-assembler.h
@@ -73,11 +73,11 @@ class RegExpMacroAssembler {
virtual void CheckAtStart(Label* on_at_start) = 0;
// Dispatch after looking the current character up in a 2-bits-per-entry
// map. The destinations vector has up to 4 labels.
- virtual void CheckCharacter(uint32_t c, Label* on_equal) = 0;
+ virtual void CheckCharacter(unsigned c, Label* on_equal) = 0;
// Bitwise and the current character with the given constant and then
// check for a match with c.
- virtual void CheckCharacterAfterAnd(uint32_t c,
- uint32_t and_with,
+ virtual void CheckCharacterAfterAnd(unsigned c,
+ unsigned and_with,
Label* on_equal) = 0;
virtual void CheckCharacterGT(uc16 limit, Label* on_greater) = 0;
virtual void CheckCharacterLT(uc16 limit, Label* on_less) = 0;
@@ -101,9 +101,9 @@ class RegExpMacroAssembler {
// fail to match then goto the on_failure label. End of input always
// matches. If the label is NULL then we should pop a backtrack address off
// the stack and go to that.
- virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal) = 0;
- virtual void CheckNotCharacterAfterAnd(uint32_t c,
- uint32_t and_with,
+ virtual void CheckNotCharacter(unsigned c, Label* on_not_equal) = 0;
+ virtual void CheckNotCharacterAfterAnd(unsigned c,
+ unsigned and_with,
Label* on_not_equal) = 0;
// Subtract a constant from the current character, then or with the given
// constant and then check for a match with c.
diff --git a/deps/v8/src/rewriter.cc b/deps/v8/src/rewriter.cc
index 3d737a49b9..fd40cdc3fa 100644
--- a/deps/v8/src/rewriter.cc
+++ b/deps/v8/src/rewriter.cc
@@ -978,7 +978,7 @@ void Processor::VisitThisFunction(ThisFunction* node) {
}
-// Assumes code has been parsed and scopes hve been analyzed. Mutates the
+// Assumes code has been parsed and scopes have been analyzed. Mutates the
// AST, so the AST should not continue to be used in the case of failure.
bool Rewriter::Rewrite(CompilationInfo* info) {
FunctionLiteral* function = info->function();
diff --git a/deps/v8/src/runtime-profiler.cc b/deps/v8/src/runtime-profiler.cc
index c53ddd2b97..1efc6ef620 100644
--- a/deps/v8/src/runtime-profiler.cc
+++ b/deps/v8/src/runtime-profiler.cc
@@ -165,8 +165,10 @@ static void AttemptOnStackReplacement(JSFunction* function) {
}
SharedFunctionInfo* shared = function->shared();
- // If the code is not optimizable, don't try OSR.
- if (!shared->code()->optimizable()) return;
+ // If the code is not optimizable or references context slots, don't try OSR.
+ if (!shared->code()->optimizable() || !shared->allows_lazy_compilation()) {
+ return;
+ }
// We are not prepared to do OSR for a function that already has an
// allocated arguments object. The optimized code would bypass it for
diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc
index 724a436348..0cde7779a3 100644
--- a/deps/v8/src/runtime.cc
+++ b/deps/v8/src/runtime.cc
@@ -330,13 +330,18 @@ static Handle<Object> CreateObjectLiteralBoilerplate(
Handle<Object> result;
uint32_t element_index = 0;
if (key->IsSymbol()) {
- // If key is a symbol it is not an array element.
- Handle<String> name(String::cast(*key));
- ASSERT(!name->AsArrayIndex(&element_index));
- result = SetProperty(boilerplate, name, value, NONE);
+ if (Handle<String>::cast(key)->AsArrayIndex(&element_index)) {
+ // Array index as string (uint32).
+ result = SetOwnElement(boilerplate, element_index, value);
+ } else {
+ Handle<String> name(String::cast(*key));
+ ASSERT(!name->AsArrayIndex(&element_index));
+ result = SetLocalPropertyIgnoreAttributes(boilerplate, name,
+ value, NONE);
+ }
} else if (key->ToArrayIndex(&element_index)) {
// Array index (uint32).
- result = SetElement(boilerplate, element_index, value);
+ result = SetOwnElement(boilerplate, element_index, value);
} else {
// Non-uint32 number.
ASSERT(key->IsNumber());
@@ -345,7 +350,8 @@ static Handle<Object> CreateObjectLiteralBoilerplate(
Vector<char> buffer(arr, ARRAY_SIZE(arr));
const char* str = DoubleToCString(num, buffer);
Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
- result = SetProperty(boilerplate, name, value, NONE);
+ result = SetLocalPropertyIgnoreAttributes(boilerplate, name,
+ value, NONE);
}
// If setting the property on the boilerplate throws an
// exception, the exception is converted to an empty handle in
@@ -984,7 +990,7 @@ static MaybeObject* Runtime_DeclareGlobals(Arguments args) {
// of callbacks in the prototype chain (this rules out using
// SetProperty). Also, we must use the handle-based version to
// avoid GC issues.
- IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
+ SetLocalPropertyIgnoreAttributes(global, name, value, attributes);
}
}
@@ -1099,7 +1105,7 @@ static MaybeObject* Runtime_InitializeVarGlobal(Arguments args) {
// to assign to the property. When adding the property we take
// special precautions to always add it as a local property even in
// case of callbacks in the prototype chain (this rules out using
- // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
+ // SetProperty). We have SetLocalPropertyIgnoreAttributes for
// this.
// Note that objects can have hidden prototypes, so we need to traverse
// the whole chain of hidden prototypes to do a 'local' lookup.
@@ -1162,9 +1168,9 @@ static MaybeObject* Runtime_InitializeVarGlobal(Arguments args) {
global = Top::context()->global();
if (assign) {
- return global->IgnoreAttributesAndSetLocalProperty(*name,
- args[1],
- attributes);
+ return global->SetLocalPropertyIgnoreAttributes(*name,
+ args[1],
+ attributes);
}
return Heap::undefined_value();
}
@@ -1190,13 +1196,13 @@ static MaybeObject* Runtime_InitializeConstGlobal(Arguments args) {
// there, we add the property and take special precautions to always
// add it as a local property even in case of callbacks in the
// prototype chain (this rules out using SetProperty).
- // We use IgnoreAttributesAndSetLocalProperty instead
+ // We use SetLocalPropertyIgnoreAttributes instead
LookupResult lookup;
global->LocalLookup(*name, &lookup);
if (!lookup.IsProperty()) {
- return global->IgnoreAttributesAndSetLocalProperty(*name,
- *value,
- attributes);
+ return global->SetLocalPropertyIgnoreAttributes(*name,
+ *value,
+ attributes);
}
// Determine if this is a redeclaration of something not
@@ -1467,27 +1473,27 @@ static MaybeObject* Runtime_RegExpInitializeObject(Arguments args) {
PropertyAttributes writable =
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
MaybeObject* result;
- result = regexp->IgnoreAttributesAndSetLocalProperty(Heap::source_symbol(),
- source,
- final);
+ result = regexp->SetLocalPropertyIgnoreAttributes(Heap::source_symbol(),
+ source,
+ final);
ASSERT(!result->IsFailure());
- result = regexp->IgnoreAttributesAndSetLocalProperty(Heap::global_symbol(),
- global,
- final);
+ result = regexp->SetLocalPropertyIgnoreAttributes(Heap::global_symbol(),
+ global,
+ final);
ASSERT(!result->IsFailure());
result =
- regexp->IgnoreAttributesAndSetLocalProperty(Heap::ignore_case_symbol(),
- ignoreCase,
- final);
+ regexp->SetLocalPropertyIgnoreAttributes(Heap::ignore_case_symbol(),
+ ignoreCase,
+ final);
ASSERT(!result->IsFailure());
- result = regexp->IgnoreAttributesAndSetLocalProperty(Heap::multiline_symbol(),
- multiline,
- final);
+ result = regexp->SetLocalPropertyIgnoreAttributes(Heap::multiline_symbol(),
+ multiline,
+ final);
ASSERT(!result->IsFailure());
result =
- regexp->IgnoreAttributesAndSetLocalProperty(Heap::last_index_symbol(),
- Smi::FromInt(0),
- writable);
+ regexp->SetLocalPropertyIgnoreAttributes(Heap::last_index_symbol(),
+ Smi::FromInt(0),
+ writable);
ASSERT(!result->IsFailure());
USE(result);
return regexp;
@@ -1743,6 +1749,7 @@ static MaybeObject* Runtime_SetCode(Arguments args) {
// Array, and Object, and some web code
// doesn't like seeing source code for constructors.
target->shared()->set_script(Heap::undefined_value());
+ target->shared()->code()->set_optimizable(false);
// Clear the optimization hints related to the compiled code as these are no
// longer valid when the code is overwritten.
target->shared()->ClearThisPropertyAssignmentsInfo();
@@ -3571,9 +3578,9 @@ static MaybeObject* Runtime_DefineOrRedefineDataProperty(Arguments args) {
NormalizeProperties(js_object, CLEAR_INOBJECT_PROPERTIES, 0);
// Use IgnoreAttributes version since a readonly property may be
// overridden and SetProperty does not allow this.
- return js_object->IgnoreAttributesAndSetLocalProperty(*name,
- *obj_value,
- attr);
+ return js_object->SetLocalPropertyIgnoreAttributes(*name,
+ *obj_value,
+ attr);
}
return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
@@ -3674,9 +3681,9 @@ MaybeObject* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
} else {
Handle<String> key_string = Handle<String>::cast(key);
key_string->TryFlatten();
- return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
- *value,
- attr);
+ return js_object->SetLocalPropertyIgnoreAttributes(*key_string,
+ *value,
+ attr);
}
}
@@ -3689,7 +3696,7 @@ MaybeObject* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
if (name->AsArrayIndex(&index)) {
return js_object->SetElement(index, *value);
} else {
- return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
+ return js_object->SetLocalPropertyIgnoreAttributes(*name, *value, attr);
}
}
@@ -3771,7 +3778,7 @@ static MaybeObject* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
}
return object->
- IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
+ SetLocalPropertyIgnoreAttributes(name, args[2], attributes);
}
@@ -4615,12 +4622,12 @@ MaybeObject* AllocateRawString<SeqAsciiString>(int length) {
}
-template <typename Char, typename StringType>
+template <typename Char, typename StringType, bool comma>
static MaybeObject* SlowQuoteJsonString(Vector<const Char> characters) {
int length = characters.length();
const Char* read_cursor = characters.start();
const Char* end = read_cursor + length;
- const int kSpaceForQuotes = 2;
+ const int kSpaceForQuotes = 2 + (comma ? 1 :0);
int quoted_length = kSpaceForQuotes;
while (read_cursor < end) {
Char c = *(read_cursor++);
@@ -4639,6 +4646,7 @@ static MaybeObject* SlowQuoteJsonString(Vector<const Char> characters) {
Char* write_cursor = reinterpret_cast<Char*>(
new_string->address() + SeqAsciiString::kHeaderSize);
+ if (comma) *(write_cursor++) = ',';
*(write_cursor++) = '"';
read_cursor = characters.start();
@@ -4660,14 +4668,14 @@ static MaybeObject* SlowQuoteJsonString(Vector<const Char> characters) {
}
-template <typename Char, typename StringType>
+template <typename Char, typename StringType, bool comma>
static MaybeObject* QuoteJsonString(Vector<const Char> characters) {
int length = characters.length();
Counters::quote_json_char_count.Increment(length);
- const int kSpaceForQuotes = 2;
+ const int kSpaceForQuotes = 2 + (comma ? 1 :0);
int worst_case_length = length * kJsonQuoteWorstCaseBlowup + kSpaceForQuotes;
if (worst_case_length > kMaxGuaranteedNewSpaceString) {
- return SlowQuoteJsonString<Char, StringType>(characters);
+ return SlowQuoteJsonString<Char, StringType, comma>(characters);
}
MaybeObject* new_alloc = AllocateRawString<StringType>(worst_case_length);
@@ -4680,7 +4688,7 @@ static MaybeObject* QuoteJsonString(Vector<const Char> characters) {
// handle it being allocated in old space as may happen in the third
// attempt. See CALL_AND_RETRY in heap-inl.h and similar code in
// CEntryStub::GenerateCore.
- return SlowQuoteJsonString<Char, StringType>(characters);
+ return SlowQuoteJsonString<Char, StringType, comma>(characters);
}
StringType* new_string = StringType::cast(new_object);
ASSERT(Heap::new_space()->Contains(new_string));
@@ -4688,6 +4696,7 @@ static MaybeObject* QuoteJsonString(Vector<const Char> characters) {
STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize);
Char* write_cursor = reinterpret_cast<Char*>(
new_string->address() + SeqAsciiString::kHeaderSize);
+ if (comma) *(write_cursor++) = ',';
*(write_cursor++) = '"';
const Char* read_cursor = characters.start();
@@ -4738,13 +4747,32 @@ static MaybeObject* Runtime_QuoteJSONString(Arguments args) {
ASSERT(str->IsFlat());
}
if (str->IsTwoByteRepresentation()) {
- return QuoteJsonString<uc16, SeqTwoByteString>(str->ToUC16Vector());
+ return QuoteJsonString<uc16, SeqTwoByteString, false>(str->ToUC16Vector());
} else {
- return QuoteJsonString<char, SeqAsciiString>(str->ToAsciiVector());
+ return QuoteJsonString<char, SeqAsciiString, false>(str->ToAsciiVector());
}
}
+static MaybeObject* Runtime_QuoteJSONStringComma(Arguments args) {
+ NoHandleAllocation ha;
+ CONVERT_CHECKED(String, str, args[0]);
+ if (!str->IsFlat()) {
+ MaybeObject* try_flatten = str->TryFlatten();
+ Object* flat;
+ if (!try_flatten->ToObject(&flat)) {
+ return try_flatten;
+ }
+ str = String::cast(flat);
+ ASSERT(str->IsFlat());
+ }
+ if (str->IsTwoByteRepresentation()) {
+ return QuoteJsonString<uc16, SeqTwoByteString, true>(str->ToUC16Vector());
+ } else {
+ return QuoteJsonString<char, SeqAsciiString, true>(str->ToAsciiVector());
+ }
+}
+
static MaybeObject* Runtime_StringParseInt(Arguments args) {
NoHandleAllocation ha;
@@ -6708,12 +6736,24 @@ static MaybeObject* Runtime_LazyRecompile(Arguments args) {
// code from the full compiler.
if (!function->shared()->code()->optimizable() ||
Debug::has_break_points()) {
+ if (FLAG_trace_opt) {
+ PrintF("[failed to optimize ");
+ function->PrintName();
+ PrintF(": is code optimizable: %s, is debugger enabled: %s]\n",
+ function->shared()->code()->optimizable() ? "T" : "F",
+ Debug::has_break_points() ? "T" : "F");
+ }
function->ReplaceCode(function->shared()->code());
return function->code();
}
if (CompileOptimized(function, AstNode::kNoNumber)) {
return function->code();
}
+ if (FLAG_trace_opt) {
+ PrintF("[failed to optimize ");
+ function->PrintName();
+ PrintF(": optimized compilation failed]\n");
+ }
function->ReplaceCode(function->shared()->code());
return Failure::Exception();
}
@@ -6742,7 +6782,7 @@ static MaybeObject* Runtime_NotifyDeoptimized(Arguments args) {
Handle<JSFunction> function(JSFunction::cast(frame->function()));
Handle<Object> arguments;
for (int i = frame->ComputeExpressionsCount() - 1; i >= 0; --i) {
- if (frame->GetExpression(i) == Heap::the_hole_value()) {
+ if (frame->GetExpression(i) == Heap::arguments_marker()) {
if (arguments.is_null()) {
// FunctionGetArguments can't throw an exception, so cast away the
// doubt with an assert.
@@ -10328,15 +10368,16 @@ static MaybeObject* Runtime_LiveEditCheckAndDropActivations(Arguments args) {
return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
}
-// Compares 2 strings line-by-line and returns diff in form of JSArray of
-// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
-static MaybeObject* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
+// Compares 2 strings line-by-line, then token-wise and returns diff in form
+// of JSArray of triplets (pos1, pos1_end, pos2_end) describing list
+// of diff chunks.
+static MaybeObject* Runtime_LiveEditCompareStrings(Arguments args) {
ASSERT(args.length() == 2);
HandleScope scope;
CONVERT_ARG_CHECKED(String, s1, 0);
CONVERT_ARG_CHECKED(String, s2, 1);
- return *LiveEdit::CompareStringsLinewise(s1, s2);
+ return *LiveEdit::CompareStrings(s1, s2);
}
@@ -10406,10 +10447,36 @@ static MaybeObject* Runtime_ExecuteInDebugContext(Arguments args) {
}
+// Sets a v8 flag.
+static MaybeObject* Runtime_SetFlags(Arguments args) {
+ CONVERT_CHECKED(String, arg, args[0]);
+ SmartPointer<char> flags =
+ arg->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ FlagList::SetFlagsFromString(*flags, StrLength(*flags));
+ return Heap::undefined_value();
+}
+
+
+// Performs a GC.
+// Presently, it only does a full GC.
+static MaybeObject* Runtime_CollectGarbage(Arguments args) {
+ Heap::CollectAllGarbage(true);
+ return Heap::undefined_value();
+}
+
+
+// Gets the current heap usage.
+static MaybeObject* Runtime_GetHeapUsage(Arguments args) {
+ int usage = static_cast<int>(Heap::SizeOfObjects());
+ if (!Smi::IsValid(usage)) {
+ return *Factory::NewNumberFromInt(usage);
+ }
+ return Smi::FromInt(usage);
+}
#endif // ENABLE_DEBUGGER_SUPPORT
-#ifdef ENABLE_LOGGING_AND_PROFILING
+#ifdef ENABLE_LOGGING_AND_PROFILING
static MaybeObject* Runtime_ProfilerResume(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
diff --git a/deps/v8/src/runtime.h b/deps/v8/src/runtime.h
index 5ecae7e940..dbd8d644b7 100644
--- a/deps/v8/src/runtime.h
+++ b/deps/v8/src/runtime.h
@@ -106,6 +106,7 @@ namespace internal {
F(URIEscape, 1, 1) \
F(URIUnescape, 1, 1) \
F(QuoteJSONString, 1, 1) \
+ F(QuoteJSONStringComma, 1, 1) \
\
F(NumberToString, 1, 1) \
F(NumberToStringSkipCache, 1, 1) \
@@ -361,9 +362,14 @@ namespace internal {
F(LiveEditReplaceRefToNestedFunction, 3, 1) \
F(LiveEditPatchFunctionPositions, 2, 1) \
F(LiveEditCheckAndDropActivations, 2, 1) \
- F(LiveEditCompareStringsLinewise, 2, 1) \
+ F(LiveEditCompareStrings, 2, 1) \
F(GetFunctionCodePositionFromSource, 2, 1) \
- F(ExecuteInDebugContext, 2, 1)
+ F(ExecuteInDebugContext, 2, 1) \
+ \
+ F(SetFlags, 1, 1) \
+ F(CollectGarbage, 1, 1) \
+ F(GetHeapUsage, 0, 1)
+
#else
#define RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F)
#endif
diff --git a/deps/v8/src/runtime.js b/deps/v8/src/runtime.js
index 28a38ca896..2cdbbdeebf 100644
--- a/deps/v8/src/runtime.js
+++ b/deps/v8/src/runtime.js
@@ -165,7 +165,7 @@ function ADD(x) {
if (IS_STRING(a)) {
return %_StringAdd(a, %ToString(b));
} else if (IS_STRING(b)) {
- return %_StringAdd(%ToString(a), b);
+ return %_StringAdd(%NonStringToString(a), b);
} else {
return %NumberAdd(%ToNumber(a), %ToNumber(b));
}
@@ -205,32 +205,32 @@ function STRING_ADD_RIGHT(y) {
// ECMA-262, section 11.6.2, page 50.
function SUB(y) {
- var x = IS_NUMBER(this) ? this : %ToNumber(this);
- if (!IS_NUMBER(y)) y = %ToNumber(y);
+ var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
return %NumberSub(x, y);
}
// ECMA-262, section 11.5.1, page 48.
function MUL(y) {
- var x = IS_NUMBER(this) ? this : %ToNumber(this);
- if (!IS_NUMBER(y)) y = %ToNumber(y);
+ var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
return %NumberMul(x, y);
}
// ECMA-262, section 11.5.2, page 49.
function DIV(y) {
- var x = IS_NUMBER(this) ? this : %ToNumber(this);
- if (!IS_NUMBER(y)) y = %ToNumber(y);
+ var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
return %NumberDiv(x, y);
}
// ECMA-262, section 11.5.3, page 49.
function MOD(y) {
- var x = IS_NUMBER(this) ? this : %ToNumber(this);
- if (!IS_NUMBER(y)) y = %ToNumber(y);
+ var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
return %NumberMod(x, y);
}
@@ -243,8 +243,8 @@ function MOD(y) {
// ECMA-262, section 11.10, page 57.
function BIT_OR(y) {
- var x = IS_NUMBER(this) ? this : %ToNumber(this);
- if (!IS_NUMBER(y)) y = %ToNumber(y);
+ var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
return %NumberOr(x, y);
}
@@ -254,14 +254,14 @@ function BIT_AND(y) {
var x;
if (IS_NUMBER(this)) {
x = this;
- if (!IS_NUMBER(y)) y = %ToNumber(y);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
} else {
- x = %ToNumber(this);
+ x = %NonNumberToNumber(this);
// Make sure to convert the right operand to a number before
// bailing out in the fast case, but after converting the
// left operand. This ensures that valueOf methods on the right
// operand are always executed.
- if (!IS_NUMBER(y)) y = %ToNumber(y);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
// Optimize for the case where we end up AND'ing a value
// that doesn't convert to a number. This is common in
// certain benchmarks.
@@ -273,30 +273,30 @@ function BIT_AND(y) {
// ECMA-262, section 11.10, page 57.
function BIT_XOR(y) {
- var x = IS_NUMBER(this) ? this : %ToNumber(this);
- if (!IS_NUMBER(y)) y = %ToNumber(y);
+ var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
return %NumberXor(x, y);
}
// ECMA-262, section 11.4.7, page 47.
function UNARY_MINUS() {
- var x = IS_NUMBER(this) ? this : %ToNumber(this);
+ var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
return %NumberUnaryMinus(x);
}
// ECMA-262, section 11.4.8, page 48.
function BIT_NOT() {
- var x = IS_NUMBER(this) ? this : %ToNumber(this);
+ var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
return %NumberNot(x);
}
// ECMA-262, section 11.7.1, page 51.
function SHL(y) {
- var x = IS_NUMBER(this) ? this : %ToNumber(this);
- if (!IS_NUMBER(y)) y = %ToNumber(y);
+ var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
return %NumberShl(x, y);
}
@@ -306,14 +306,14 @@ function SAR(y) {
var x;
if (IS_NUMBER(this)) {
x = this;
- if (!IS_NUMBER(y)) y = %ToNumber(y);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
} else {
- x = %ToNumber(this);
+ x = %NonNumberToNumber(this);
// Make sure to convert the right operand to a number before
// bailing out in the fast case, but after converting the
// left operand. This ensures that valueOf methods on the right
// operand are always executed.
- if (!IS_NUMBER(y)) y = %ToNumber(y);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
// Optimize for the case where we end up shifting a value
// that doesn't convert to a number. This is common in
// certain benchmarks.
@@ -325,8 +325,8 @@ function SAR(y) {
// ECMA-262, section 11.7.3, page 52.
function SHR(y) {
- var x = IS_NUMBER(this) ? this : %ToNumber(this);
- if (!IS_NUMBER(y)) y = %ToNumber(y);
+ var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
return %NumberShr(x, y);
}
@@ -511,6 +511,16 @@ function ToNumber(x) {
return (IS_NULL(x)) ? 0 : ToNumber(%DefaultNumber(x));
}
+function NonNumberToNumber(x) {
+ if (IS_STRING(x)) {
+ return %_HasCachedArrayIndex(x) ? %_GetCachedArrayIndex(x)
+ : %StringToNumber(x);
+ }
+ if (IS_BOOLEAN(x)) return x ? 1 : 0;
+ if (IS_UNDEFINED(x)) return $NaN;
+ return (IS_NULL(x)) ? 0 : ToNumber(%DefaultNumber(x));
+}
+
// ECMA-262, section 9.8, page 35.
function ToString(x) {
@@ -568,12 +578,9 @@ function SameValue(x, y) {
if (IS_NUMBER(x)) {
if (NUMBER_IS_NAN(x) && NUMBER_IS_NAN(y)) return true;
// x is +0 and y is -0 or vice versa.
- if (x === 0 && y === 0 && (1 / x) != (1 / y)) {
- return false;
- }
- return x === y;
+ if (x === 0 && y === 0 && (1 / x) != (1 / y)) return false;
}
- return x === y
+ return x === y;
}
diff --git a/deps/v8/src/safepoint-table.cc b/deps/v8/src/safepoint-table.cc
index b9468a50bf..e79dcff09a 100644
--- a/deps/v8/src/safepoint-table.cc
+++ b/deps/v8/src/safepoint-table.cc
@@ -26,11 +26,34 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "safepoint-table.h"
+
#include "disasm.h"
+#include "macro-assembler.h"
namespace v8 {
namespace internal {
+
+bool SafepointEntry::HasRegisters() const {
+ ASSERT(is_valid());
+ ASSERT(IsAligned(kNumSafepointRegisters, kBitsPerByte));
+ const int num_reg_bytes = kNumSafepointRegisters >> kBitsPerByteLog2;
+ for (int i = 0; i < num_reg_bytes; i++) {
+ if (bits_[i] != SafepointTable::kNoRegisters) return true;
+ }
+ return false;
+}
+
+
+bool SafepointEntry::HasRegisterAt(int reg_index) const {
+ ASSERT(is_valid());
+ ASSERT(reg_index >= 0 && reg_index < kNumSafepointRegisters);
+ int byte_index = reg_index >> kBitsPerByteLog2;
+ int bit_index = reg_index & (kBitsPerByte - 1);
+ return (bits_[byte_index] & (1 << bit_index)) != 0;
+}
+
+
SafepointTable::SafepointTable(Code* code) {
ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION);
code_ = code;
@@ -41,45 +64,39 @@ SafepointTable::SafepointTable(Code* code) {
entries_ = pc_and_deoptimization_indexes_ +
(length_ * kPcAndDeoptimizationIndexSize);
ASSERT(entry_size_ > 0);
- ASSERT_EQ(DeoptimizationIndexField::max(), Safepoint::kNoDeoptimizationIndex);
+ ASSERT_EQ(SafepointEntry::DeoptimizationIndexField::max(),
+ Safepoint::kNoDeoptimizationIndex);
}
-bool SafepointTable::HasRegisters(uint8_t* entry) {
- ASSERT(IsAligned(kNumSafepointRegisters, kBitsPerByte));
- const int num_reg_bytes = kNumSafepointRegisters >> kBitsPerByteLog2;
- for (int i = 0; i < num_reg_bytes; i++) {
- if (entry[i] != kNoRegisters) return true;
+SafepointEntry SafepointTable::FindEntry(Address pc) const {
+ unsigned pc_offset = static_cast<unsigned>(pc - code_->instruction_start());
+ for (unsigned i = 0; i < length(); i++) {
+ // TODO(kasperl): Replace the linear search with binary search.
+ if (GetPcOffset(i) == pc_offset) return GetEntry(i);
}
- return false;
-}
-
-
-bool SafepointTable::HasRegisterAt(uint8_t* entry, int reg_index) {
- ASSERT(reg_index >= 0 && reg_index < kNumSafepointRegisters);
- int byte_index = reg_index >> kBitsPerByteLog2;
- int bit_index = reg_index & (kBitsPerByte - 1);
- return (entry[byte_index] & (1 << bit_index)) != 0;
+ return SafepointEntry();
}
void SafepointTable::PrintEntry(unsigned index) const {
disasm::NameConverter converter;
- uint8_t* entry = GetEntry(index);
+ SafepointEntry entry = GetEntry(index);
+ uint8_t* bits = entry.bits();
// Print the stack slot bits.
if (entry_size_ > 0) {
ASSERT(IsAligned(kNumSafepointRegisters, kBitsPerByte));
const int first = kNumSafepointRegisters >> kBitsPerByteLog2;
int last = entry_size_ - 1;
- for (int i = first; i < last; i++) PrintBits(entry[i], kBitsPerByte);
+ for (int i = first; i < last; i++) PrintBits(bits[i], kBitsPerByte);
int last_bits = code_->stack_slots() - ((last - first) * kBitsPerByte);
- PrintBits(entry[last], last_bits);
+ PrintBits(bits[last], last_bits);
// Print the registers (if any).
- if (!HasRegisters(entry)) return;
+ if (!entry.HasRegisters()) return;
for (int j = 0; j < kNumSafepointRegisters; j++) {
- if (HasRegisterAt(entry, j)) {
+ if (entry.HasRegisterAt(j)) {
PrintF(" | %s", converter.NameOfCPURegister(j));
}
}
@@ -95,6 +112,11 @@ void SafepointTable::PrintBits(uint8_t byte, int digits) {
}
+void Safepoint::DefinePointerRegister(Register reg) {
+ registers_->Add(reg.code());
+}
+
+
Safepoint SafepointTableBuilder::DefineSafepoint(Assembler* assembler,
int deoptimization_index) {
ASSERT(deoptimization_index != -1);
@@ -102,6 +124,8 @@ Safepoint SafepointTableBuilder::DefineSafepoint(Assembler* assembler,
pc_and_deoptimization_index.pc = assembler->pc_offset();
pc_and_deoptimization_index.deoptimization_index = deoptimization_index;
pc_and_deoptimization_index.pc_after_gap = assembler->pc_offset();
+ pc_and_deoptimization_index.arguments = 0;
+ pc_and_deoptimization_index.has_doubles = false;
deoptimization_info_.Add(pc_and_deoptimization_index);
indexes_.Add(new ZoneList<int>(8));
registers_.Add(NULL);
@@ -112,11 +136,13 @@ Safepoint SafepointTableBuilder::DefineSafepoint(Assembler* assembler,
Safepoint SafepointTableBuilder::DefineSafepointWithRegisters(
Assembler* assembler, int arguments, int deoptimization_index) {
ASSERT(deoptimization_index != -1);
- ASSERT(arguments == 0); // Only case that works for now.
+ ASSERT(arguments >= 0);
DeoptimizationInfo pc_and_deoptimization_index;
pc_and_deoptimization_index.pc = assembler->pc_offset();
pc_and_deoptimization_index.deoptimization_index = deoptimization_index;
pc_and_deoptimization_index.pc_after_gap = assembler->pc_offset();
+ pc_and_deoptimization_index.arguments = arguments;
+ pc_and_deoptimization_index.has_doubles = false;
deoptimization_info_.Add(pc_and_deoptimization_index);
indexes_.Add(new ZoneList<int>(8));
registers_.Add(new ZoneList<int>(4));
@@ -124,6 +150,22 @@ Safepoint SafepointTableBuilder::DefineSafepointWithRegisters(
}
+Safepoint SafepointTableBuilder::DefineSafepointWithRegistersAndDoubles(
+ Assembler* assembler, int arguments, int deoptimization_index) {
+ ASSERT(deoptimization_index != -1);
+ ASSERT(arguments >= 0);
+ DeoptimizationInfo pc_and_deoptimization_index;
+ pc_and_deoptimization_index.pc = assembler->pc_offset();
+ pc_and_deoptimization_index.deoptimization_index = deoptimization_index;
+ pc_and_deoptimization_index.pc_after_gap = assembler->pc_offset();
+ pc_and_deoptimization_index.arguments = arguments;
+ pc_and_deoptimization_index.has_doubles = true;
+ deoptimization_info_.Add(pc_and_deoptimization_index);
+ indexes_.Add(new ZoneList<int>(8));
+ registers_.Add(new ZoneList<int>(4));
+ return Safepoint(indexes_.last(), registers_.last());
+}
+
unsigned SafepointTableBuilder::GetCodeOffset() const {
ASSERT(emitted_);
return offset_;
@@ -152,7 +194,7 @@ void SafepointTableBuilder::Emit(Assembler* assembler, int bits_per_entry) {
// pc after gap information.
for (int i = 0; i < length; i++) {
assembler->dd(deoptimization_info_[i].pc);
- assembler->dd(EncodeDeoptimizationIndexAndGap(deoptimization_info_[i]));
+ assembler->dd(EncodeExceptPC(deoptimization_info_[i]));
}
// Emit table of bitmaps.
@@ -197,12 +239,13 @@ void SafepointTableBuilder::Emit(Assembler* assembler, int bits_per_entry) {
}
-uint32_t SafepointTableBuilder::EncodeDeoptimizationIndexAndGap(
- DeoptimizationInfo info) {
+uint32_t SafepointTableBuilder::EncodeExceptPC(const DeoptimizationInfo& info) {
unsigned index = info.deoptimization_index;
unsigned gap_size = info.pc_after_gap - info.pc;
- uint32_t encoding = SafepointTable::DeoptimizationIndexField::encode(index);
- encoding |= SafepointTable::GapCodeSizeField::encode(gap_size);
+ uint32_t encoding = SafepointEntry::DeoptimizationIndexField::encode(index);
+ encoding |= SafepointEntry::GapCodeSizeField::encode(gap_size);
+ encoding |= SafepointEntry::ArgumentsField::encode(info.arguments);
+ encoding |= SafepointEntry::SaveDoublesField::encode(info.has_doubles);
return encoding;
}
diff --git a/deps/v8/src/safepoint-table.h b/deps/v8/src/safepoint-table.h
index 010ac5759b..d703051423 100644
--- a/deps/v8/src/safepoint-table.h
+++ b/deps/v8/src/safepoint-table.h
@@ -30,13 +30,89 @@
#include "v8.h"
-#include "macro-assembler.h"
+#include "heap.h"
#include "zone.h"
#include "zone-inl.h"
namespace v8 {
namespace internal {
+struct Register;
+
+class SafepointEntry BASE_EMBEDDED {
+ public:
+ SafepointEntry() : info_(0), bits_(NULL) {}
+
+ SafepointEntry(unsigned info, uint8_t* bits) : info_(info), bits_(bits) {
+ ASSERT(is_valid());
+ }
+
+ bool is_valid() const { return bits_ != NULL; }
+
+ bool Equals(const SafepointEntry& other) const {
+ return info_ == other.info_ && bits_ == other.bits_;
+ }
+
+ void Reset() {
+ info_ = 0;
+ bits_ = NULL;
+ }
+
+ int deoptimization_index() const {
+ ASSERT(is_valid());
+ return DeoptimizationIndexField::decode(info_);
+ }
+
+ int gap_code_size() const {
+ ASSERT(is_valid());
+ return GapCodeSizeField::decode(info_);
+ }
+
+ int argument_count() const {
+ ASSERT(is_valid());
+ return ArgumentsField::decode(info_);
+ }
+
+ bool has_doubles() const {
+ ASSERT(is_valid());
+ return SaveDoublesField::decode(info_);
+ }
+
+ uint8_t* bits() {
+ ASSERT(is_valid());
+ return bits_;
+ }
+
+ bool HasRegisters() const;
+ bool HasRegisterAt(int reg_index) const;
+
+ // Reserve 13 bits for the gap code size. On ARM a constant pool can be
+ // emitted when generating the gap code. The size of the const pool is less
+ // than what can be represented in 12 bits, so 13 bits gives room for having
+ // instructions before potentially emitting a constant pool.
+ static const int kGapCodeSizeBits = 13;
+ static const int kArgumentsFieldBits = 3;
+ static const int kSaveDoublesFieldBits = 1;
+ static const int kDeoptIndexBits =
+ 32 - kGapCodeSizeBits - kArgumentsFieldBits - kSaveDoublesFieldBits;
+ class GapCodeSizeField: public BitField<unsigned, 0, kGapCodeSizeBits> {};
+ class DeoptimizationIndexField: public BitField<int,
+ kGapCodeSizeBits,
+ kDeoptIndexBits> {}; // NOLINT
+ class ArgumentsField: public BitField<unsigned,
+ kGapCodeSizeBits + kDeoptIndexBits,
+ kArgumentsFieldBits> {}; // NOLINT
+ class SaveDoublesField: public BitField<bool,
+ kGapCodeSizeBits + kDeoptIndexBits +
+ kArgumentsFieldBits,
+ kSaveDoublesFieldBits> { }; // NOLINT
+
+ private:
+ unsigned info_;
+ uint8_t* bits_;
+};
+
+
class SafepointTable BASE_EMBEDDED {
public:
explicit SafepointTable(Code* code);
@@ -52,28 +128,15 @@ class SafepointTable BASE_EMBEDDED {
return Memory::uint32_at(GetPcOffsetLocation(index));
}
- int GetDeoptimizationIndex(unsigned index) const {
- ASSERT(index < length_);
- unsigned value = Memory::uint32_at(GetDeoptimizationLocation(index));
- return DeoptimizationIndexField::decode(value);
- }
-
- unsigned GetGapCodeSize(unsigned index) const {
+ SafepointEntry GetEntry(unsigned index) const {
ASSERT(index < length_);
- unsigned value = Memory::uint32_at(GetDeoptimizationLocation(index));
- return GapCodeSizeField::decode(value);
+ unsigned info = Memory::uint32_at(GetInfoLocation(index));
+ uint8_t* bits = &Memory::uint8_at(entries_ + (index * entry_size_));
+ return SafepointEntry(info, bits);
}
- uint8_t* GetEntry(unsigned index) const {
- ASSERT(index < length_);
- return &Memory::uint8_at(entries_ + (index * entry_size_));
- }
-
- class GapCodeSizeField: public BitField<unsigned, 0, 8> {};
- class DeoptimizationIndexField: public BitField<int, 8, 24> {};
-
- static bool HasRegisters(uint8_t* entry);
- static bool HasRegisterAt(uint8_t* entry, int reg_index);
+ // Returns the entry for the given pc.
+ SafepointEntry FindEntry(Address pc) const;
void PrintEntry(unsigned index) const;
@@ -94,7 +157,7 @@ class SafepointTable BASE_EMBEDDED {
(index * kPcAndDeoptimizationIndexSize);
}
- Address GetDeoptimizationLocation(unsigned index) const {
+ Address GetInfoLocation(unsigned index) const {
return GetPcOffsetLocation(index) + kPcSize;
}
@@ -109,15 +172,19 @@ class SafepointTable BASE_EMBEDDED {
Address entries_;
friend class SafepointTableBuilder;
+ friend class SafepointEntry;
+
+ DISALLOW_COPY_AND_ASSIGN(SafepointTable);
};
class Safepoint BASE_EMBEDDED {
public:
- static const int kNoDeoptimizationIndex = 0x00ffffff;
+ static const int kNoDeoptimizationIndex =
+ (1 << (SafepointEntry::kDeoptIndexBits)) - 1;
void DefinePointerSlot(int index) { indexes_->Add(index); }
- void DefinePointerRegister(Register reg) { registers_->Add(reg.code()); }
+ void DefinePointerRegister(Register reg);
private:
Safepoint(ZoneList<int>* indexes, ZoneList<int>* registers) :
@@ -153,6 +220,16 @@ class SafepointTableBuilder BASE_EMBEDDED {
int arguments,
int deoptimization_index = Safepoint::kNoDeoptimizationIndex);
+ // Define a new safepoint with all double registers and the normal
+ // registers on the stack for the current position in the body and
+ // take the number of arguments on top of the registers into account.
+ // TODO(1043) Rewrite the three SafepointTableBuilder::DefineSafepoint
+ // methods to one method that uses template arguments.
+ Safepoint DefineSafepointWithRegistersAndDoubles(
+ Assembler* assembler,
+ int arguments,
+ int deoptimization_index = Safepoint::kNoDeoptimizationIndex);
+
// Update the last safepoint with the size of the code generated for the gap
// following it.
void SetPcAfterGap(int pc) {
@@ -170,9 +247,11 @@ class SafepointTableBuilder BASE_EMBEDDED {
unsigned pc;
unsigned deoptimization_index;
unsigned pc_after_gap;
+ unsigned arguments;
+ bool has_doubles;
};
- uint32_t EncodeDeoptimizationIndexAndGap(DeoptimizationInfo info);
+ uint32_t EncodeExceptPC(const DeoptimizationInfo& info);
ZoneList<DeoptimizationInfo> deoptimization_info_;
ZoneList<ZoneList<int>*> indexes_;
diff --git a/deps/v8/src/scanner-base.cc b/deps/v8/src/scanner-base.cc
index b26fee01a7..997fb312fc 100644
--- a/deps/v8/src/scanner-base.cc
+++ b/deps/v8/src/scanner-base.cc
@@ -35,28 +35,6 @@ namespace v8 {
namespace internal {
// ----------------------------------------------------------------------------
-// LiteralCollector
-
-LiteralCollector::LiteralCollector()
- : buffer_(kInitialCapacity), recording_(false) { }
-
-
-LiteralCollector::~LiteralCollector() {}
-
-
-void LiteralCollector::AddCharSlow(uc32 c) {
- ASSERT(static_cast<unsigned>(c) > unibrow::Utf8::kMaxOneByteChar);
- int length = unibrow::Utf8::Length(c);
- Vector<char> block = buffer_.AddBlock(length, '\0');
-#ifdef DEBUG
- int written_length = unibrow::Utf8::Encode(block.start(), c);
- CHECK_EQ(length, written_length);
-#else
- unibrow::Utf8::Encode(block.start(), c);
-#endif
-}
-
-// ----------------------------------------------------------------------------
// Character predicates
unibrow::Predicate<IdentifierStart, 128> ScannerConstants::kIsIdentifierStart;
@@ -256,7 +234,7 @@ Token::Value JavaScriptScanner::ScanHtmlComment() {
void JavaScriptScanner::Scan() {
- next_.literal_chars = Vector<const char>();
+ next_.literal_chars = NULL;
Token::Value token;
do {
// Remember the position of the next token
@@ -561,7 +539,7 @@ Token::Value JavaScriptScanner::ScanString() {
uc32 quote = c0_;
Advance(); // consume quote
- LiteralScope literal(this, kLiteralString);
+ LiteralScope literal(this);
while (c0_ != quote && c0_ >= 0
&& !ScannerConstants::kIsLineTerminator.get(c0_)) {
uc32 c = c0_;
@@ -592,7 +570,7 @@ Token::Value JavaScriptScanner::ScanNumber(bool seen_period) {
enum { DECIMAL, HEX, OCTAL } kind = DECIMAL;
- LiteralScope literal(this, kLiteralNumber);
+ LiteralScope literal(this);
if (seen_period) {
// we have already seen a decimal point of the float
AddLiteralChar('.');
@@ -681,7 +659,7 @@ uc32 JavaScriptScanner::ScanIdentifierUnicodeEscape() {
Token::Value JavaScriptScanner::ScanIdentifierOrKeyword() {
ASSERT(ScannerConstants::kIsIdentifierStart.get(c0_));
- LiteralScope literal(this, kLiteralIdentifier);
+ LiteralScope literal(this);
KeywordMatcher keyword_match;
// Scan identifier start character.
if (c0_ == '\\') {
@@ -747,17 +725,24 @@ bool JavaScriptScanner::ScanRegExpPattern(bool seen_equal) {
// Scan regular expression body: According to ECMA-262, 3rd, 7.8.5,
// the scanner should pass uninterpreted bodies to the RegExp
// constructor.
- LiteralScope literal(this, kLiteralRegExp);
+ LiteralScope literal(this);
if (seen_equal)
AddLiteralChar('=');
while (c0_ != '/' || in_character_class) {
if (ScannerConstants::kIsLineTerminator.get(c0_) || c0_ < 0) return false;
- if (c0_ == '\\') { // escaped character
+ if (c0_ == '\\') { // Escape sequence.
AddLiteralCharAdvance();
if (ScannerConstants::kIsLineTerminator.get(c0_) || c0_ < 0) return false;
AddLiteralCharAdvance();
- } else { // unescaped character
+ // If the escape allows more characters, i.e., \x??, \u????, or \c?,
+ // only "safe" characters are allowed (letters, digits, underscore),
+ // otherwise the escape isn't valid and the invalid character has
+ // its normal meaning. I.e., we can just continue scanning without
+ // worrying whether the following characters are part of the escape
+ // or not, since any '/', '\\' or '[' is guaranteed to not be part
+ // of the escape sequence.
+ } else { // Unescaped character.
if (c0_ == '[') in_character_class = true;
if (c0_ == ']') in_character_class = false;
AddLiteralCharAdvance();
@@ -773,7 +758,7 @@ bool JavaScriptScanner::ScanRegExpPattern(bool seen_equal) {
bool JavaScriptScanner::ScanRegExpFlags() {
// Scan regular expression flags.
- LiteralScope literal(this, kLiteralRegExpFlags);
+ LiteralScope literal(this);
while (ScannerConstants::kIsIdentifierPart.get(c0_)) {
if (c0_ == '\\') {
uc32 c = ScanIdentifierUnicodeEscape();
diff --git a/deps/v8/src/scanner-base.h b/deps/v8/src/scanner-base.h
index c50b8f3ef6..1024ad1858 100644
--- a/deps/v8/src/scanner-base.h
+++ b/deps/v8/src/scanner-base.h
@@ -64,10 +64,10 @@ class UC16CharacterStream {
// Returns and advances past the next UC16 character in the input
// stream. If there are no more characters, it returns a negative
// value.
- inline int32_t Advance() {
+ inline uc32 Advance() {
if (buffer_cursor_ < buffer_end_ || ReadBlock()) {
pos_++;
- return *(buffer_cursor_++);
+ return static_cast<uc32>(*(buffer_cursor_++));
}
// Note: currently the following increment is necessary to avoid a
// parser problem! The scanner treats the final kEndOfInput as
@@ -97,13 +97,14 @@ class UC16CharacterStream {
return SlowSeekForward(character_count);
}
- // Pushes back the most recently read UC16 character, i.e.,
- // the value returned by the most recent call to Advance.
+ // Pushes back the most recently read UC16 character (or negative
+ // value if at end of input), i.e., the value returned by the most recent
+ // call to Advance.
// Must not be used right after calling SeekForward.
- virtual void PushBack(uc16 character) = 0;
+ virtual void PushBack(int32_t character) = 0;
protected:
- static const int32_t kEndOfInput = -1;
+ static const uc32 kEndOfInput = -1;
// Ensures that the buffer_cursor_ points to the character at
// position pos_ of the input, if possible. If the position
@@ -141,61 +142,105 @@ class ScannerConstants : AllStatic {
};
// ----------------------------------------------------------------------------
-// LiteralCollector - Collector of chars of literals.
+// LiteralBuffer - Collector of chars of literals.
-class LiteralCollector {
+class LiteralBuffer {
public:
- LiteralCollector();
- ~LiteralCollector();
-
- inline void AddChar(uc32 c) {
- if (recording_) {
- if (static_cast<unsigned>(c) <= unibrow::Utf8::kMaxOneByteChar) {
- buffer_.Add(static_cast<char>(c));
- } else {
- AddCharSlow(c);
+ LiteralBuffer() : is_ascii_(true), position_(0), backing_store_() { }
+
+ ~LiteralBuffer() {
+ if (backing_store_.length() > 0) {
+ backing_store_.Dispose();
+ }
+ }
+
+ inline void AddChar(uc16 character) {
+ if (position_ >= backing_store_.length()) ExpandBuffer();
+ if (is_ascii_) {
+ if (character < kMaxAsciiCharCodeU) {
+ backing_store_[position_] = static_cast<byte>(character);
+ position_ += kASCIISize;
+ return;
}
+ ConvertToUC16();
}
+ *reinterpret_cast<uc16*>(&backing_store_[position_]) = character;
+ position_ += kUC16Size;
}
- void StartLiteral() {
- buffer_.StartSequence();
- recording_ = true;
+ bool is_ascii() { return is_ascii_; }
+
+ Vector<const uc16> uc16_literal() {
+ ASSERT(!is_ascii_);
+ ASSERT((position_ & 0x1) == 0);
+ return Vector<const uc16>(
+ reinterpret_cast<const uc16*>(backing_store_.start()),
+ position_ >> 1);
}
- Vector<const char> EndLiteral() {
- if (recording_) {
- recording_ = false;
- buffer_.Add(kEndMarker);
- Vector<char> sequence = buffer_.EndSequence();
- return Vector<const char>(sequence.start(), sequence.length());
- }
- return Vector<const char>();
+ Vector<const char> ascii_literal() {
+ ASSERT(is_ascii_);
+ return Vector<const char>(
+ reinterpret_cast<const char*>(backing_store_.start()),
+ position_);
}
- void DropLiteral() {
- if (recording_) {
- recording_ = false;
- buffer_.DropSequence();
- }
+ int length() {
+ return is_ascii_ ? position_ : (position_ >> 1);
}
void Reset() {
- buffer_.Reset();
+ position_ = 0;
+ is_ascii_ = true;
}
-
- // The end marker added after a parsed literal.
- // Using zero allows the usage of strlen and similar functions on
- // identifiers and numbers (but not strings, since they may contain zero
- // bytes).
- static const char kEndMarker = '\x00';
private:
- static const int kInitialCapacity = 256;
- SequenceCollector<char, 4> buffer_;
- bool recording_;
- void AddCharSlow(uc32 c);
+ static const int kInitialCapacity = 16;
+ static const int kGrowthFactory = 4;
+ static const int kMinConversionSlack = 256;
+ static const int kMaxGrowth = 1 * MB;
+ inline int NewCapacity(int min_capacity) {
+ int capacity = Max(min_capacity, backing_store_.length());
+ int new_capacity = Min(capacity * kGrowthFactory, capacity + kMaxGrowth);
+ return new_capacity;
+ }
+
+ void ExpandBuffer() {
+ Vector<byte> new_store = Vector<byte>::New(NewCapacity(kInitialCapacity));
+ memcpy(new_store.start(), backing_store_.start(), position_);
+ backing_store_.Dispose();
+ backing_store_ = new_store;
+ }
+
+ void ConvertToUC16() {
+ ASSERT(is_ascii_);
+ Vector<byte> new_store;
+ int new_content_size = position_ * kUC16Size;
+ if (new_content_size >= backing_store_.length()) {
+ // Ensure room for all currently read characters as UC16 as well
+ // as the character about to be stored.
+ new_store = Vector<byte>::New(NewCapacity(new_content_size));
+ } else {
+ new_store = backing_store_;
+ }
+ char* src = reinterpret_cast<char*>(backing_store_.start());
+ uc16* dst = reinterpret_cast<uc16*>(new_store.start());
+ for (int i = position_ - 1; i >= 0; i--) {
+ dst[i] = src[i];
+ }
+ if (new_store.start() != backing_store_.start()) {
+ backing_store_.Dispose();
+ backing_store_ = new_store;
+ }
+ position_ = new_content_size;
+ is_ascii_ = false;
+ }
+
+ bool is_ascii_;
+ int position_;
+ Vector<byte> backing_store_;
};
+
// ----------------------------------------------------------------------------
// Scanner base-class.
@@ -241,35 +286,40 @@ class Scanner {
// collected for identifiers, strings, and numbers.
// These functions only give the correct result if the literal
// was scanned between calls to StartLiteral() and TerminateLiteral().
- const char* literal_string() const {
- return current_.literal_chars.start();
+ bool is_literal_ascii() {
+ ASSERT_NOT_NULL(current_.literal_chars);
+ return current_.literal_chars->is_ascii();
}
-
- int literal_length() const {
- // Excluding terminal '\x00' added by TerminateLiteral().
- return current_.literal_chars.length() - 1;
+ Vector<const char> literal_ascii_string() {
+ ASSERT_NOT_NULL(current_.literal_chars);
+ return current_.literal_chars->ascii_literal();
}
-
- Vector<const char> literal() const {
- return Vector<const char>(literal_string(), literal_length());
+ Vector<const uc16> literal_uc16_string() {
+ ASSERT_NOT_NULL(current_.literal_chars);
+ return current_.literal_chars->uc16_literal();
+ }
+ int literal_length() const {
+ ASSERT_NOT_NULL(current_.literal_chars);
+ return current_.literal_chars->length();
}
// Returns the literal string for the next token (the token that
// would be returned if Next() were called).
- const char* next_literal_string() const {
- return next_.literal_chars.start();
+ bool is_next_literal_ascii() {
+ ASSERT_NOT_NULL(next_.literal_chars);
+ return next_.literal_chars->is_ascii();
}
-
-
- // Returns the length of the next token (that would be returned if
- // Next() were called).
- int next_literal_length() const {
- // Excluding terminal '\x00' added by TerminateLiteral().
- return next_.literal_chars.length() - 1;
+ Vector<const char> next_literal_ascii_string() {
+ ASSERT_NOT_NULL(next_.literal_chars);
+ return next_.literal_chars->ascii_literal();
}
-
- Vector<const char> next_literal() const {
- return Vector<const char>(next_literal_string(), next_literal_length());
+ Vector<const uc16> next_literal_uc16_string() {
+ ASSERT_NOT_NULL(next_.literal_chars);
+ return next_.literal_chars->uc16_literal();
+ }
+ int next_literal_length() const {
+ ASSERT_NOT_NULL(next_.literal_chars);
+ return next_.literal_chars->length();
}
static const int kCharacterLookaheadBufferSize = 1;
@@ -279,7 +329,7 @@ class Scanner {
struct TokenDesc {
Token::Value token;
Location location;
- Vector<const char> literal_chars;
+ LiteralBuffer* literal_chars;
};
// Call this after setting source_ to the input.
@@ -288,29 +338,31 @@ class Scanner {
ASSERT(kCharacterLookaheadBufferSize == 1);
Advance();
// Initialize current_ to not refer to a literal.
- current_.literal_chars = Vector<const char>();
- // Reset literal buffer.
- literal_buffer_.Reset();
+ current_.literal_chars = NULL;
}
// Literal buffer support
inline void StartLiteral() {
- literal_buffer_.StartLiteral();
+ LiteralBuffer* free_buffer = (current_.literal_chars == &literal_buffer1_) ?
+ &literal_buffer2_ : &literal_buffer1_;
+ free_buffer->Reset();
+ next_.literal_chars = free_buffer;
}
inline void AddLiteralChar(uc32 c) {
- literal_buffer_.AddChar(c);
+ ASSERT_NOT_NULL(next_.literal_chars);
+ next_.literal_chars->AddChar(c);
}
// Complete scanning of a literal.
inline void TerminateLiteral() {
- next_.literal_chars = literal_buffer_.EndLiteral();
+ // Does nothing in the current implementation.
}
// Stops scanning of a literal and drop the collected characters,
// e.g., due to an encountered error.
inline void DropLiteral() {
- literal_buffer_.DropLiteral();
+ next_.literal_chars = NULL;
}
inline void AddLiteralCharAdvance() {
@@ -348,15 +400,16 @@ class Scanner {
return source_->pos() - kCharacterLookaheadBufferSize;
}
+ // Buffers collecting literal strings, numbers, etc.
+ LiteralBuffer literal_buffer1_;
+ LiteralBuffer literal_buffer2_;
+
TokenDesc current_; // desc for current token (as returned by Next())
TokenDesc next_; // desc for next token (one token look-ahead)
// Input stream. Must be initialized to an UC16CharacterStream.
UC16CharacterStream* source_;
- // Buffer to hold literal values (identifiers, strings, numbers)
- // using '\x00'-terminated UTF-8 encoding. Handles allocation internally.
- LiteralCollector literal_buffer_;
// One Unicode character look-ahead; c0_ < 0 at the end of the input.
uc32 c0_;
@@ -367,28 +420,14 @@ class Scanner {
class JavaScriptScanner : public Scanner {
public:
-
- // Bit vector representing set of types of literals.
- enum LiteralType {
- kNoLiterals = 0,
- kLiteralNumber = 1,
- kLiteralIdentifier = 2,
- kLiteralString = 4,
- kLiteralRegExp = 8,
- kLiteralRegExpFlags = 16,
- kAllLiterals = 31
- };
-
// A LiteralScope that disables recording of some types of JavaScript
// literals. If the scanner is configured to not record the specific
// type of literal, the scope will not call StartLiteral.
class LiteralScope {
public:
- LiteralScope(JavaScriptScanner* self, LiteralType type)
+ explicit LiteralScope(JavaScriptScanner* self)
: scanner_(self), complete_(false) {
- if (scanner_->RecordsLiteral(type)) {
- scanner_->StartLiteral();
- }
+ scanner_->StartLiteral();
}
~LiteralScope() {
if (!complete_) scanner_->DropLiteral();
@@ -430,11 +469,6 @@ class JavaScriptScanner : public Scanner {
// tokens, which is what it is used for.
void SeekForward(int pos);
- // Whether this scanner records the given literal type or not.
- bool RecordsLiteral(LiteralType type) {
- return (literal_flags_ & type) != 0;
- }
-
protected:
bool SkipWhiteSpace();
Token::Value SkipSingleLineComment();
@@ -458,7 +492,6 @@ class JavaScriptScanner : public Scanner {
// If the escape sequence cannot be decoded the result is kBadChar.
uc32 ScanIdentifierUnicodeEscape();
- int literal_flags_;
bool has_line_terminator_before_next_;
};
diff --git a/deps/v8/src/scanner.cc b/deps/v8/src/scanner.cc
index 47e9895cf4..b66d10b988 100755
--- a/deps/v8/src/scanner.cc
+++ b/deps/v8/src/scanner.cc
@@ -48,14 +48,18 @@ BufferedUC16CharacterStream::BufferedUC16CharacterStream()
BufferedUC16CharacterStream::~BufferedUC16CharacterStream() { }
-void BufferedUC16CharacterStream::PushBack(uc16 character) {
+void BufferedUC16CharacterStream::PushBack(uc32 character) {
+ if (character == kEndOfInput) {
+ pos_--;
+ return;
+ }
if (pushback_limit_ == NULL && buffer_cursor_ > buffer_) {
// buffer_ is writable, buffer_cursor_ is const pointer.
- buffer_[--buffer_cursor_ - buffer_] = character;
+ buffer_[--buffer_cursor_ - buffer_] = static_cast<uc16>(character);
pos_--;
return;
}
- SlowPushBack(character);
+ SlowPushBack(static_cast<uc16>(character));
}
@@ -324,10 +328,8 @@ void Scanner::LiteralScope::Complete() {
V8JavaScriptScanner::V8JavaScriptScanner() : JavaScriptScanner() { }
-void V8JavaScriptScanner::Initialize(UC16CharacterStream* source,
- int literal_flags) {
+void V8JavaScriptScanner::Initialize(UC16CharacterStream* source) {
source_ = source;
- literal_flags_ = literal_flags | kLiteralIdentifier;
// Need to capture identifiers in order to recognize "get" and "set"
// in object literals.
Init();
@@ -377,7 +379,7 @@ bool JsonScanner::SkipJsonWhiteSpace() {
void JsonScanner::ScanJson() {
- next_.literal_chars = Vector<const char>();
+ next_.literal_chars = NULL;
Token::Value token;
do {
// Remember the position of the next token
@@ -459,7 +461,7 @@ Token::Value JsonScanner::ScanJsonString() {
ASSERT_EQ('"', c0_);
Advance();
LiteralScope literal(this);
- while (c0_ != '"' && c0_ > 0) {
+ while (c0_ != '"') {
// Check for control character (0x00-0x1f) or unterminated string (<0).
if (c0_ < 0x20) return Token::ILLEGAL;
if (c0_ != '\\') {
@@ -506,9 +508,6 @@ Token::Value JsonScanner::ScanJsonString() {
Advance();
}
}
- if (c0_ != '"') {
- return Token::ILLEGAL;
- }
literal.Complete();
Advance();
return Token::STRING;
diff --git a/deps/v8/src/scanner.h b/deps/v8/src/scanner.h
index 572778f8ac..d7621825a9 100644
--- a/deps/v8/src/scanner.h
+++ b/deps/v8/src/scanner.h
@@ -43,7 +43,7 @@ class BufferedUC16CharacterStream: public UC16CharacterStream {
BufferedUC16CharacterStream();
virtual ~BufferedUC16CharacterStream();
- virtual void PushBack(uc16 character);
+ virtual void PushBack(uc32 character);
protected:
static const unsigned kBufferSize = 512;
@@ -107,11 +107,12 @@ class ExternalTwoByteStringUC16CharacterStream: public UC16CharacterStream {
int end_position);
virtual ~ExternalTwoByteStringUC16CharacterStream();
- virtual void PushBack(uc16 character) {
+ virtual void PushBack(uc32 character) {
ASSERT(buffer_cursor_ > raw_data_);
buffer_cursor_--;
pos_--;
}
+
protected:
virtual unsigned SlowSeekForward(unsigned delta) {
// Fast case always handles seeking.
@@ -134,8 +135,7 @@ class ExternalTwoByteStringUC16CharacterStream: public UC16CharacterStream {
class V8JavaScriptScanner : public JavaScriptScanner {
public:
V8JavaScriptScanner();
- void Initialize(UC16CharacterStream* source,
- int literal_flags = kAllLiterals);
+ void Initialize(UC16CharacterStream* source);
};
diff --git a/deps/v8/src/scopes.cc b/deps/v8/src/scopes.cc
index 3565e11b58..58a10ee346 100644
--- a/deps/v8/src/scopes.cc
+++ b/deps/v8/src/scopes.cc
@@ -112,68 +112,74 @@ Variable* VariableMap::Lookup(Handle<String> name) {
// Dummy constructor
Scope::Scope(Type type)
- : outer_scope_(NULL),
- inner_scopes_(0),
- type_(type),
- scope_name_(Factory::empty_symbol()),
+ : inner_scopes_(0),
variables_(false),
temps_(0),
params_(0),
- dynamics_(NULL),
unresolved_(0),
- decls_(0),
- receiver_(NULL),
- function_(NULL),
- arguments_(NULL),
- arguments_shadow_(NULL),
- illegal_redecl_(NULL),
- scope_inside_with_(false),
- scope_contains_with_(false),
- scope_calls_eval_(false),
- outer_scope_calls_eval_(false),
- inner_scope_calls_eval_(false),
- outer_scope_is_eval_scope_(false),
- force_eager_compilation_(false),
- num_stack_slots_(0),
- num_heap_slots_(0) {
+ decls_(0) {
+ SetDefaults(type, NULL, NULL);
+ ASSERT(!resolved());
}
Scope::Scope(Scope* outer_scope, Type type)
- : outer_scope_(outer_scope),
- inner_scopes_(4),
- type_(type),
- scope_name_(Factory::empty_symbol()),
+ : inner_scopes_(4),
+ variables_(),
temps_(4),
params_(4),
- dynamics_(NULL),
unresolved_(16),
- decls_(4),
- receiver_(NULL),
- function_(NULL),
- arguments_(NULL),
- arguments_shadow_(NULL),
- illegal_redecl_(NULL),
- scope_inside_with_(false),
- scope_contains_with_(false),
- scope_calls_eval_(false),
- outer_scope_calls_eval_(false),
- inner_scope_calls_eval_(false),
- outer_scope_is_eval_scope_(false),
- force_eager_compilation_(false),
- num_stack_slots_(0),
- num_heap_slots_(0) {
+ decls_(4) {
+ SetDefaults(type, outer_scope, NULL);
// At some point we might want to provide outer scopes to
// eval scopes (by walking the stack and reading the scope info).
// In that case, the ASSERT below needs to be adjusted.
ASSERT((type == GLOBAL_SCOPE || type == EVAL_SCOPE) == (outer_scope == NULL));
ASSERT(!HasIllegalRedeclaration());
+ ASSERT(!resolved());
}
+Scope::Scope(Scope* inner_scope, SerializedScopeInfo* scope_info)
+ : inner_scopes_(4),
+ variables_(),
+ temps_(4),
+ params_(4),
+ unresolved_(16),
+ decls_(4) {
+ ASSERT(scope_info != NULL);
+ SetDefaults(FUNCTION_SCOPE, inner_scope->outer_scope(), scope_info);
+ ASSERT(resolved());
+ InsertAfterScope(inner_scope);
+ if (scope_info->HasHeapAllocatedLocals()) {
+ num_heap_slots_ = scope_info_->NumberOfContextSlots();
+ }
+}
+
+
+
bool Scope::Analyze(CompilationInfo* info) {
ASSERT(info->function() != NULL);
Scope* top = info->function()->scope();
+
+ // If we have a serialized scope info, reuse it.
+ if (!info->closure().is_null()) {
+ SerializedScopeInfo* scope_info = info->closure()->shared()->scope_info();
+ if (scope_info != SerializedScopeInfo::Empty()) {
+ Scope* scope = top;
+ JSFunction* current = *info->closure();
+ do {
+ current = current->context()->closure();
+ SerializedScopeInfo* scope_info = current->shared()->scope_info();
+ if (scope_info != SerializedScopeInfo::Empty()) {
+ scope = new Scope(scope, scope_info);
+ } else {
+ ASSERT(current->context()->IsGlobalContext());
+ }
+ } while (!current->context()->IsGlobalContext());
+ }
+ }
+
while (top->outer_scope() != NULL) top = top->outer_scope();
top->AllocateVariables(info->calling_context());
@@ -191,6 +197,8 @@ bool Scope::Analyze(CompilationInfo* info) {
void Scope::Initialize(bool inside_with) {
+ ASSERT(!resolved());
+
// Add this scope as a new inner scope of the outer scope.
if (outer_scope_ != NULL) {
outer_scope_->inner_scopes_.Add(this);
@@ -210,7 +218,7 @@ void Scope::Initialize(bool inside_with) {
Variable* var =
variables_.Declare(this, Factory::this_symbol(), Variable::VAR,
false, Variable::THIS);
- var->rewrite_ = new Slot(var, Slot::PARAMETER, -1);
+ var->set_rewrite(new Slot(var, Slot::PARAMETER, -1));
receiver_ = var;
if (is_function_scope()) {
@@ -224,7 +232,28 @@ void Scope::Initialize(bool inside_with) {
Variable* Scope::LocalLookup(Handle<String> name) {
- return variables_.Lookup(name);
+ Variable* result = variables_.Lookup(name);
+ if (result != NULL || !resolved()) {
+ return result;
+ }
+ // If the scope is resolved, we can find a variable in serialized scope info.
+
+ // We should never lookup 'arguments' in this scope
+ // as it is impllicitly present in any scope.
+ ASSERT(*name != *Factory::arguments_symbol());
+
+ // Check context slot lookup.
+ Variable::Mode mode;
+ int index = scope_info_->ContextSlotIndex(*name, &mode);
+ if (index < 0) {
+ return NULL;
+ }
+
+ // Check that there is no local slot with the given name.
+ ASSERT(scope_info_->StackSlotIndex(*name) < 0);
+ Variable* var = variables_.Declare(this, name, mode, true, Variable::NORMAL);
+ var->set_rewrite(new Slot(var, Slot::CONTEXT, index));
+ return var;
}
@@ -250,6 +279,7 @@ Variable* Scope::DeclareLocal(Handle<String> name, Variable::Mode mode) {
// DYNAMIC variables are introduces during variable allocation,
// INTERNAL variables are allocated explicitly, and TEMPORARY
// variables are allocated via NewTemporary().
+ ASSERT(!resolved());
ASSERT(mode == Variable::VAR || mode == Variable::CONST);
return variables_.Declare(this, name, mode, true, Variable::NORMAL);
}
@@ -273,6 +303,7 @@ VariableProxy* Scope::NewUnresolved(Handle<String> name, bool inside_with) {
// Note that we must not share the unresolved variables with
// the same name because they may be removed selectively via
// RemoveUnresolved().
+ ASSERT(!resolved());
VariableProxy* proxy = new VariableProxy(name, false, inside_with);
unresolved_.Add(proxy);
return proxy;
@@ -292,6 +323,7 @@ void Scope::RemoveUnresolved(VariableProxy* var) {
Variable* Scope::NewTemporary(Handle<String> name) {
+ ASSERT(!resolved());
Variable* var =
new Variable(this, name, Variable::TEMPORARY, true, Variable::NORMAL);
temps_.Add(var);
@@ -550,7 +582,7 @@ Variable* Scope::NonLocal(Handle<String> name, Variable::Mode mode) {
// Declare a new non-local.
var = map->Declare(NULL, name, mode, true, Variable::NORMAL);
// Allocate it by giving it a dynamic lookup.
- var->rewrite_ = new Slot(var, Slot::LOOKUP, -1);
+ var->set_rewrite(new Slot(var, Slot::LOOKUP, -1));
}
return var;
}
@@ -612,8 +644,9 @@ Variable* Scope::LookupRecursive(Handle<String> name,
ASSERT(var != NULL);
// If this is a lookup from an inner scope, mark the variable.
- if (inner_lookup)
- var->is_accessed_from_inner_scope_ = true;
+ if (inner_lookup) {
+ var->MarkAsAccessedFromInnerScope();
+ }
// If the variable we have found is just a guess, invalidate the
// result. If the found variable is local, record that fact so we
@@ -753,7 +786,7 @@ bool Scope::MustAllocate(Variable* var) {
// via an eval() call. This is only possible if the variable has a
// visible name.
if ((var->is_this() || var->name()->length() > 0) &&
- (var->is_accessed_from_inner_scope_ ||
+ (var->is_accessed_from_inner_scope() ||
scope_calls_eval_ || inner_scope_calls_eval_ ||
scope_contains_with_)) {
var->set_is_used(true);
@@ -771,7 +804,7 @@ bool Scope::MustAllocateInContext(Variable* var) {
// context.
return
var->mode() != Variable::TEMPORARY &&
- (var->is_accessed_from_inner_scope_ ||
+ (var->is_accessed_from_inner_scope() ||
scope_calls_eval_ || inner_scope_calls_eval_ ||
scope_contains_with_ || var->is_global());
}
@@ -787,12 +820,12 @@ bool Scope::HasArgumentsParameter() {
void Scope::AllocateStackSlot(Variable* var) {
- var->rewrite_ = new Slot(var, Slot::LOCAL, num_stack_slots_++);
+ var->set_rewrite(new Slot(var, Slot::LOCAL, num_stack_slots_++));
}
void Scope::AllocateHeapSlot(Variable* var) {
- var->rewrite_ = new Slot(var, Slot::CONTEXT, num_heap_slots_++);
+ var->set_rewrite(new Slot(var, Slot::CONTEXT, num_heap_slots_++));
}
@@ -857,7 +890,7 @@ void Scope::AllocateParameterLocals() {
// It is ok to set this only now, because arguments is a local
// variable that is allocated after the parameters have been
// allocated.
- arguments_shadow_->is_accessed_from_inner_scope_ = true;
+ arguments_shadow_->MarkAsAccessedFromInnerScope();
}
Property* rewrite =
new Property(new VariableProxy(arguments_shadow_),
@@ -865,7 +898,7 @@ void Scope::AllocateParameterLocals() {
RelocInfo::kNoPosition,
Property::SYNTHETIC);
rewrite->set_is_arguments_access(true);
- var->rewrite_ = rewrite;
+ var->set_rewrite(rewrite);
}
}
@@ -880,23 +913,23 @@ void Scope::AllocateParameterLocals() {
ASSERT(var->scope() == this);
if (MustAllocate(var)) {
if (MustAllocateInContext(var)) {
- ASSERT(var->rewrite_ == NULL ||
+ ASSERT(var->rewrite() == NULL ||
(var->AsSlot() != NULL &&
var->AsSlot()->type() == Slot::CONTEXT));
- if (var->rewrite_ == NULL) {
+ if (var->rewrite() == NULL) {
// Only set the heap allocation if the parameter has not
// been allocated yet.
AllocateHeapSlot(var);
}
} else {
- ASSERT(var->rewrite_ == NULL ||
+ ASSERT(var->rewrite() == NULL ||
(var->AsSlot() != NULL &&
var->AsSlot()->type() == Slot::PARAMETER));
// Set the parameter index always, even if the parameter
// was seen before! (We need to access the actual parameter
// supplied for the last occurrence of a multiply declared
// parameter.)
- var->rewrite_ = new Slot(var, Slot::PARAMETER, i);
+ var->set_rewrite(new Slot(var, Slot::PARAMETER, i));
}
}
}
@@ -906,10 +939,10 @@ void Scope::AllocateParameterLocals() {
void Scope::AllocateNonParameterLocal(Variable* var) {
ASSERT(var->scope() == this);
- ASSERT(var->rewrite_ == NULL ||
+ ASSERT(var->rewrite() == NULL ||
(!var->IsVariable(Factory::result_symbol())) ||
(var->AsSlot() == NULL || var->AsSlot()->type() != Slot::LOCAL));
- if (var->rewrite_ == NULL && MustAllocate(var)) {
+ if (var->rewrite() == NULL && MustAllocate(var)) {
if (MustAllocateInContext(var)) {
AllocateHeapSlot(var);
} else {
@@ -943,15 +976,18 @@ void Scope::AllocateNonParameterLocals() {
void Scope::AllocateVariablesRecursively() {
- // The number of slots required for variables.
- num_stack_slots_ = 0;
- num_heap_slots_ = Context::MIN_CONTEXT_SLOTS;
-
// Allocate variables for inner scopes.
for (int i = 0; i < inner_scopes_.length(); i++) {
inner_scopes_[i]->AllocateVariablesRecursively();
}
+ // If scope is already resolved, we still need to allocate
+ // variables in inner scopes which might not had been resolved yet.
+ if (resolved()) return;
+ // The number of slots required for variables.
+ num_stack_slots_ = 0;
+ num_heap_slots_ = Context::MIN_CONTEXT_SLOTS;
+
// Allocate variables for this scope.
// Parameters must be allocated first, if any.
if (is_function_scope()) AllocateParameterLocals();
diff --git a/deps/v8/src/scopes.h b/deps/v8/src/scopes.h
index d909b81fc1..09901ade61 100644
--- a/deps/v8/src/scopes.h
+++ b/deps/v8/src/scopes.h
@@ -302,6 +302,14 @@ class Scope: public ZoneObject {
explicit Scope(Type type);
+ void InsertAfterScope(Scope* scope) {
+ inner_scopes_.Add(scope);
+ outer_scope_ = scope->outer_scope_;
+ outer_scope_->inner_scopes_.RemoveElement(scope);
+ outer_scope_->inner_scopes_.Add(this);
+ scope->outer_scope_ = this;
+ }
+
// Scope tree.
Scope* outer_scope_; // the immediately enclosing outer scope, or NULL
ZoneList<Scope*> inner_scopes_; // the immediately enclosed inner scopes
@@ -355,6 +363,10 @@ class Scope: public ZoneObject {
int num_stack_slots_;
int num_heap_slots_;
+ // Serialized scopes support.
+ SerializedScopeInfo* scope_info_;
+ bool resolved() { return scope_info_ != NULL; }
+
// Create a non-local variable with a given name.
// These variables are looked up dynamically at runtime.
Variable* NonLocal(Handle<String> name, Variable::Mode mode);
@@ -386,6 +398,33 @@ class Scope: public ZoneObject {
void AllocateNonParameterLocal(Variable* var);
void AllocateNonParameterLocals();
void AllocateVariablesRecursively();
+
+ private:
+ Scope(Scope* inner_scope, SerializedScopeInfo* scope_info);
+
+ void SetDefaults(Type type,
+ Scope* outer_scope,
+ SerializedScopeInfo* scope_info) {
+ outer_scope_ = outer_scope;
+ type_ = type;
+ scope_name_ = Factory::empty_symbol();
+ dynamics_ = NULL;
+ receiver_ = NULL;
+ function_ = NULL;
+ arguments_ = NULL;
+ arguments_shadow_ = NULL;
+ illegal_redecl_ = NULL;
+ scope_inside_with_ = false;
+ scope_contains_with_ = false;
+ scope_calls_eval_ = false;
+ outer_scope_calls_eval_ = false;
+ inner_scope_calls_eval_ = false;
+ outer_scope_is_eval_scope_ = false;
+ force_eager_compilation_ = false;
+ num_stack_slots_ = 0;
+ num_heap_slots_ = 0;
+ scope_info_ = scope_info;
+ }
};
diff --git a/deps/v8/src/serialize.cc b/deps/v8/src/serialize.cc
index 00a601ffd9..6a6c6bbdb1 100644
--- a/deps/v8/src/serialize.cc
+++ b/deps/v8/src/serialize.cc
@@ -486,18 +486,26 @@ void ExternalReferenceTable::PopulateTable() {
UNCLASSIFIED,
36,
"LDoubleConstant::one_half");
- Add(ExternalReference::address_of_negative_infinity().address(),
+ Add(ExternalReference::address_of_minus_zero().address(),
UNCLASSIFIED,
37,
+ "LDoubleConstant::minus_zero");
+ Add(ExternalReference::address_of_negative_infinity().address(),
+ UNCLASSIFIED,
+ 38,
"LDoubleConstant::negative_infinity");
Add(ExternalReference::power_double_double_function().address(),
UNCLASSIFIED,
- 38,
+ 39,
"power_double_double_function");
Add(ExternalReference::power_double_int_function().address(),
UNCLASSIFIED,
- 39,
+ 40,
"power_double_int_function");
+ Add(ExternalReference::arguments_marker_location().address(),
+ UNCLASSIFIED,
+ 40,
+ "Factory::arguments_marker().location()");
}
diff --git a/deps/v8/src/string-search.h b/deps/v8/src/string-search.h
index eac84757ec..5de3c0951e 100644
--- a/deps/v8/src/string-search.h
+++ b/deps/v8/src/string-search.h
@@ -66,12 +66,7 @@ class StringSearchBase {
}
static inline bool IsAsciiString(Vector<const uc16> string) {
- for (int i = 0, n = string.length(); i < n; i++) {
- if (static_cast<unsigned>(string[i]) > String::kMaxAsciiCharCodeU) {
- return false;
- }
- }
- return true;
+ return String::IsAscii(string.start(), string.length());
}
// The following tables are shared by all searches.
diff --git a/deps/v8/src/string.js b/deps/v8/src/string.js
index 95275995e1..ab0ab54f03 100644
--- a/deps/v8/src/string.js
+++ b/deps/v8/src/string.js
@@ -127,7 +127,7 @@ function StringLastIndexOf(pat /* position */) { // length == 1
var index = subLength - patLength;
if (%_ArgumentsLength() > 1) {
var position = ToNumber(%_Arguments(1));
- if (!$isNaN(position)) {
+ if (!NUMBER_IS_NAN(position)) {
position = TO_INTEGER(position);
if (position < 0) {
position = 0;
diff --git a/deps/v8/src/token.h b/deps/v8/src/token.h
index 2f5ca1b5fc..fb890d2342 100644
--- a/deps/v8/src/token.h
+++ b/deps/v8/src/token.h
@@ -217,7 +217,7 @@ class Token {
// Returns a string corresponding to the C++ token name
// (e.g. "LT" for the token LT).
static const char* Name(Value tok) {
- ASSERT(0 <= tok && tok < NUM_TOKENS);
+ ASSERT(tok < NUM_TOKENS); // tok is unsigned
return name_[tok];
}
@@ -292,14 +292,14 @@ class Token {
// (.e., "<" for the token LT) or NULL if the token doesn't
// have a (unique) string (e.g. an IDENTIFIER).
static const char* String(Value tok) {
- ASSERT(0 <= tok && tok < NUM_TOKENS);
+ ASSERT(tok < NUM_TOKENS); // tok is unsigned.
return string_[tok];
}
// Returns the precedence > 0 for binary and compare
// operators; returns 0 otherwise.
static int Precedence(Value tok) {
- ASSERT(0 <= tok && tok < NUM_TOKENS);
+ ASSERT(tok < NUM_TOKENS); // tok is unsigned.
return precedence_[tok];
}
diff --git a/deps/v8/src/top.cc b/deps/v8/src/top.cc
index 3d86d11b7c..98c673c1b8 100644
--- a/deps/v8/src/top.cc
+++ b/deps/v8/src/top.cc
@@ -170,7 +170,9 @@ void Top::InitializeThreadLocal() {
// into for use by a stacks only core dump (aka minidump).
class PreallocatedMemoryThread: public Thread {
public:
- PreallocatedMemoryThread() : keep_running_(true) {
+ PreallocatedMemoryThread()
+ : Thread("v8:PreallocMem"),
+ keep_running_(true) {
wait_for_ever_semaphore_ = OS::CreateSemaphore(0);
data_ready_semaphore_ = OS::CreateSemaphore(0);
}
diff --git a/deps/v8/src/type-info.cc b/deps/v8/src/type-info.cc
index 8719439ad4..032d98567e 100644
--- a/deps/v8/src/type-info.cc
+++ b/deps/v8/src/type-info.cc
@@ -58,7 +58,9 @@ TypeInfo TypeInfo::TypeFromValue(Handle<Object> value) {
}
-TypeFeedbackOracle::TypeFeedbackOracle(Handle<Code> code) {
+TypeFeedbackOracle::TypeFeedbackOracle(Handle<Code> code,
+ Handle<Context> global_context) {
+ global_context_ = global_context;
Initialize(code);
}
@@ -71,17 +73,18 @@ void TypeFeedbackOracle::Initialize(Handle<Code> code) {
bool TypeFeedbackOracle::LoadIsMonomorphic(Property* expr) {
- return IsMonomorphic(expr->position());
+ return GetElement(map_, expr->position())->IsMap();
}
bool TypeFeedbackOracle:: StoreIsMonomorphic(Assignment* expr) {
- return IsMonomorphic(expr->position());
+ return GetElement(map_, expr->position())->IsMap();
}
bool TypeFeedbackOracle::CallIsMonomorphic(Call* expr) {
- return IsMonomorphic(expr->position());
+ Handle<Object> value = GetElement(map_, expr->position());
+ return value->IsMap() || value->IsSmi();
}
@@ -97,12 +100,6 @@ Handle<Map> TypeFeedbackOracle::StoreMonomorphicReceiverType(Assignment* expr) {
}
-Handle<Map> TypeFeedbackOracle::CallMonomorphicReceiverType(Call* expr) {
- ASSERT(CallIsMonomorphic(expr));
- return Handle<Map>::cast(GetElement(map_, expr->position()));
-}
-
-
ZoneMapList* TypeFeedbackOracle::LoadReceiverTypes(Property* expr,
Handle<String> name) {
Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL);
@@ -126,6 +123,37 @@ ZoneMapList* TypeFeedbackOracle::CallReceiverTypes(Call* expr,
}
+CheckType TypeFeedbackOracle::GetCallCheckType(Call* expr) {
+ Handle<Object> value = GetElement(map_, expr->position());
+ if (!value->IsSmi()) return RECEIVER_MAP_CHECK;
+ CheckType check = static_cast<CheckType>(Smi::cast(*value)->value());
+ ASSERT(check != RECEIVER_MAP_CHECK);
+ return check;
+}
+
+
+Handle<JSObject> TypeFeedbackOracle::GetPrototypeForPrimitiveCheck(
+ CheckType check) {
+ JSFunction* function = NULL;
+ switch (check) {
+ case RECEIVER_MAP_CHECK:
+ UNREACHABLE();
+ break;
+ case STRING_CHECK:
+ function = global_context_->string_function();
+ break;
+ case NUMBER_CHECK:
+ function = global_context_->number_function();
+ break;
+ case BOOLEAN_CHECK:
+ function = global_context_->boolean_function();
+ break;
+ }
+ ASSERT(function != NULL);
+ return Handle<JSObject>(JSObject::cast(function->instance_prototype()));
+}
+
+
bool TypeFeedbackOracle::LoadIsBuiltin(Property* expr, Builtins::Name id) {
Handle<Object> object = GetElement(map_, expr->position());
return *object == Builtins::builtin(id);
@@ -220,6 +248,7 @@ TypeInfo TypeFeedbackOracle::BinaryType(BinaryOperation* expr, Side side) {
return unknown;
}
+
TypeInfo TypeFeedbackOracle::SwitchType(CaseClause* clause) {
Handle<Object> object = GetElement(map_, clause->position());
TypeInfo unknown = TypeInfo::Unknown();
@@ -247,12 +276,11 @@ TypeInfo TypeFeedbackOracle::SwitchType(CaseClause* clause) {
}
-
ZoneMapList* TypeFeedbackOracle::CollectReceiverTypes(int position,
Handle<String> name,
Code::Flags flags) {
Handle<Object> object = GetElement(map_, position);
- if (object->IsUndefined()) return NULL;
+ if (object->IsUndefined() || object->IsSmi()) return NULL;
if (*object == Builtins::builtin(Builtins::StoreIC_GlobalProxy)) {
// TODO(fschneider): We could collect the maps and signal that
@@ -301,11 +329,20 @@ void TypeFeedbackOracle::PopulateMap(Handle<Code> code) {
SetElement(map_, position, target);
}
} else if (state == MONOMORPHIC) {
- Handle<Map> map = Handle<Map>(target->FindFirstMap());
- if (*map == NULL) {
- SetElement(map_, position, target);
+ if (target->kind() != Code::CALL_IC ||
+ target->check_type() == RECEIVER_MAP_CHECK) {
+ Handle<Map> map = Handle<Map>(target->FindFirstMap());
+ if (*map == NULL) {
+ SetElement(map_, position, target);
+ } else {
+ SetElement(map_, position, map);
+ }
} else {
- SetElement(map_, position, map);
+ ASSERT(target->kind() == Code::CALL_IC);
+ CheckType check = target->check_type();
+ ASSERT(check != RECEIVER_MAP_CHECK);
+ SetElement(map_, position, Handle<Object>(Smi::FromInt(check)));
+ ASSERT(Smi::cast(*GetElement(map_, position))->value() == check);
}
} else if (state == MEGAMORPHIC) {
SetElement(map_, position, target);
@@ -342,8 +379,6 @@ void TypeFeedbackOracle::CollectPositions(Code* code,
} else if (kind == Code::COMPARE_IC) {
if (target->compare_state() == CompareIC::GENERIC) continue;
} else {
- if (kind == Code::CALL_IC && state == MONOMORPHIC &&
- target->check_type() != RECEIVER_MAP_CHECK) continue;
if (state != MONOMORPHIC && state != MEGAMORPHIC) continue;
}
code_positions->Add(
diff --git a/deps/v8/src/type-info.h b/deps/v8/src/type-info.h
index cb3e75d8ab..98d97def29 100644
--- a/deps/v8/src/type-info.h
+++ b/deps/v8/src/type-info.h
@@ -236,7 +236,7 @@ class TypeFeedbackOracle BASE_EMBEDDED {
RESULT
};
- explicit TypeFeedbackOracle(Handle<Code> code);
+ TypeFeedbackOracle(Handle<Code> code, Handle<Context> global_context);
bool LoadIsMonomorphic(Property* expr);
bool StoreIsMonomorphic(Assignment* expr);
@@ -244,12 +244,14 @@ class TypeFeedbackOracle BASE_EMBEDDED {
Handle<Map> LoadMonomorphicReceiverType(Property* expr);
Handle<Map> StoreMonomorphicReceiverType(Assignment* expr);
- Handle<Map> CallMonomorphicReceiverType(Call* expr);
ZoneMapList* LoadReceiverTypes(Property* expr, Handle<String> name);
ZoneMapList* StoreReceiverTypes(Assignment* expr, Handle<String> name);
ZoneMapList* CallReceiverTypes(Call* expr, Handle<String> name);
+ CheckType GetCallCheckType(Call* expr);
+ Handle<JSObject> GetPrototypeForPrimitiveCheck(CheckType check);
+
bool LoadIsBuiltin(Property* expr, Builtins::Name id);
// Get type information for arithmetic operations and compares.
@@ -260,8 +262,6 @@ class TypeFeedbackOracle BASE_EMBEDDED {
private:
void Initialize(Handle<Code> code);
- bool IsMonomorphic(int pos) { return GetElement(map_, pos)->IsMap(); }
-
ZoneMapList* CollectReceiverTypes(int position,
Handle<String> name,
Code::Flags flags);
@@ -272,6 +272,7 @@ class TypeFeedbackOracle BASE_EMBEDDED {
List<int>* code_positions,
List<int>* source_positions);
+ Handle<Context> global_context_;
Handle<JSObject> map_;
DISALLOW_COPY_AND_ASSIGN(TypeFeedbackOracle);
diff --git a/deps/v8/src/unicode.cc b/deps/v8/src/unicode.cc
index 35a66c69a0..346f673fd5 100644
--- a/deps/v8/src/unicode.cc
+++ b/deps/v8/src/unicode.cc
@@ -25,7 +25,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
-// This file was generated at 2010-07-30 14:07:24.988557
+// This file was generated at 2011-01-03 10:57:02.088925
#include "unicode-inl.h"
#include <stdlib.h>
@@ -35,6 +35,7 @@ namespace unibrow {
static const int kStartBit = (1 << 30);
static const int kChunkBits = (1 << 13);
+static const uchar kSentinel = static_cast<uchar>(-1);
/**
* \file
@@ -96,12 +97,12 @@ static bool LookupPredicate(const int32_t* table, uint16_t size, uchar chr) {
int32_t field = TableGet<kEntryDist>(table, low);
uchar entry = GetEntry(field);
bool is_start = IsStart(field);
- return (entry == value) ||
- (entry < value && is_start);
+ return (entry == value) || (entry < value && is_start);
}
template <int kW>
struct MultiCharacterSpecialCase {
+ static const uchar kEndOfEncoding = kSentinel;
uchar chars[kW];
};
@@ -172,7 +173,7 @@ static int LookupMapping(const int32_t* table,
int length = 0;
for (length = 0; length < kW; length++) {
uchar mapped = mapping.chars[length];
- if (mapped == static_cast<uchar>(-1)) break;
+ if (mapped == MultiCharacterSpecialCase<kW>::kEndOfEncoding) break;
if (ranges_are_linear) {
result[length] = mapped + (key - entry);
} else {
@@ -225,13 +226,13 @@ uchar Utf8::CalculateValue(const byte* str,
*cursor += 1;
return kBadChar;
}
- uchar l = ((first << 6) | second) & kMaxTwoByteChar;
- if (l <= kMaxOneByteChar) {
+ uchar code_point = ((first << 6) | second) & kMaxTwoByteChar;
+ if (code_point <= kMaxOneByteChar) {
*cursor += 1;
return kBadChar;
}
*cursor += 2;
- return l;
+ return code_point;
}
if (length == 2) {
*cursor += 1;
@@ -243,13 +244,14 @@ uchar Utf8::CalculateValue(const byte* str,
return kBadChar;
}
if (first < 0xF0) {
- uchar l = ((((first << 6) | second) << 6) | third) & kMaxThreeByteChar;
- if (l <= kMaxTwoByteChar) {
+ uchar code_point = ((((first << 6) | second) << 6) | third)
+ & kMaxThreeByteChar;
+ if (code_point <= kMaxTwoByteChar) {
*cursor += 1;
return kBadChar;
}
*cursor += 3;
- return l;
+ return code_point;
}
if (length == 3) {
*cursor += 1;
@@ -261,14 +263,14 @@ uchar Utf8::CalculateValue(const byte* str,
return kBadChar;
}
if (first < 0xF8) {
- uchar l = (((((first << 6 | second) << 6) | third) << 6) | fourth) &
- kMaxFourByteChar;
- if (l <= kMaxThreeByteChar) {
+ uchar code_point = (((((first << 6 | second) << 6) | third) << 6) | fourth)
+ & kMaxFourByteChar;
+ if (code_point <= kMaxThreeByteChar) {
*cursor += 1;
return kBadChar;
}
*cursor += 4;
- return l;
+ return code_point;
}
*cursor += 1;
return kBadChar;
@@ -823,7 +825,7 @@ bool ConnectorPunctuation::Is(uchar c) {
}
static const MultiCharacterSpecialCase<2> kToLowercaseMultiStrings0[2] = { // NOLINT
- {{105, 775}}, {{-1}} }; // NOLINT
+ {{105, 775}}, {{kSentinel}} }; // NOLINT
static const uint16_t kToLowercaseTable0Size = 463; // NOLINT
static const int32_t kToLowercaseTable0[926] = {
1073741889, 128, 90, 128, 1073742016, 128, 214, 128, 1073742040, 128, 222, 128, 256, 4, 258, 4, // NOLINT
@@ -886,7 +888,7 @@ static const int32_t kToLowercaseTable0[926] = {
8171, -448, 8172, -28, 1073750008, -512, 8185, -512, 1073750010, -504, 8187, -504, 8188, -36 }; // NOLINT
static const uint16_t kToLowercaseMultiStrings0Size = 2; // NOLINT
static const MultiCharacterSpecialCase<1> kToLowercaseMultiStrings1[1] = { // NOLINT
- {{-1}} }; // NOLINT
+ {{kSentinel}} }; // NOLINT
static const uint16_t kToLowercaseTable1Size = 69; // NOLINT
static const int32_t kToLowercaseTable1[138] = {
294, -30068, 298, -33532, 299, -33048, 306, 112, 1073742176, 64, 367, 64, 387, 4, 1073743030, 104, // NOLINT
@@ -900,7 +902,7 @@ static const int32_t kToLowercaseTable1[138] = {
3290, 4, 3292, 4, 3294, 4, 3296, 4, 3298, 4 }; // NOLINT
static const uint16_t kToLowercaseMultiStrings1Size = 1; // NOLINT
static const MultiCharacterSpecialCase<1> kToLowercaseMultiStrings7[1] = { // NOLINT
- {{-1}} }; // NOLINT
+ {{kSentinel}} }; // NOLINT
static const uint16_t kToLowercaseTable7Size = 2; // NOLINT
static const int32_t kToLowercaseTable7[4] = {
1073749793, 128, 7994, 128 }; // NOLINT
@@ -937,22 +939,22 @@ int ToLowercase::Convert(uchar c,
}
static const MultiCharacterSpecialCase<3> kToUppercaseMultiStrings0[62] = { // NOLINT
- {{83, 83, -1}}, {{700, 78, -1}}, {{74, 780, -1}}, {{921, 776, 769}}, // NOLINT
- {{933, 776, 769}}, {{1333, 1362, -1}}, {{72, 817, -1}}, {{84, 776, -1}}, // NOLINT
- {{87, 778, -1}}, {{89, 778, -1}}, {{65, 702, -1}}, {{933, 787, -1}}, // NOLINT
- {{933, 787, 768}}, {{933, 787, 769}}, {{933, 787, 834}}, {{7944, 921, -1}}, // NOLINT
- {{7945, 921, -1}}, {{7946, 921, -1}}, {{7947, 921, -1}}, {{7948, 921, -1}}, // NOLINT
- {{7949, 921, -1}}, {{7950, 921, -1}}, {{7951, 921, -1}}, {{7976, 921, -1}}, // NOLINT
- {{7977, 921, -1}}, {{7978, 921, -1}}, {{7979, 921, -1}}, {{7980, 921, -1}}, // NOLINT
- {{7981, 921, -1}}, {{7982, 921, -1}}, {{7983, 921, -1}}, {{8040, 921, -1}}, // NOLINT
- {{8041, 921, -1}}, {{8042, 921, -1}}, {{8043, 921, -1}}, {{8044, 921, -1}}, // NOLINT
- {{8045, 921, -1}}, {{8046, 921, -1}}, {{8047, 921, -1}}, {{8122, 921, -1}}, // NOLINT
- {{913, 921, -1}}, {{902, 921, -1}}, {{913, 834, -1}}, {{913, 834, 921}}, // NOLINT
- {{8138, 921, -1}}, {{919, 921, -1}}, {{905, 921, -1}}, {{919, 834, -1}}, // NOLINT
- {{919, 834, 921}}, {{921, 776, 768}}, {{921, 834, -1}}, {{921, 776, 834}}, // NOLINT
- {{933, 776, 768}}, {{929, 787, -1}}, {{933, 834, -1}}, {{933, 776, 834}}, // NOLINT
- {{8186, 921, -1}}, {{937, 921, -1}}, {{911, 921, -1}}, {{937, 834, -1}}, // NOLINT
- {{937, 834, 921}}, {{-1}} }; // NOLINT
+ {{83, 83, kSentinel}}, {{700, 78, kSentinel}}, {{74, 780, kSentinel}}, {{921, 776, 769}}, // NOLINT
+ {{933, 776, 769}}, {{1333, 1362, kSentinel}}, {{72, 817, kSentinel}}, {{84, 776, kSentinel}}, // NOLINT
+ {{87, 778, kSentinel}}, {{89, 778, kSentinel}}, {{65, 702, kSentinel}}, {{933, 787, kSentinel}}, // NOLINT
+ {{933, 787, 768}}, {{933, 787, 769}}, {{933, 787, 834}}, {{7944, 921, kSentinel}}, // NOLINT
+ {{7945, 921, kSentinel}}, {{7946, 921, kSentinel}}, {{7947, 921, kSentinel}}, {{7948, 921, kSentinel}}, // NOLINT
+ {{7949, 921, kSentinel}}, {{7950, 921, kSentinel}}, {{7951, 921, kSentinel}}, {{7976, 921, kSentinel}}, // NOLINT
+ {{7977, 921, kSentinel}}, {{7978, 921, kSentinel}}, {{7979, 921, kSentinel}}, {{7980, 921, kSentinel}}, // NOLINT
+ {{7981, 921, kSentinel}}, {{7982, 921, kSentinel}}, {{7983, 921, kSentinel}}, {{8040, 921, kSentinel}}, // NOLINT
+ {{8041, 921, kSentinel}}, {{8042, 921, kSentinel}}, {{8043, 921, kSentinel}}, {{8044, 921, kSentinel}}, // NOLINT
+ {{8045, 921, kSentinel}}, {{8046, 921, kSentinel}}, {{8047, 921, kSentinel}}, {{8122, 921, kSentinel}}, // NOLINT
+ {{913, 921, kSentinel}}, {{902, 921, kSentinel}}, {{913, 834, kSentinel}}, {{913, 834, 921}}, // NOLINT
+ {{8138, 921, kSentinel}}, {{919, 921, kSentinel}}, {{905, 921, kSentinel}}, {{919, 834, kSentinel}}, // NOLINT
+ {{919, 834, 921}}, {{921, 776, 768}}, {{921, 834, kSentinel}}, {{921, 776, 834}}, // NOLINT
+ {{933, 776, 768}}, {{929, 787, kSentinel}}, {{933, 834, kSentinel}}, {{933, 776, 834}}, // NOLINT
+ {{8186, 921, kSentinel}}, {{937, 921, kSentinel}}, {{911, 921, kSentinel}}, {{937, 834, kSentinel}}, // NOLINT
+ {{937, 834, 921}}, {{kSentinel}} }; // NOLINT
static const uint16_t kToUppercaseTable0Size = 554; // NOLINT
static const int32_t kToUppercaseTable0[1108] = {
1073741921, -128, 122, -128, 181, 2972, 223, 1, 1073742048, -128, 246, -128, 1073742072, -128, 254, -128, // NOLINT
@@ -1027,7 +1029,7 @@ static const int32_t kToUppercaseTable0[1108] = {
8183, 241, 8188, 229 }; // NOLINT
static const uint16_t kToUppercaseMultiStrings0Size = 62; // NOLINT
static const MultiCharacterSpecialCase<1> kToUppercaseMultiStrings1[1] = { // NOLINT
- {{-1}} }; // NOLINT
+ {{kSentinel}} }; // NOLINT
static const uint16_t kToUppercaseTable1Size = 67; // NOLINT
static const int32_t kToUppercaseTable1[134] = {
334, -112, 1073742192, -64, 383, -64, 388, -4, 1073743056, -104, 1257, -104, 1073744944, -192, 3166, -192, // NOLINT
@@ -1041,9 +1043,9 @@ static const int32_t kToUppercaseTable1[134] = {
3299, -4, 1073745152, -29056, 3365, -29056 }; // NOLINT
static const uint16_t kToUppercaseMultiStrings1Size = 1; // NOLINT
static const MultiCharacterSpecialCase<3> kToUppercaseMultiStrings7[12] = { // NOLINT
- {{70, 70, -1}}, {{70, 73, -1}}, {{70, 76, -1}}, {{70, 70, 73}}, // NOLINT
- {{70, 70, 76}}, {{83, 84, -1}}, {{1348, 1350, -1}}, {{1348, 1333, -1}}, // NOLINT
- {{1348, 1339, -1}}, {{1358, 1350, -1}}, {{1348, 1341, -1}}, {{-1}} }; // NOLINT
+ {{70, 70, kSentinel}}, {{70, 73, kSentinel}}, {{70, 76, kSentinel}}, {{70, 70, 73}}, // NOLINT
+ {{70, 70, 76}}, {{83, 84, kSentinel}}, {{1348, 1350, kSentinel}}, {{1348, 1333, kSentinel}}, // NOLINT
+ {{1348, 1339, kSentinel}}, {{1358, 1350, kSentinel}}, {{1348, 1341, kSentinel}}, {{kSentinel}} }; // NOLINT
static const uint16_t kToUppercaseTable7Size = 14; // NOLINT
static const int32_t kToUppercaseTable7[28] = {
6912, 1, 6913, 5, 6914, 9, 6915, 13, 6916, 17, 6917, 21, 6918, 21, 6931, 25, // NOLINT
@@ -1081,7 +1083,7 @@ int ToUppercase::Convert(uchar c,
}
static const MultiCharacterSpecialCase<1> kEcma262CanonicalizeMultiStrings0[1] = { // NOLINT
- {{-1}} }; // NOLINT
+ {{kSentinel}} }; // NOLINT
static const uint16_t kEcma262CanonicalizeTable0Size = 462; // NOLINT
static const int32_t kEcma262CanonicalizeTable0[924] = {
1073741921, -128, 122, -128, 181, 2972, 1073742048, -128, 246, -128, 1073742072, -128, 254, -128, 255, 484, // NOLINT
@@ -1144,7 +1146,7 @@ static const int32_t kEcma262CanonicalizeTable0[924] = {
8126, -28820, 1073749968, 32, 8145, 32, 1073749984, 32, 8161, 32, 8165, 28 }; // NOLINT
static const uint16_t kEcma262CanonicalizeMultiStrings0Size = 1; // NOLINT
static const MultiCharacterSpecialCase<1> kEcma262CanonicalizeMultiStrings1[1] = { // NOLINT
- {{-1}} }; // NOLINT
+ {{kSentinel}} }; // NOLINT
static const uint16_t kEcma262CanonicalizeTable1Size = 67; // NOLINT
static const int32_t kEcma262CanonicalizeTable1[134] = {
334, -112, 1073742192, -64, 383, -64, 388, -4, 1073743056, -104, 1257, -104, 1073744944, -192, 3166, -192, // NOLINT
@@ -1158,7 +1160,7 @@ static const int32_t kEcma262CanonicalizeTable1[134] = {
3299, -4, 1073745152, -29056, 3365, -29056 }; // NOLINT
static const uint16_t kEcma262CanonicalizeMultiStrings1Size = 1; // NOLINT
static const MultiCharacterSpecialCase<1> kEcma262CanonicalizeMultiStrings7[1] = { // NOLINT
- {{-1}} }; // NOLINT
+ {{kSentinel}} }; // NOLINT
static const uint16_t kEcma262CanonicalizeTable7Size = 2; // NOLINT
static const int32_t kEcma262CanonicalizeTable7[4] = {
1073749825, -128, 8026, -128 }; // NOLINT
@@ -1195,124 +1197,124 @@ int Ecma262Canonicalize::Convert(uchar c,
}
static const MultiCharacterSpecialCase<4> kEcma262UnCanonicalizeMultiStrings0[469] = { // NOLINT
- {{65, 97, -1}}, {{90, 122, -1}}, {{181, 924, 956, -1}}, {{192, 224, -1}}, // NOLINT
- {{214, 246, -1}}, {{216, 248, -1}}, {{222, 254, -1}}, {{255, 376, -1}}, // NOLINT
- {{256, 257, -1}}, {{258, 259, -1}}, {{260, 261, -1}}, {{262, 263, -1}}, // NOLINT
- {{264, 265, -1}}, {{266, 267, -1}}, {{268, 269, -1}}, {{270, 271, -1}}, // NOLINT
- {{272, 273, -1}}, {{274, 275, -1}}, {{276, 277, -1}}, {{278, 279, -1}}, // NOLINT
- {{280, 281, -1}}, {{282, 283, -1}}, {{284, 285, -1}}, {{286, 287, -1}}, // NOLINT
- {{288, 289, -1}}, {{290, 291, -1}}, {{292, 293, -1}}, {{294, 295, -1}}, // NOLINT
- {{296, 297, -1}}, {{298, 299, -1}}, {{300, 301, -1}}, {{302, 303, -1}}, // NOLINT
- {{306, 307, -1}}, {{308, 309, -1}}, {{310, 311, -1}}, {{313, 314, -1}}, // NOLINT
- {{315, 316, -1}}, {{317, 318, -1}}, {{319, 320, -1}}, {{321, 322, -1}}, // NOLINT
- {{323, 324, -1}}, {{325, 326, -1}}, {{327, 328, -1}}, {{330, 331, -1}}, // NOLINT
- {{332, 333, -1}}, {{334, 335, -1}}, {{336, 337, -1}}, {{338, 339, -1}}, // NOLINT
- {{340, 341, -1}}, {{342, 343, -1}}, {{344, 345, -1}}, {{346, 347, -1}}, // NOLINT
- {{348, 349, -1}}, {{350, 351, -1}}, {{352, 353, -1}}, {{354, 355, -1}}, // NOLINT
- {{356, 357, -1}}, {{358, 359, -1}}, {{360, 361, -1}}, {{362, 363, -1}}, // NOLINT
- {{364, 365, -1}}, {{366, 367, -1}}, {{368, 369, -1}}, {{370, 371, -1}}, // NOLINT
- {{372, 373, -1}}, {{374, 375, -1}}, {{377, 378, -1}}, {{379, 380, -1}}, // NOLINT
- {{381, 382, -1}}, {{384, 579, -1}}, {{385, 595, -1}}, {{386, 387, -1}}, // NOLINT
- {{388, 389, -1}}, {{390, 596, -1}}, {{391, 392, -1}}, {{393, 598, -1}}, // NOLINT
- {{394, 599, -1}}, {{395, 396, -1}}, {{398, 477, -1}}, {{399, 601, -1}}, // NOLINT
- {{400, 603, -1}}, {{401, 402, -1}}, {{403, 608, -1}}, {{404, 611, -1}}, // NOLINT
- {{405, 502, -1}}, {{406, 617, -1}}, {{407, 616, -1}}, {{408, 409, -1}}, // NOLINT
- {{410, 573, -1}}, {{412, 623, -1}}, {{413, 626, -1}}, {{414, 544, -1}}, // NOLINT
- {{415, 629, -1}}, {{416, 417, -1}}, {{418, 419, -1}}, {{420, 421, -1}}, // NOLINT
- {{422, 640, -1}}, {{423, 424, -1}}, {{425, 643, -1}}, {{428, 429, -1}}, // NOLINT
- {{430, 648, -1}}, {{431, 432, -1}}, {{433, 650, -1}}, {{434, 651, -1}}, // NOLINT
- {{435, 436, -1}}, {{437, 438, -1}}, {{439, 658, -1}}, {{440, 441, -1}}, // NOLINT
- {{444, 445, -1}}, {{447, 503, -1}}, {{452, 453, 454, -1}}, {{455, 456, 457, -1}}, // NOLINT
- {{458, 459, 460, -1}}, {{461, 462, -1}}, {{463, 464, -1}}, {{465, 466, -1}}, // NOLINT
- {{467, 468, -1}}, {{469, 470, -1}}, {{471, 472, -1}}, {{473, 474, -1}}, // NOLINT
- {{475, 476, -1}}, {{478, 479, -1}}, {{480, 481, -1}}, {{482, 483, -1}}, // NOLINT
- {{484, 485, -1}}, {{486, 487, -1}}, {{488, 489, -1}}, {{490, 491, -1}}, // NOLINT
- {{492, 493, -1}}, {{494, 495, -1}}, {{497, 498, 499, -1}}, {{500, 501, -1}}, // NOLINT
- {{504, 505, -1}}, {{506, 507, -1}}, {{508, 509, -1}}, {{510, 511, -1}}, // NOLINT
- {{512, 513, -1}}, {{514, 515, -1}}, {{516, 517, -1}}, {{518, 519, -1}}, // NOLINT
- {{520, 521, -1}}, {{522, 523, -1}}, {{524, 525, -1}}, {{526, 527, -1}}, // NOLINT
- {{528, 529, -1}}, {{530, 531, -1}}, {{532, 533, -1}}, {{534, 535, -1}}, // NOLINT
- {{536, 537, -1}}, {{538, 539, -1}}, {{540, 541, -1}}, {{542, 543, -1}}, // NOLINT
- {{546, 547, -1}}, {{548, 549, -1}}, {{550, 551, -1}}, {{552, 553, -1}}, // NOLINT
- {{554, 555, -1}}, {{556, 557, -1}}, {{558, 559, -1}}, {{560, 561, -1}}, // NOLINT
- {{562, 563, -1}}, {{570, 11365, -1}}, {{571, 572, -1}}, {{574, 11366, -1}}, // NOLINT
- {{577, 578, -1}}, {{580, 649, -1}}, {{581, 652, -1}}, {{582, 583, -1}}, // NOLINT
- {{584, 585, -1}}, {{586, 587, -1}}, {{588, 589, -1}}, {{590, 591, -1}}, // NOLINT
- {{619, 11362, -1}}, {{637, 11364, -1}}, {{837, 921, 953, 8126}}, {{891, 1021, -1}}, // NOLINT
- {{893, 1023, -1}}, {{902, 940, -1}}, {{904, 941, -1}}, {{906, 943, -1}}, // NOLINT
- {{908, 972, -1}}, {{910, 973, -1}}, {{911, 974, -1}}, {{913, 945, -1}}, // NOLINT
- {{914, 946, 976, -1}}, {{915, 947, -1}}, {{916, 948, -1}}, {{917, 949, 1013, -1}}, // NOLINT
- {{918, 950, -1}}, {{919, 951, -1}}, {{920, 952, 977, -1}}, {{922, 954, 1008, -1}}, // NOLINT
- {{923, 955, -1}}, {{925, 957, -1}}, {{927, 959, -1}}, {{928, 960, 982, -1}}, // NOLINT
- {{929, 961, 1009, -1}}, {{931, 962, 963, -1}}, {{932, 964, -1}}, {{933, 965, -1}}, // NOLINT
- {{934, 966, 981, -1}}, {{935, 967, -1}}, {{939, 971, -1}}, {{984, 985, -1}}, // NOLINT
- {{986, 987, -1}}, {{988, 989, -1}}, {{990, 991, -1}}, {{992, 993, -1}}, // NOLINT
- {{994, 995, -1}}, {{996, 997, -1}}, {{998, 999, -1}}, {{1000, 1001, -1}}, // NOLINT
- {{1002, 1003, -1}}, {{1004, 1005, -1}}, {{1006, 1007, -1}}, {{1010, 1017, -1}}, // NOLINT
- {{1015, 1016, -1}}, {{1018, 1019, -1}}, {{1024, 1104, -1}}, {{1039, 1119, -1}}, // NOLINT
- {{1040, 1072, -1}}, {{1071, 1103, -1}}, {{1120, 1121, -1}}, {{1122, 1123, -1}}, // NOLINT
- {{1124, 1125, -1}}, {{1126, 1127, -1}}, {{1128, 1129, -1}}, {{1130, 1131, -1}}, // NOLINT
- {{1132, 1133, -1}}, {{1134, 1135, -1}}, {{1136, 1137, -1}}, {{1138, 1139, -1}}, // NOLINT
- {{1140, 1141, -1}}, {{1142, 1143, -1}}, {{1144, 1145, -1}}, {{1146, 1147, -1}}, // NOLINT
- {{1148, 1149, -1}}, {{1150, 1151, -1}}, {{1152, 1153, -1}}, {{1162, 1163, -1}}, // NOLINT
- {{1164, 1165, -1}}, {{1166, 1167, -1}}, {{1168, 1169, -1}}, {{1170, 1171, -1}}, // NOLINT
- {{1172, 1173, -1}}, {{1174, 1175, -1}}, {{1176, 1177, -1}}, {{1178, 1179, -1}}, // NOLINT
- {{1180, 1181, -1}}, {{1182, 1183, -1}}, {{1184, 1185, -1}}, {{1186, 1187, -1}}, // NOLINT
- {{1188, 1189, -1}}, {{1190, 1191, -1}}, {{1192, 1193, -1}}, {{1194, 1195, -1}}, // NOLINT
- {{1196, 1197, -1}}, {{1198, 1199, -1}}, {{1200, 1201, -1}}, {{1202, 1203, -1}}, // NOLINT
- {{1204, 1205, -1}}, {{1206, 1207, -1}}, {{1208, 1209, -1}}, {{1210, 1211, -1}}, // NOLINT
- {{1212, 1213, -1}}, {{1214, 1215, -1}}, {{1216, 1231, -1}}, {{1217, 1218, -1}}, // NOLINT
- {{1219, 1220, -1}}, {{1221, 1222, -1}}, {{1223, 1224, -1}}, {{1225, 1226, -1}}, // NOLINT
- {{1227, 1228, -1}}, {{1229, 1230, -1}}, {{1232, 1233, -1}}, {{1234, 1235, -1}}, // NOLINT
- {{1236, 1237, -1}}, {{1238, 1239, -1}}, {{1240, 1241, -1}}, {{1242, 1243, -1}}, // NOLINT
- {{1244, 1245, -1}}, {{1246, 1247, -1}}, {{1248, 1249, -1}}, {{1250, 1251, -1}}, // NOLINT
- {{1252, 1253, -1}}, {{1254, 1255, -1}}, {{1256, 1257, -1}}, {{1258, 1259, -1}}, // NOLINT
- {{1260, 1261, -1}}, {{1262, 1263, -1}}, {{1264, 1265, -1}}, {{1266, 1267, -1}}, // NOLINT
- {{1268, 1269, -1}}, {{1270, 1271, -1}}, {{1272, 1273, -1}}, {{1274, 1275, -1}}, // NOLINT
- {{1276, 1277, -1}}, {{1278, 1279, -1}}, {{1280, 1281, -1}}, {{1282, 1283, -1}}, // NOLINT
- {{1284, 1285, -1}}, {{1286, 1287, -1}}, {{1288, 1289, -1}}, {{1290, 1291, -1}}, // NOLINT
- {{1292, 1293, -1}}, {{1294, 1295, -1}}, {{1296, 1297, -1}}, {{1298, 1299, -1}}, // NOLINT
- {{1329, 1377, -1}}, {{1366, 1414, -1}}, {{4256, 11520, -1}}, {{4293, 11557, -1}}, // NOLINT
- {{7549, 11363, -1}}, {{7680, 7681, -1}}, {{7682, 7683, -1}}, {{7684, 7685, -1}}, // NOLINT
- {{7686, 7687, -1}}, {{7688, 7689, -1}}, {{7690, 7691, -1}}, {{7692, 7693, -1}}, // NOLINT
- {{7694, 7695, -1}}, {{7696, 7697, -1}}, {{7698, 7699, -1}}, {{7700, 7701, -1}}, // NOLINT
- {{7702, 7703, -1}}, {{7704, 7705, -1}}, {{7706, 7707, -1}}, {{7708, 7709, -1}}, // NOLINT
- {{7710, 7711, -1}}, {{7712, 7713, -1}}, {{7714, 7715, -1}}, {{7716, 7717, -1}}, // NOLINT
- {{7718, 7719, -1}}, {{7720, 7721, -1}}, {{7722, 7723, -1}}, {{7724, 7725, -1}}, // NOLINT
- {{7726, 7727, -1}}, {{7728, 7729, -1}}, {{7730, 7731, -1}}, {{7732, 7733, -1}}, // NOLINT
- {{7734, 7735, -1}}, {{7736, 7737, -1}}, {{7738, 7739, -1}}, {{7740, 7741, -1}}, // NOLINT
- {{7742, 7743, -1}}, {{7744, 7745, -1}}, {{7746, 7747, -1}}, {{7748, 7749, -1}}, // NOLINT
- {{7750, 7751, -1}}, {{7752, 7753, -1}}, {{7754, 7755, -1}}, {{7756, 7757, -1}}, // NOLINT
- {{7758, 7759, -1}}, {{7760, 7761, -1}}, {{7762, 7763, -1}}, {{7764, 7765, -1}}, // NOLINT
- {{7766, 7767, -1}}, {{7768, 7769, -1}}, {{7770, 7771, -1}}, {{7772, 7773, -1}}, // NOLINT
- {{7774, 7775, -1}}, {{7776, 7777, 7835, -1}}, {{7778, 7779, -1}}, {{7780, 7781, -1}}, // NOLINT
- {{7782, 7783, -1}}, {{7784, 7785, -1}}, {{7786, 7787, -1}}, {{7788, 7789, -1}}, // NOLINT
- {{7790, 7791, -1}}, {{7792, 7793, -1}}, {{7794, 7795, -1}}, {{7796, 7797, -1}}, // NOLINT
- {{7798, 7799, -1}}, {{7800, 7801, -1}}, {{7802, 7803, -1}}, {{7804, 7805, -1}}, // NOLINT
- {{7806, 7807, -1}}, {{7808, 7809, -1}}, {{7810, 7811, -1}}, {{7812, 7813, -1}}, // NOLINT
- {{7814, 7815, -1}}, {{7816, 7817, -1}}, {{7818, 7819, -1}}, {{7820, 7821, -1}}, // NOLINT
- {{7822, 7823, -1}}, {{7824, 7825, -1}}, {{7826, 7827, -1}}, {{7828, 7829, -1}}, // NOLINT
- {{7840, 7841, -1}}, {{7842, 7843, -1}}, {{7844, 7845, -1}}, {{7846, 7847, -1}}, // NOLINT
- {{7848, 7849, -1}}, {{7850, 7851, -1}}, {{7852, 7853, -1}}, {{7854, 7855, -1}}, // NOLINT
- {{7856, 7857, -1}}, {{7858, 7859, -1}}, {{7860, 7861, -1}}, {{7862, 7863, -1}}, // NOLINT
- {{7864, 7865, -1}}, {{7866, 7867, -1}}, {{7868, 7869, -1}}, {{7870, 7871, -1}}, // NOLINT
- {{7872, 7873, -1}}, {{7874, 7875, -1}}, {{7876, 7877, -1}}, {{7878, 7879, -1}}, // NOLINT
- {{7880, 7881, -1}}, {{7882, 7883, -1}}, {{7884, 7885, -1}}, {{7886, 7887, -1}}, // NOLINT
- {{7888, 7889, -1}}, {{7890, 7891, -1}}, {{7892, 7893, -1}}, {{7894, 7895, -1}}, // NOLINT
- {{7896, 7897, -1}}, {{7898, 7899, -1}}, {{7900, 7901, -1}}, {{7902, 7903, -1}}, // NOLINT
- {{7904, 7905, -1}}, {{7906, 7907, -1}}, {{7908, 7909, -1}}, {{7910, 7911, -1}}, // NOLINT
- {{7912, 7913, -1}}, {{7914, 7915, -1}}, {{7916, 7917, -1}}, {{7918, 7919, -1}}, // NOLINT
- {{7920, 7921, -1}}, {{7922, 7923, -1}}, {{7924, 7925, -1}}, {{7926, 7927, -1}}, // NOLINT
- {{7928, 7929, -1}}, {{7936, 7944, -1}}, {{7943, 7951, -1}}, {{7952, 7960, -1}}, // NOLINT
- {{7957, 7965, -1}}, {{7968, 7976, -1}}, {{7975, 7983, -1}}, {{7984, 7992, -1}}, // NOLINT
- {{7991, 7999, -1}}, {{8000, 8008, -1}}, {{8005, 8013, -1}}, {{8017, 8025, -1}}, // NOLINT
- {{8019, 8027, -1}}, {{8021, 8029, -1}}, {{8023, 8031, -1}}, {{8032, 8040, -1}}, // NOLINT
- {{8039, 8047, -1}}, {{8048, 8122, -1}}, {{8049, 8123, -1}}, {{8050, 8136, -1}}, // NOLINT
- {{8053, 8139, -1}}, {{8054, 8154, -1}}, {{8055, 8155, -1}}, {{8056, 8184, -1}}, // NOLINT
- {{8057, 8185, -1}}, {{8058, 8170, -1}}, {{8059, 8171, -1}}, {{8060, 8186, -1}}, // NOLINT
- {{8061, 8187, -1}}, {{8112, 8120, -1}}, {{8113, 8121, -1}}, {{8144, 8152, -1}}, // NOLINT
- {{8145, 8153, -1}}, {{8160, 8168, -1}}, {{8161, 8169, -1}}, {{8165, 8172, -1}}, // NOLINT
- {{-1}} }; // NOLINT
+ {{65, 97, kSentinel}}, {{90, 122, kSentinel}}, {{181, 924, 956, kSentinel}}, {{192, 224, kSentinel}}, // NOLINT
+ {{214, 246, kSentinel}}, {{216, 248, kSentinel}}, {{222, 254, kSentinel}}, {{255, 376, kSentinel}}, // NOLINT
+ {{256, 257, kSentinel}}, {{258, 259, kSentinel}}, {{260, 261, kSentinel}}, {{262, 263, kSentinel}}, // NOLINT
+ {{264, 265, kSentinel}}, {{266, 267, kSentinel}}, {{268, 269, kSentinel}}, {{270, 271, kSentinel}}, // NOLINT
+ {{272, 273, kSentinel}}, {{274, 275, kSentinel}}, {{276, 277, kSentinel}}, {{278, 279, kSentinel}}, // NOLINT
+ {{280, 281, kSentinel}}, {{282, 283, kSentinel}}, {{284, 285, kSentinel}}, {{286, 287, kSentinel}}, // NOLINT
+ {{288, 289, kSentinel}}, {{290, 291, kSentinel}}, {{292, 293, kSentinel}}, {{294, 295, kSentinel}}, // NOLINT
+ {{296, 297, kSentinel}}, {{298, 299, kSentinel}}, {{300, 301, kSentinel}}, {{302, 303, kSentinel}}, // NOLINT
+ {{306, 307, kSentinel}}, {{308, 309, kSentinel}}, {{310, 311, kSentinel}}, {{313, 314, kSentinel}}, // NOLINT
+ {{315, 316, kSentinel}}, {{317, 318, kSentinel}}, {{319, 320, kSentinel}}, {{321, 322, kSentinel}}, // NOLINT
+ {{323, 324, kSentinel}}, {{325, 326, kSentinel}}, {{327, 328, kSentinel}}, {{330, 331, kSentinel}}, // NOLINT
+ {{332, 333, kSentinel}}, {{334, 335, kSentinel}}, {{336, 337, kSentinel}}, {{338, 339, kSentinel}}, // NOLINT
+ {{340, 341, kSentinel}}, {{342, 343, kSentinel}}, {{344, 345, kSentinel}}, {{346, 347, kSentinel}}, // NOLINT
+ {{348, 349, kSentinel}}, {{350, 351, kSentinel}}, {{352, 353, kSentinel}}, {{354, 355, kSentinel}}, // NOLINT
+ {{356, 357, kSentinel}}, {{358, 359, kSentinel}}, {{360, 361, kSentinel}}, {{362, 363, kSentinel}}, // NOLINT
+ {{364, 365, kSentinel}}, {{366, 367, kSentinel}}, {{368, 369, kSentinel}}, {{370, 371, kSentinel}}, // NOLINT
+ {{372, 373, kSentinel}}, {{374, 375, kSentinel}}, {{377, 378, kSentinel}}, {{379, 380, kSentinel}}, // NOLINT
+ {{381, 382, kSentinel}}, {{384, 579, kSentinel}}, {{385, 595, kSentinel}}, {{386, 387, kSentinel}}, // NOLINT
+ {{388, 389, kSentinel}}, {{390, 596, kSentinel}}, {{391, 392, kSentinel}}, {{393, 598, kSentinel}}, // NOLINT
+ {{394, 599, kSentinel}}, {{395, 396, kSentinel}}, {{398, 477, kSentinel}}, {{399, 601, kSentinel}}, // NOLINT
+ {{400, 603, kSentinel}}, {{401, 402, kSentinel}}, {{403, 608, kSentinel}}, {{404, 611, kSentinel}}, // NOLINT
+ {{405, 502, kSentinel}}, {{406, 617, kSentinel}}, {{407, 616, kSentinel}}, {{408, 409, kSentinel}}, // NOLINT
+ {{410, 573, kSentinel}}, {{412, 623, kSentinel}}, {{413, 626, kSentinel}}, {{414, 544, kSentinel}}, // NOLINT
+ {{415, 629, kSentinel}}, {{416, 417, kSentinel}}, {{418, 419, kSentinel}}, {{420, 421, kSentinel}}, // NOLINT
+ {{422, 640, kSentinel}}, {{423, 424, kSentinel}}, {{425, 643, kSentinel}}, {{428, 429, kSentinel}}, // NOLINT
+ {{430, 648, kSentinel}}, {{431, 432, kSentinel}}, {{433, 650, kSentinel}}, {{434, 651, kSentinel}}, // NOLINT
+ {{435, 436, kSentinel}}, {{437, 438, kSentinel}}, {{439, 658, kSentinel}}, {{440, 441, kSentinel}}, // NOLINT
+ {{444, 445, kSentinel}}, {{447, 503, kSentinel}}, {{452, 453, 454, kSentinel}}, {{455, 456, 457, kSentinel}}, // NOLINT
+ {{458, 459, 460, kSentinel}}, {{461, 462, kSentinel}}, {{463, 464, kSentinel}}, {{465, 466, kSentinel}}, // NOLINT
+ {{467, 468, kSentinel}}, {{469, 470, kSentinel}}, {{471, 472, kSentinel}}, {{473, 474, kSentinel}}, // NOLINT
+ {{475, 476, kSentinel}}, {{478, 479, kSentinel}}, {{480, 481, kSentinel}}, {{482, 483, kSentinel}}, // NOLINT
+ {{484, 485, kSentinel}}, {{486, 487, kSentinel}}, {{488, 489, kSentinel}}, {{490, 491, kSentinel}}, // NOLINT
+ {{492, 493, kSentinel}}, {{494, 495, kSentinel}}, {{497, 498, 499, kSentinel}}, {{500, 501, kSentinel}}, // NOLINT
+ {{504, 505, kSentinel}}, {{506, 507, kSentinel}}, {{508, 509, kSentinel}}, {{510, 511, kSentinel}}, // NOLINT
+ {{512, 513, kSentinel}}, {{514, 515, kSentinel}}, {{516, 517, kSentinel}}, {{518, 519, kSentinel}}, // NOLINT
+ {{520, 521, kSentinel}}, {{522, 523, kSentinel}}, {{524, 525, kSentinel}}, {{526, 527, kSentinel}}, // NOLINT
+ {{528, 529, kSentinel}}, {{530, 531, kSentinel}}, {{532, 533, kSentinel}}, {{534, 535, kSentinel}}, // NOLINT
+ {{536, 537, kSentinel}}, {{538, 539, kSentinel}}, {{540, 541, kSentinel}}, {{542, 543, kSentinel}}, // NOLINT
+ {{546, 547, kSentinel}}, {{548, 549, kSentinel}}, {{550, 551, kSentinel}}, {{552, 553, kSentinel}}, // NOLINT
+ {{554, 555, kSentinel}}, {{556, 557, kSentinel}}, {{558, 559, kSentinel}}, {{560, 561, kSentinel}}, // NOLINT
+ {{562, 563, kSentinel}}, {{570, 11365, kSentinel}}, {{571, 572, kSentinel}}, {{574, 11366, kSentinel}}, // NOLINT
+ {{577, 578, kSentinel}}, {{580, 649, kSentinel}}, {{581, 652, kSentinel}}, {{582, 583, kSentinel}}, // NOLINT
+ {{584, 585, kSentinel}}, {{586, 587, kSentinel}}, {{588, 589, kSentinel}}, {{590, 591, kSentinel}}, // NOLINT
+ {{619, 11362, kSentinel}}, {{637, 11364, kSentinel}}, {{837, 921, 953, 8126}}, {{891, 1021, kSentinel}}, // NOLINT
+ {{893, 1023, kSentinel}}, {{902, 940, kSentinel}}, {{904, 941, kSentinel}}, {{906, 943, kSentinel}}, // NOLINT
+ {{908, 972, kSentinel}}, {{910, 973, kSentinel}}, {{911, 974, kSentinel}}, {{913, 945, kSentinel}}, // NOLINT
+ {{914, 946, 976, kSentinel}}, {{915, 947, kSentinel}}, {{916, 948, kSentinel}}, {{917, 949, 1013, kSentinel}}, // NOLINT
+ {{918, 950, kSentinel}}, {{919, 951, kSentinel}}, {{920, 952, 977, kSentinel}}, {{922, 954, 1008, kSentinel}}, // NOLINT
+ {{923, 955, kSentinel}}, {{925, 957, kSentinel}}, {{927, 959, kSentinel}}, {{928, 960, 982, kSentinel}}, // NOLINT
+ {{929, 961, 1009, kSentinel}}, {{931, 962, 963, kSentinel}}, {{932, 964, kSentinel}}, {{933, 965, kSentinel}}, // NOLINT
+ {{934, 966, 981, kSentinel}}, {{935, 967, kSentinel}}, {{939, 971, kSentinel}}, {{984, 985, kSentinel}}, // NOLINT
+ {{986, 987, kSentinel}}, {{988, 989, kSentinel}}, {{990, 991, kSentinel}}, {{992, 993, kSentinel}}, // NOLINT
+ {{994, 995, kSentinel}}, {{996, 997, kSentinel}}, {{998, 999, kSentinel}}, {{1000, 1001, kSentinel}}, // NOLINT
+ {{1002, 1003, kSentinel}}, {{1004, 1005, kSentinel}}, {{1006, 1007, kSentinel}}, {{1010, 1017, kSentinel}}, // NOLINT
+ {{1015, 1016, kSentinel}}, {{1018, 1019, kSentinel}}, {{1024, 1104, kSentinel}}, {{1039, 1119, kSentinel}}, // NOLINT
+ {{1040, 1072, kSentinel}}, {{1071, 1103, kSentinel}}, {{1120, 1121, kSentinel}}, {{1122, 1123, kSentinel}}, // NOLINT
+ {{1124, 1125, kSentinel}}, {{1126, 1127, kSentinel}}, {{1128, 1129, kSentinel}}, {{1130, 1131, kSentinel}}, // NOLINT
+ {{1132, 1133, kSentinel}}, {{1134, 1135, kSentinel}}, {{1136, 1137, kSentinel}}, {{1138, 1139, kSentinel}}, // NOLINT
+ {{1140, 1141, kSentinel}}, {{1142, 1143, kSentinel}}, {{1144, 1145, kSentinel}}, {{1146, 1147, kSentinel}}, // NOLINT
+ {{1148, 1149, kSentinel}}, {{1150, 1151, kSentinel}}, {{1152, 1153, kSentinel}}, {{1162, 1163, kSentinel}}, // NOLINT
+ {{1164, 1165, kSentinel}}, {{1166, 1167, kSentinel}}, {{1168, 1169, kSentinel}}, {{1170, 1171, kSentinel}}, // NOLINT
+ {{1172, 1173, kSentinel}}, {{1174, 1175, kSentinel}}, {{1176, 1177, kSentinel}}, {{1178, 1179, kSentinel}}, // NOLINT
+ {{1180, 1181, kSentinel}}, {{1182, 1183, kSentinel}}, {{1184, 1185, kSentinel}}, {{1186, 1187, kSentinel}}, // NOLINT
+ {{1188, 1189, kSentinel}}, {{1190, 1191, kSentinel}}, {{1192, 1193, kSentinel}}, {{1194, 1195, kSentinel}}, // NOLINT
+ {{1196, 1197, kSentinel}}, {{1198, 1199, kSentinel}}, {{1200, 1201, kSentinel}}, {{1202, 1203, kSentinel}}, // NOLINT
+ {{1204, 1205, kSentinel}}, {{1206, 1207, kSentinel}}, {{1208, 1209, kSentinel}}, {{1210, 1211, kSentinel}}, // NOLINT
+ {{1212, 1213, kSentinel}}, {{1214, 1215, kSentinel}}, {{1216, 1231, kSentinel}}, {{1217, 1218, kSentinel}}, // NOLINT
+ {{1219, 1220, kSentinel}}, {{1221, 1222, kSentinel}}, {{1223, 1224, kSentinel}}, {{1225, 1226, kSentinel}}, // NOLINT
+ {{1227, 1228, kSentinel}}, {{1229, 1230, kSentinel}}, {{1232, 1233, kSentinel}}, {{1234, 1235, kSentinel}}, // NOLINT
+ {{1236, 1237, kSentinel}}, {{1238, 1239, kSentinel}}, {{1240, 1241, kSentinel}}, {{1242, 1243, kSentinel}}, // NOLINT
+ {{1244, 1245, kSentinel}}, {{1246, 1247, kSentinel}}, {{1248, 1249, kSentinel}}, {{1250, 1251, kSentinel}}, // NOLINT
+ {{1252, 1253, kSentinel}}, {{1254, 1255, kSentinel}}, {{1256, 1257, kSentinel}}, {{1258, 1259, kSentinel}}, // NOLINT
+ {{1260, 1261, kSentinel}}, {{1262, 1263, kSentinel}}, {{1264, 1265, kSentinel}}, {{1266, 1267, kSentinel}}, // NOLINT
+ {{1268, 1269, kSentinel}}, {{1270, 1271, kSentinel}}, {{1272, 1273, kSentinel}}, {{1274, 1275, kSentinel}}, // NOLINT
+ {{1276, 1277, kSentinel}}, {{1278, 1279, kSentinel}}, {{1280, 1281, kSentinel}}, {{1282, 1283, kSentinel}}, // NOLINT
+ {{1284, 1285, kSentinel}}, {{1286, 1287, kSentinel}}, {{1288, 1289, kSentinel}}, {{1290, 1291, kSentinel}}, // NOLINT
+ {{1292, 1293, kSentinel}}, {{1294, 1295, kSentinel}}, {{1296, 1297, kSentinel}}, {{1298, 1299, kSentinel}}, // NOLINT
+ {{1329, 1377, kSentinel}}, {{1366, 1414, kSentinel}}, {{4256, 11520, kSentinel}}, {{4293, 11557, kSentinel}}, // NOLINT
+ {{7549, 11363, kSentinel}}, {{7680, 7681, kSentinel}}, {{7682, 7683, kSentinel}}, {{7684, 7685, kSentinel}}, // NOLINT
+ {{7686, 7687, kSentinel}}, {{7688, 7689, kSentinel}}, {{7690, 7691, kSentinel}}, {{7692, 7693, kSentinel}}, // NOLINT
+ {{7694, 7695, kSentinel}}, {{7696, 7697, kSentinel}}, {{7698, 7699, kSentinel}}, {{7700, 7701, kSentinel}}, // NOLINT
+ {{7702, 7703, kSentinel}}, {{7704, 7705, kSentinel}}, {{7706, 7707, kSentinel}}, {{7708, 7709, kSentinel}}, // NOLINT
+ {{7710, 7711, kSentinel}}, {{7712, 7713, kSentinel}}, {{7714, 7715, kSentinel}}, {{7716, 7717, kSentinel}}, // NOLINT
+ {{7718, 7719, kSentinel}}, {{7720, 7721, kSentinel}}, {{7722, 7723, kSentinel}}, {{7724, 7725, kSentinel}}, // NOLINT
+ {{7726, 7727, kSentinel}}, {{7728, 7729, kSentinel}}, {{7730, 7731, kSentinel}}, {{7732, 7733, kSentinel}}, // NOLINT
+ {{7734, 7735, kSentinel}}, {{7736, 7737, kSentinel}}, {{7738, 7739, kSentinel}}, {{7740, 7741, kSentinel}}, // NOLINT
+ {{7742, 7743, kSentinel}}, {{7744, 7745, kSentinel}}, {{7746, 7747, kSentinel}}, {{7748, 7749, kSentinel}}, // NOLINT
+ {{7750, 7751, kSentinel}}, {{7752, 7753, kSentinel}}, {{7754, 7755, kSentinel}}, {{7756, 7757, kSentinel}}, // NOLINT
+ {{7758, 7759, kSentinel}}, {{7760, 7761, kSentinel}}, {{7762, 7763, kSentinel}}, {{7764, 7765, kSentinel}}, // NOLINT
+ {{7766, 7767, kSentinel}}, {{7768, 7769, kSentinel}}, {{7770, 7771, kSentinel}}, {{7772, 7773, kSentinel}}, // NOLINT
+ {{7774, 7775, kSentinel}}, {{7776, 7777, 7835, kSentinel}}, {{7778, 7779, kSentinel}}, {{7780, 7781, kSentinel}}, // NOLINT
+ {{7782, 7783, kSentinel}}, {{7784, 7785, kSentinel}}, {{7786, 7787, kSentinel}}, {{7788, 7789, kSentinel}}, // NOLINT
+ {{7790, 7791, kSentinel}}, {{7792, 7793, kSentinel}}, {{7794, 7795, kSentinel}}, {{7796, 7797, kSentinel}}, // NOLINT
+ {{7798, 7799, kSentinel}}, {{7800, 7801, kSentinel}}, {{7802, 7803, kSentinel}}, {{7804, 7805, kSentinel}}, // NOLINT
+ {{7806, 7807, kSentinel}}, {{7808, 7809, kSentinel}}, {{7810, 7811, kSentinel}}, {{7812, 7813, kSentinel}}, // NOLINT
+ {{7814, 7815, kSentinel}}, {{7816, 7817, kSentinel}}, {{7818, 7819, kSentinel}}, {{7820, 7821, kSentinel}}, // NOLINT
+ {{7822, 7823, kSentinel}}, {{7824, 7825, kSentinel}}, {{7826, 7827, kSentinel}}, {{7828, 7829, kSentinel}}, // NOLINT
+ {{7840, 7841, kSentinel}}, {{7842, 7843, kSentinel}}, {{7844, 7845, kSentinel}}, {{7846, 7847, kSentinel}}, // NOLINT
+ {{7848, 7849, kSentinel}}, {{7850, 7851, kSentinel}}, {{7852, 7853, kSentinel}}, {{7854, 7855, kSentinel}}, // NOLINT
+ {{7856, 7857, kSentinel}}, {{7858, 7859, kSentinel}}, {{7860, 7861, kSentinel}}, {{7862, 7863, kSentinel}}, // NOLINT
+ {{7864, 7865, kSentinel}}, {{7866, 7867, kSentinel}}, {{7868, 7869, kSentinel}}, {{7870, 7871, kSentinel}}, // NOLINT
+ {{7872, 7873, kSentinel}}, {{7874, 7875, kSentinel}}, {{7876, 7877, kSentinel}}, {{7878, 7879, kSentinel}}, // NOLINT
+ {{7880, 7881, kSentinel}}, {{7882, 7883, kSentinel}}, {{7884, 7885, kSentinel}}, {{7886, 7887, kSentinel}}, // NOLINT
+ {{7888, 7889, kSentinel}}, {{7890, 7891, kSentinel}}, {{7892, 7893, kSentinel}}, {{7894, 7895, kSentinel}}, // NOLINT
+ {{7896, 7897, kSentinel}}, {{7898, 7899, kSentinel}}, {{7900, 7901, kSentinel}}, {{7902, 7903, kSentinel}}, // NOLINT
+ {{7904, 7905, kSentinel}}, {{7906, 7907, kSentinel}}, {{7908, 7909, kSentinel}}, {{7910, 7911, kSentinel}}, // NOLINT
+ {{7912, 7913, kSentinel}}, {{7914, 7915, kSentinel}}, {{7916, 7917, kSentinel}}, {{7918, 7919, kSentinel}}, // NOLINT
+ {{7920, 7921, kSentinel}}, {{7922, 7923, kSentinel}}, {{7924, 7925, kSentinel}}, {{7926, 7927, kSentinel}}, // NOLINT
+ {{7928, 7929, kSentinel}}, {{7936, 7944, kSentinel}}, {{7943, 7951, kSentinel}}, {{7952, 7960, kSentinel}}, // NOLINT
+ {{7957, 7965, kSentinel}}, {{7968, 7976, kSentinel}}, {{7975, 7983, kSentinel}}, {{7984, 7992, kSentinel}}, // NOLINT
+ {{7991, 7999, kSentinel}}, {{8000, 8008, kSentinel}}, {{8005, 8013, kSentinel}}, {{8017, 8025, kSentinel}}, // NOLINT
+ {{8019, 8027, kSentinel}}, {{8021, 8029, kSentinel}}, {{8023, 8031, kSentinel}}, {{8032, 8040, kSentinel}}, // NOLINT
+ {{8039, 8047, kSentinel}}, {{8048, 8122, kSentinel}}, {{8049, 8123, kSentinel}}, {{8050, 8136, kSentinel}}, // NOLINT
+ {{8053, 8139, kSentinel}}, {{8054, 8154, kSentinel}}, {{8055, 8155, kSentinel}}, {{8056, 8184, kSentinel}}, // NOLINT
+ {{8057, 8185, kSentinel}}, {{8058, 8170, kSentinel}}, {{8059, 8171, kSentinel}}, {{8060, 8186, kSentinel}}, // NOLINT
+ {{8061, 8187, kSentinel}}, {{8112, 8120, kSentinel}}, {{8113, 8121, kSentinel}}, {{8144, 8152, kSentinel}}, // NOLINT
+ {{8145, 8153, kSentinel}}, {{8160, 8168, kSentinel}}, {{8161, 8169, kSentinel}}, {{8165, 8172, kSentinel}}, // NOLINT
+ {{kSentinel}} }; // NOLINT
static const uint16_t kEcma262UnCanonicalizeTable0Size = 945; // NOLINT
static const int32_t kEcma262UnCanonicalizeTable0[1890] = {
1073741889, 1, 90, 5, 1073741921, 1, 122, 5, 181, 9, 1073742016, 13, 214, 17, 1073742040, 21, // NOLINT
@@ -1453,7 +1455,7 @@ static const MultiCharacterSpecialCase<2> kEcma262UnCanonicalizeMultiStrings1[71
{{11468, 11469}}, {{11470, 11471}}, {{11472, 11473}}, {{11474, 11475}}, // NOLINT
{{11476, 11477}}, {{11478, 11479}}, {{11480, 11481}}, {{11482, 11483}}, // NOLINT
{{11484, 11485}}, {{11486, 11487}}, {{11488, 11489}}, {{11490, 11491}}, // NOLINT
- {{4256, 11520}}, {{4293, 11557}}, {{-1}} }; // NOLINT
+ {{4256, 11520}}, {{4293, 11557}}, {{kSentinel}} }; // NOLINT
static const uint16_t kEcma262UnCanonicalizeTable1Size = 133; // NOLINT
static const int32_t kEcma262UnCanonicalizeTable1[266] = {
306, 1, 334, 1, 1073742176, 5, 367, 9, 1073742192, 5, 383, 9, 387, 13, 388, 13, // NOLINT
@@ -1475,7 +1477,7 @@ static const int32_t kEcma262UnCanonicalizeTable1[266] = {
3297, 265, 3298, 269, 3299, 269, 1073745152, 273, 3365, 277 }; // NOLINT
static const uint16_t kEcma262UnCanonicalizeMultiStrings1Size = 71; // NOLINT
static const MultiCharacterSpecialCase<2> kEcma262UnCanonicalizeMultiStrings7[3] = { // NOLINT
- {{65313, 65345}}, {{65338, 65370}}, {{-1}} }; // NOLINT
+ {{65313, 65345}}, {{65338, 65370}}, {{kSentinel}} }; // NOLINT
static const uint16_t kEcma262UnCanonicalizeTable7Size = 4; // NOLINT
static const int32_t kEcma262UnCanonicalizeTable7[8] = {
1073749793, 1, 7994, 5, 1073749825, 1, 8026, 5 }; // NOLINT
@@ -1512,7 +1514,7 @@ int Ecma262UnCanonicalize::Convert(uchar c,
}
static const MultiCharacterSpecialCase<1> kCanonicalizationRangeMultiStrings0[1] = { // NOLINT
- {{-1}} }; // NOLINT
+ {{kSentinel}} }; // NOLINT
static const uint16_t kCanonicalizationRangeTable0Size = 70; // NOLINT
static const int32_t kCanonicalizationRangeTable0[140] = {
1073741889, 100, 90, 0, 1073741921, 100, 122, 0, 1073742016, 88, 214, 0, 1073742040, 24, 222, 0, // NOLINT
@@ -1526,14 +1528,14 @@ static const int32_t kCanonicalizationRangeTable0[140] = {
1073749864, 28, 8047, 0, 1073749874, 12, 8053, 0, 1073749960, 12, 8139, 0 }; // NOLINT
static const uint16_t kCanonicalizationRangeMultiStrings0Size = 1; // NOLINT
static const MultiCharacterSpecialCase<1> kCanonicalizationRangeMultiStrings1[1] = { // NOLINT
- {{-1}} }; // NOLINT
+ {{kSentinel}} }; // NOLINT
static const uint16_t kCanonicalizationRangeTable1Size = 14; // NOLINT
static const int32_t kCanonicalizationRangeTable1[28] = {
1073742176, 60, 367, 0, 1073742192, 60, 383, 0, 1073743030, 100, 1231, 0, 1073743056, 100, 1257, 0, // NOLINT
1073744896, 184, 3118, 0, 1073744944, 184, 3166, 0, 1073745152, 148, 3365, 0 }; // NOLINT
static const uint16_t kCanonicalizationRangeMultiStrings1Size = 1; // NOLINT
static const MultiCharacterSpecialCase<1> kCanonicalizationRangeMultiStrings7[1] = { // NOLINT
- {{-1}} }; // NOLINT
+ {{kSentinel}} }; // NOLINT
static const uint16_t kCanonicalizationRangeTable7Size = 4; // NOLINT
static const int32_t kCanonicalizationRangeTable7[8] = {
1073749793, 100, 7994, 0, 1073749825, 100, 8026, 0 }; // NOLINT
diff --git a/deps/v8/src/utils.h b/deps/v8/src/utils.h
index 651ef50f4e..21e70d758e 100644
--- a/deps/v8/src/utils.h
+++ b/deps/v8/src/utils.h
@@ -28,10 +28,6 @@
#ifndef V8_UTILS_H_
#define V8_UTILS_H_
-#ifdef __MINGW32__
-#include <stdarg.h>
-#endif
-
#include <stdlib.h>
#include <string.h>
@@ -534,6 +530,24 @@ class Collector {
}
+ // Add a contiguous block of elements and return a vector backed
+ // by the added block.
+ // A basic Collector will keep this vector valid as long as the Collector
+ // is alive.
+ inline Vector<T> AddBlock(Vector<const T> source) {
+ if (source.length() > current_chunk_.length() - index_) {
+ Grow(source.length());
+ }
+ T* position = current_chunk_.start() + index_;
+ index_ += source.length();
+ size_ += source.length();
+ for (int i = 0; i < source.length(); i++) {
+ position[i] = source[i];
+ }
+ return Vector<T>(position, source.length());
+ }
+
+
// Write the contents of the collector into the provided vector.
void WriteTo(Vector<T> destination) {
ASSERT(size_ <= destination.length());
@@ -758,7 +772,7 @@ inline Dest BitCast(const Source& source) {
}
template <class Dest, class Source>
-inline Dest BitCast(Source*& source) {
+inline Dest BitCast(Source* source) {
return BitCast<Dest>(reinterpret_cast<uintptr_t>(source));
}
diff --git a/deps/v8/src/v8-counters.h b/deps/v8/src/v8-counters.h
index fa5d581eab..aa30e4e150 100644
--- a/deps/v8/src/v8-counters.h
+++ b/deps/v8/src/v8-counters.h
@@ -249,15 +249,7 @@ namespace internal {
SC(smi_checks_removed, V8.SmiChecksRemoved) \
SC(map_checks_removed, V8.MapChecksRemoved) \
SC(quote_json_char_count, V8.QuoteJsonCharacterCount) \
- SC(quote_json_char_recount, V8.QuoteJsonCharacterReCount) \
- SC(instance_of, V8.InstanceOf) \
- SC(instance_of_cache, V8.InstanceOfCache) \
- SC(instance_of_stub_true, V8.InstanceOfStubTrue) \
- SC(instance_of_stub_false, V8.InstanceOfStubFalse) \
- SC(instance_of_stub_false_null, V8.InstanceOfStubFalseNull) \
- SC(instance_of_stub_false_string, V8.InstanceOfStubFalseString) \
- SC(instance_of_full, V8.InstanceOfFull) \
- SC(instance_of_slow, V8.InstanceOfSlow)
+ SC(quote_json_char_recount, V8.QuoteJsonCharacterReCount)
// This file contains all the v8 counters that are in use.
diff --git a/deps/v8/src/v8globals.h b/deps/v8/src/v8globals.h
index 65bbf6ab24..3f27114bec 100644
--- a/deps/v8/src/v8globals.h
+++ b/deps/v8/src/v8globals.h
@@ -77,7 +77,8 @@ const Address kHandleZapValue =
reinterpret_cast<Address>(V8_UINT64_C(0x1baddead0baddead));
const Address kFromSpaceZapValue =
reinterpret_cast<Address>(V8_UINT64_C(0x1beefdad0beefdad));
-const uint64_t kDebugZapValue = 0xbadbaddbbadbaddb;
+const uint64_t kDebugZapValue = V8_UINT64_C(0xbadbaddbbadbaddb);
+const uint64_t kSlotsZapValue = V8_UINT64_C(0xbeefdeadbeefdeed);
#else
const Address kZapValue = reinterpret_cast<Address>(0xdeadbeed);
const Address kHandleZapValue = reinterpret_cast<Address>(0xbaddead);
diff --git a/deps/v8/src/v8natives.js b/deps/v8/src/v8natives.js
index 9fd2162686..233f8b4de9 100644
--- a/deps/v8/src/v8natives.js
+++ b/deps/v8/src/v8natives.js
@@ -83,7 +83,7 @@ function GlobalIsNaN(number) {
// ECMA 262 - 15.1.5
function GlobalIsFinite(number) {
- if (!IS_NUMBER(number)) number = ToNumber(number);
+ if (!IS_NUMBER(number)) number = NonNumberToNumber(number);
// NaN - NaN == NaN, Infinity - Infinity == NaN, -Infinity - -Infinity == NaN.
return %_IsSmi(number) || number - number == 0;
@@ -896,9 +896,14 @@ SetupObject();
function BooleanToString() {
// NOTE: Both Boolean objects and values can enter here as
// 'this'. This is not as dictated by ECMA-262.
- if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this))
- throw new $TypeError('Boolean.prototype.toString is not generic');
- return ToString(%_ValueOf(this));
+ var b = this;
+ if (!IS_BOOLEAN(b)) {
+ if (!IS_BOOLEAN_WRAPPER(b)) {
+ throw new $TypeError('Boolean.prototype.toString is not generic');
+ }
+ b = %_ValueOf(b);
+ }
+ return b ? 'true' : 'false';
}
@@ -951,7 +956,7 @@ function NumberToString(radix) {
}
// Fast case: Convert number in radix 10.
if (IS_UNDEFINED(radix) || radix === 10) {
- return ToString(number);
+ return %_NumberToString(number);
}
// Convert the radix to an integer and check the range.
@@ -1150,11 +1155,8 @@ function NewFunction(arg1) { // length == 1
var p = '';
if (n > 1) {
p = new $Array(n - 1);
- // Explicitly convert all parameters to strings.
- // Array.prototype.join replaces null with empty strings which is
- // not appropriate.
- for (var i = 0; i < n - 1; i++) p[i] = ToString(%_Arguments(i));
- p = p.join(',');
+ for (var i = 0; i < n - 1; i++) p[i] = %_Arguments(i);
+ p = Join(p, n - 1, ',', NonStringToString);
// If the formal parameters string include ) - an illegal
// character - it may make the combined function expression
// compile. We avoid this problem by checking for this early on.
diff --git a/deps/v8/src/v8threads.cc b/deps/v8/src/v8threads.cc
index b6e656d25a..8a5fe6902d 100644
--- a/deps/v8/src/v8threads.cc
+++ b/deps/v8/src/v8threads.cc
@@ -380,7 +380,8 @@ ContextSwitcher* ContextSwitcher::singleton_ = NULL;
ContextSwitcher::ContextSwitcher(int every_n_ms)
- : keep_going_(true),
+ : Thread("v8:CtxtSwitcher"),
+ keep_going_(true),
sleep_ms_(every_n_ms) {
}
diff --git a/deps/v8/src/v8utils.h b/deps/v8/src/v8utils.h
index b000d14409..e9623be62e 100644
--- a/deps/v8/src/v8utils.h
+++ b/deps/v8/src/v8utils.h
@@ -29,7 +29,7 @@
#define V8_V8UTILS_H_
#include "utils.h"
-#include <stdarg.h>
+#include "platform.h" // For va_list on Solaris.
namespace v8 {
namespace internal {
diff --git a/deps/v8/src/variables.cc b/deps/v8/src/variables.cc
index c1440b7f6f..7f580fc6f0 100644
--- a/deps/v8/src/variables.cc
+++ b/deps/v8/src/variables.cc
@@ -98,6 +98,12 @@ bool Variable::IsStackLocal() const {
}
+bool Variable::IsContextSlot() const {
+ Slot* s = AsSlot();
+ return s != NULL && s->type() == Slot::CONTEXT;
+}
+
+
Variable::Variable(Scope* scope,
Handle<String> name,
Mode mode,
diff --git a/deps/v8/src/variables.h b/deps/v8/src/variables.h
index 9e460f7619..882a52ed82 100644
--- a/deps/v8/src/variables.h
+++ b/deps/v8/src/variables.h
@@ -138,6 +138,9 @@ class Variable: public ZoneObject {
bool is_accessed_from_inner_scope() const {
return is_accessed_from_inner_scope_;
}
+ void MarkAsAccessedFromInnerScope() {
+ is_accessed_from_inner_scope_ = true;
+ }
bool is_used() { return is_used_; }
void set_is_used(bool flag) { is_used_ = flag; }
@@ -148,6 +151,7 @@ class Variable: public ZoneObject {
bool IsStackAllocated() const;
bool IsParameter() const; // Includes 'this'.
bool IsStackLocal() const;
+ bool IsContextSlot() const;
bool is_dynamic() const {
return (mode_ == DYNAMIC ||
@@ -175,6 +179,7 @@ class Variable: public ZoneObject {
}
Expression* rewrite() const { return rewrite_; }
+ void set_rewrite(Expression* expr) { rewrite_ = expr; }
StaticType* type() { return &type_; }
@@ -197,8 +202,6 @@ class Variable: public ZoneObject {
// Code generation.
// rewrite_ is usually a Slot or a Property, but may be any expression.
Expression* rewrite_;
-
- friend class Scope; // Has explicit access to rewrite_.
};
diff --git a/deps/v8/src/version.cc b/deps/v8/src/version.cc
index d2c0960508..a77d85f75f 100644
--- a/deps/v8/src/version.cc
+++ b/deps/v8/src/version.cc
@@ -34,8 +34,8 @@
// cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 3
#define MINOR_VERSION 0
-#define BUILD_NUMBER 4
-#define PATCH_LEVEL 1
+#define BUILD_NUMBER 8
+#define PATCH_LEVEL 0
#define CANDIDATE_VERSION false
// Define SONAME to have the SCons build the put a specific SONAME into the
diff --git a/deps/v8/src/x64/assembler-x64.cc b/deps/v8/src/x64/assembler-x64.cc
index 8f15f23ba7..9060d57586 100644
--- a/deps/v8/src/x64/assembler-x64.cc
+++ b/deps/v8/src/x64/assembler-x64.cc
@@ -74,7 +74,7 @@ void CpuFeatures::Probe(bool portable) {
__ xor_(rax, rdx); // Different if CPUID is supported.
__ j(not_zero, &cpuid);
- // CPUID not supported. Clear the supported features in edx:eax.
+ // CPUID not supported. Clear the supported features in rax.
__ xor_(rax, rax);
__ jmp(&done);
@@ -186,6 +186,20 @@ void RelocInfo::PatchCode(byte* instructions, int instruction_count) {
CPU::FlushICache(pc_, instruction_count);
}
+
+// -----------------------------------------------------------------------------
+// Register constants.
+
+const int Register::registerCodeByAllocationIndex[kNumAllocatableRegisters] = {
+ // rax, rbx, rdx, rcx, rdi, r8, r9, r11, r14, r12
+ 0, 3, 2, 1, 7, 8, 9, 11, 14, 12
+};
+
+const int Register::allocationIndexByRegisterCode[kNumRegisters] = {
+ 0, 3, 2, 1, -1, -1, -1, 4, 5, 6, -1, 7, 9, -1, 8, -1
+};
+
+
// -----------------------------------------------------------------------------
// Implementation of Operand
diff --git a/deps/v8/src/x64/assembler-x64.h b/deps/v8/src/x64/assembler-x64.h
index fde88df776..fa2f4c3518 100644
--- a/deps/v8/src/x64/assembler-x64.h
+++ b/deps/v8/src/x64/assembler-x64.h
@@ -98,19 +98,29 @@ struct Register {
static const int kNumRegisters = 16;
static const int kNumAllocatableRegisters = 10;
+ static int ToAllocationIndex(Register reg) {
+ return allocationIndexByRegisterCode[reg.code()];
+ }
+
+ static Register FromAllocationIndex(int index) {
+ ASSERT(index >= 0 && index < kNumAllocatableRegisters);
+ Register result = { registerCodeByAllocationIndex[index] };
+ return result;
+ }
+
static const char* AllocationIndexToString(int index) {
ASSERT(index >= 0 && index < kNumAllocatableRegisters);
const char* const names[] = {
"rax",
- "rcx",
- "rdx",
"rbx",
+ "rdx",
+ "rcx",
"rdi",
"r8",
"r9",
"r11",
- "r12",
- "r14"
+ "r14",
+ "r12"
};
return names[index];
}
@@ -143,6 +153,9 @@ struct Register {
// Unfortunately we can't make this private in a struct when initializing
// by assignment.
int code_;
+ private:
+ static const int registerCodeByAllocationIndex[kNumAllocatableRegisters];
+ static const int allocationIndexByRegisterCode[kNumRegisters];
};
const Register rax = { 0 };
@@ -173,6 +186,12 @@ struct XMMRegister {
return reg.code() - 1;
}
+ static XMMRegister FromAllocationIndex(int index) {
+ ASSERT(0 <= index && index < kNumAllocatableRegisters);
+ XMMRegister result = { index + 1 };
+ return result;
+ }
+
static const char* AllocationIndexToString(int index) {
ASSERT(index >= 0 && index < kNumAllocatableRegisters);
const char* const names[] = {
@@ -196,6 +215,7 @@ struct XMMRegister {
}
bool is_valid() const { return 0 <= code_ && code_ < kNumRegisters; }
+ bool is(XMMRegister reg) const { return code_ == reg.code_; }
int code() const {
ASSERT(is_valid());
return code_;
@@ -536,6 +556,8 @@ class Assembler : public Malloced {
// The debug break slot must be able to contain a call instruction.
static const int kDebugBreakSlotLength = kCallInstructionLength;
+ // One byte opcode for test eax,0xXXXXXXXX.
+ static const byte kTestEaxByte = 0xA9;
// ---------------------------------------------------------------------------
// Code generation
diff --git a/deps/v8/src/x64/builtins-x64.cc b/deps/v8/src/x64/builtins-x64.cc
index 456d0765b9..d738261a1b 100644
--- a/deps/v8/src/x64/builtins-x64.cc
+++ b/deps/v8/src/x64/builtins-x64.cc
@@ -422,7 +422,7 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
// [rsp+0x20] : argv
// Clear the context before we push it when entering the JS frame.
- __ xor_(rsi, rsi);
+ __ Set(rsi, 0);
__ EnterInternalFrame();
// Load the function context into rsi.
@@ -451,7 +451,7 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
// rdi : function
// Clear the context before we push it when entering the JS frame.
- __ xor_(rsi, rsi);
+ __ Set(rsi, 0);
// Enter an internal frame.
__ EnterInternalFrame();
@@ -479,7 +479,7 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
// Register rbx points to array of pointers to handle locations.
// Push the values of these handles.
Label loop, entry;
- __ xor_(rcx, rcx); // Set loop variable to 0.
+ __ Set(rcx, 0); // Set loop variable to 0.
__ jmp(&entry);
__ bind(&loop);
__ movq(kScratchRegister, Operand(rbx, rcx, times_pointer_size, 0));
@@ -668,7 +668,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
// become the receiver.
__ bind(&non_function);
__ movq(Operand(rsp, rax, times_pointer_size, 0), rdi);
- __ xor_(rdi, rdi);
+ __ Set(rdi, 0);
// 4. Shift arguments and return address one slot down on the stack
// (overwriting the original receiver). Adjust argument count to make
@@ -689,7 +689,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
{ Label function;
__ testq(rdi, rdi);
__ j(not_zero, &function);
- __ xor_(rbx, rbx);
+ __ Set(rbx, 0);
__ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
__ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
RelocInfo::CODE_TARGET);
diff --git a/deps/v8/src/x64/code-stubs-x64.cc b/deps/v8/src/x64/code-stubs-x64.cc
index c3eb5bf440..59522d22f9 100644
--- a/deps/v8/src/x64/code-stubs-x64.cc
+++ b/deps/v8/src/x64/code-stubs-x64.cc
@@ -104,7 +104,7 @@ void FastNewContextStub::Generate(MacroAssembler* masm) {
__ Move(FieldOperand(rax, FixedArray::kLengthOffset), Smi::FromInt(length));
// Setup the fixed slots.
- __ xor_(rbx, rbx); // Set to NULL.
+ __ Set(rbx, 0); // Set to NULL.
__ movq(Operand(rax, Context::SlotOffset(Context::CLOSURE_INDEX)), rcx);
__ movq(Operand(rax, Context::SlotOffset(Context::FCONTEXT_INDEX)), rax);
__ movq(Operand(rax, Context::SlotOffset(Context::PREVIOUS_INDEX)), rbx);
@@ -250,7 +250,7 @@ void ToBooleanStub::Generate(MacroAssembler* masm) {
__ movq(rax, Immediate(1));
__ ret(1 * kPointerSize);
__ bind(&false_result);
- __ xor_(rax, rax);
+ __ Set(rax, 0);
__ ret(1 * kPointerSize);
}
@@ -988,8 +988,195 @@ Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
Handle<Code> GetTypeRecordingBinaryOpStub(int key,
TRBinaryOpIC::TypeInfo type_info,
TRBinaryOpIC::TypeInfo result_type_info) {
+ TypeRecordingBinaryOpStub stub(key, type_info, result_type_info);
+ return stub.GetCode();
+}
+
+
+void TypeRecordingBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
+ __ pop(rcx); // Save return address.
+ __ push(rdx);
+ __ push(rax);
+ // Left and right arguments are now on top.
+ // Push this stub's key. Although the operation and the type info are
+ // encoded into the key, the encoding is opaque, so push them too.
+ __ Push(Smi::FromInt(MinorKey()));
+ __ Push(Smi::FromInt(op_));
+ __ Push(Smi::FromInt(operands_type_));
+
+ __ push(rcx); // Push return address.
+
+ // Patch the caller to an appropriate specialized stub and return the
+ // operation result to the caller of the stub.
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kTypeRecordingBinaryOp_Patch)),
+ 5,
+ 1);
+}
+
+
+// Prepare for a type transition runtime call when the args are already on
+// the stack, under the return address.
+void TypeRecordingBinaryOpStub::GenerateTypeTransitionWithSavedArgs(
+ MacroAssembler* masm) {
+ __ pop(rcx); // Save return address.
+ // Left and right arguments are already on top of the stack.
+ // Push this stub's key. Although the operation and the type info are
+ // encoded into the key, the encoding is opaque, so push them too.
+ __ Push(Smi::FromInt(MinorKey()));
+ __ Push(Smi::FromInt(op_));
+ __ Push(Smi::FromInt(operands_type_));
+
+ __ push(rcx); // Push return address.
+
+ // Patch the caller to an appropriate specialized stub and return the
+ // operation result to the caller of the stub.
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kTypeRecordingBinaryOp_Patch)),
+ 5,
+ 1);
+}
+
+
+void TypeRecordingBinaryOpStub::Generate(MacroAssembler* masm) {
+ switch (operands_type_) {
+ case TRBinaryOpIC::UNINITIALIZED:
+ GenerateTypeTransition(masm);
+ break;
+ case TRBinaryOpIC::SMI:
+ GenerateSmiStub(masm);
+ break;
+ case TRBinaryOpIC::INT32:
+ GenerateInt32Stub(masm);
+ break;
+ case TRBinaryOpIC::HEAP_NUMBER:
+ GenerateHeapNumberStub(masm);
+ break;
+ case TRBinaryOpIC::STRING:
+ GenerateStringStub(masm);
+ break;
+ case TRBinaryOpIC::GENERIC:
+ GenerateGeneric(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+const char* TypeRecordingBinaryOpStub::GetName() {
+ if (name_ != NULL) return name_;
+ const int kMaxNameLength = 100;
+ name_ = Bootstrapper::AllocateAutoDeletedArray(kMaxNameLength);
+ if (name_ == NULL) return "OOM";
+ const char* op_name = Token::Name(op_);
+ const char* overwrite_name;
+ switch (mode_) {
+ case NO_OVERWRITE: overwrite_name = "Alloc"; break;
+ case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break;
+ case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break;
+ default: overwrite_name = "UnknownOverwrite"; break;
+ }
+
+ OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
+ "TypeRecordingBinaryOpStub_%s_%s_%s",
+ op_name,
+ overwrite_name,
+ TRBinaryOpIC::GetName(operands_type_));
+ return name_;
+}
+
+
+void TypeRecordingBinaryOpStub::GenerateSmiCode(MacroAssembler* masm,
+ Label* slow,
+ SmiCodeGenerateHeapNumberResults allow_heapnumber_results) {
UNIMPLEMENTED();
- return Handle<Code>::null();
+}
+
+
+void TypeRecordingBinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
+ Label call_runtime;
+
+ switch (op_) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ break;
+ case Token::MOD:
+ case Token::BIT_OR:
+ case Token::BIT_AND:
+ case Token::BIT_XOR:
+ case Token::SAR:
+ case Token::SHL:
+ case Token::SHR:
+ GenerateRegisterArgsPush(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ if (result_type_ == TRBinaryOpIC::UNINITIALIZED ||
+ result_type_ == TRBinaryOpIC::SMI) {
+ GenerateSmiCode(masm, &call_runtime, NO_HEAPNUMBER_RESULTS);
+ } else {
+ GenerateSmiCode(masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS);
+ }
+ __ bind(&call_runtime);
+ switch (op_) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ GenerateTypeTransition(masm);
+ break;
+ case Token::MOD:
+ case Token::BIT_OR:
+ case Token::BIT_AND:
+ case Token::BIT_XOR:
+ case Token::SAR:
+ case Token::SHL:
+ case Token::SHR:
+ GenerateTypeTransitionWithSavedArgs(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void TypeRecordingBinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
+ UNIMPLEMENTED();
+}
+
+
+void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
+ UNIMPLEMENTED();
+}
+
+
+void TypeRecordingBinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
+ UNIMPLEMENTED();
+}
+
+
+void TypeRecordingBinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
+ UNIMPLEMENTED();
+}
+
+
+void TypeRecordingBinaryOpStub::GenerateHeapResultAllocation(
+ MacroAssembler* masm,
+ Label* alloc_failure) {
+ UNIMPLEMENTED();
+}
+
+
+void TypeRecordingBinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
+ __ pop(rcx);
+ __ push(rdx);
+ __ push(rax);
+ __ push(rcx);
}
@@ -2572,7 +2759,7 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
// Before returning we restore the context from the frame pointer if not NULL.
// The frame pointer is NULL in the exception handler of a JS entry frame.
- __ xor_(rsi, rsi); // tentatively set context pointer to NULL
+ __ Set(rsi, 0); // Tentatively set context pointer to NULL
NearLabel skip;
__ cmpq(rbp, Immediate(0));
__ j(equal, &skip);
@@ -2756,7 +2943,7 @@ void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
}
// Clear the context pointer.
- __ xor_(rsi, rsi);
+ __ Set(rsi, 0);
// Restore registers from handler.
STATIC_ASSERT(StackHandlerConstants::kNextOffset + kPointerSize ==
diff --git a/deps/v8/src/x64/code-stubs-x64.h b/deps/v8/src/x64/code-stubs-x64.h
index eb7ad267c9..5056f34849 100644
--- a/deps/v8/src/x64/code-stubs-x64.h
+++ b/deps/v8/src/x64/code-stubs-x64.h
@@ -87,7 +87,7 @@ class GenericBinaryOpStub: public CodeStub {
ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
}
- GenericBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info)
+ GenericBinaryOpStub(int key, BinaryOpIC::TypeInfo runtime_operands_type)
: op_(OpBits::decode(key)),
mode_(ModeBits::decode(key)),
flags_(FlagBits::decode(key)),
@@ -95,7 +95,7 @@ class GenericBinaryOpStub: public CodeStub {
args_reversed_(ArgsReversedBits::decode(key)),
static_operands_type_(TypeInfo::ExpandedRepresentation(
StaticTypeInfoBits::decode(key))),
- runtime_operands_type_(type_info),
+ runtime_operands_type_(runtime_operands_type),
name_(NULL) {
}
@@ -131,7 +131,7 @@ class GenericBinaryOpStub: public CodeStub {
#ifdef DEBUG
void Print() {
PrintF("GenericBinaryOpStub %d (op %s), "
- "(mode %d, flags %d, registers %d, reversed %d, only_numbers %s)\n",
+ "(mode %d, flags %d, registers %d, reversed %d, type_info %s)\n",
MinorKey(),
Token::String(op_),
static_cast<int>(mode_),
@@ -200,6 +200,104 @@ class GenericBinaryOpStub: public CodeStub {
friend class CodeGenerator;
};
+
+class TypeRecordingBinaryOpStub: public CodeStub {
+ public:
+ TypeRecordingBinaryOpStub(Token::Value op, OverwriteMode mode)
+ : op_(op),
+ mode_(mode),
+ operands_type_(TRBinaryOpIC::UNINITIALIZED),
+ result_type_(TRBinaryOpIC::UNINITIALIZED),
+ name_(NULL) {
+ ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
+ }
+
+ TypeRecordingBinaryOpStub(
+ int key,
+ TRBinaryOpIC::TypeInfo operands_type,
+ TRBinaryOpIC::TypeInfo result_type = TRBinaryOpIC::UNINITIALIZED)
+ : op_(OpBits::decode(key)),
+ mode_(ModeBits::decode(key)),
+ operands_type_(operands_type),
+ result_type_(result_type),
+ name_(NULL) { }
+
+ private:
+ enum SmiCodeGenerateHeapNumberResults {
+ ALLOW_HEAPNUMBER_RESULTS,
+ NO_HEAPNUMBER_RESULTS
+ };
+
+ Token::Value op_;
+ OverwriteMode mode_;
+
+ // Operand type information determined at runtime.
+ TRBinaryOpIC::TypeInfo operands_type_;
+ TRBinaryOpIC::TypeInfo result_type_;
+
+ char* name_;
+
+ const char* GetName();
+
+#ifdef DEBUG
+ void Print() {
+ PrintF("TypeRecordingBinaryOpStub %d (op %s), "
+ "(mode %d, runtime_type_info %s)\n",
+ MinorKey(),
+ Token::String(op_),
+ static_cast<int>(mode_),
+ TRBinaryOpIC::GetName(operands_type_));
+ }
+#endif
+
+ // Minor key encoding in 15 bits RRRTTTOOOOOOOMM.
+ class ModeBits: public BitField<OverwriteMode, 0, 2> {};
+ class OpBits: public BitField<Token::Value, 2, 7> {};
+ class OperandTypeInfoBits: public BitField<TRBinaryOpIC::TypeInfo, 9, 3> {};
+ class ResultTypeInfoBits: public BitField<TRBinaryOpIC::TypeInfo, 12, 3> {};
+
+ Major MajorKey() { return TypeRecordingBinaryOp; }
+ int MinorKey() {
+ return OpBits::encode(op_)
+ | ModeBits::encode(mode_)
+ | OperandTypeInfoBits::encode(operands_type_)
+ | ResultTypeInfoBits::encode(result_type_);
+ }
+
+ void Generate(MacroAssembler* masm);
+ void GenerateGeneric(MacroAssembler* masm);
+ void GenerateSmiCode(MacroAssembler* masm,
+ Label* slow,
+ SmiCodeGenerateHeapNumberResults heapnumber_results);
+ void GenerateLoadArguments(MacroAssembler* masm);
+ void GenerateReturn(MacroAssembler* masm);
+ void GenerateUninitializedStub(MacroAssembler* masm);
+ void GenerateSmiStub(MacroAssembler* masm);
+ void GenerateInt32Stub(MacroAssembler* masm);
+ void GenerateHeapNumberStub(MacroAssembler* masm);
+ void GenerateStringStub(MacroAssembler* masm);
+ void GenerateGenericStub(MacroAssembler* masm);
+
+ void GenerateHeapResultAllocation(MacroAssembler* masm, Label* alloc_failure);
+ void GenerateRegisterArgsPush(MacroAssembler* masm);
+ void GenerateTypeTransition(MacroAssembler* masm);
+ void GenerateTypeTransitionWithSavedArgs(MacroAssembler* masm);
+
+ virtual int GetCodeKind() { return Code::TYPE_RECORDING_BINARY_OP_IC; }
+
+ virtual InlineCacheState GetICState() {
+ return TRBinaryOpIC::ToState(operands_type_);
+ }
+
+ virtual void FinishCode(Code* code) {
+ code->set_type_recording_binary_op_type(operands_type_);
+ code->set_type_recording_binary_op_result_type(result_type_);
+ }
+
+ friend class CodeGenerator;
+};
+
+
class StringHelper : public AllStatic {
public:
// Generate code for copying characters using a simple loop. This should only
@@ -348,42 +446,6 @@ class NumberToStringStub: public CodeStub {
};
-class RecordWriteStub : public CodeStub {
- public:
- RecordWriteStub(Register object, Register addr, Register scratch)
- : object_(object), addr_(addr), scratch_(scratch) { }
-
- void Generate(MacroAssembler* masm);
-
- private:
- Register object_;
- Register addr_;
- Register scratch_;
-
-#ifdef DEBUG
- void Print() {
- PrintF("RecordWriteStub (object reg %d), (addr reg %d), (scratch reg %d)\n",
- object_.code(), addr_.code(), scratch_.code());
- }
-#endif
-
- // Minor key encoding in 12 bits. 4 bits for each of the 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> {};
-
- Major MajorKey() { return RecordWrite; }
-
- int MinorKey() {
- // Encode the registers.
- return ObjectBits::encode(object_.code()) |
- AddressBits::encode(addr_.code()) |
- ScratchBits::encode(scratch_.code());
- }
-};
-
-
} } // namespace v8::internal
#endif // V8_X64_CODE_STUBS_X64_H_
diff --git a/deps/v8/src/x64/codegen-x64.cc b/deps/v8/src/x64/codegen-x64.cc
index 9a25572298..a543a50487 100644
--- a/deps/v8/src/x64/codegen-x64.cc
+++ b/deps/v8/src/x64/codegen-x64.cc
@@ -627,10 +627,10 @@ Result CodeGenerator::StoreArgumentsObject(bool initial) {
Comment cmnt(masm_, "[ store arguments object");
if (mode == LAZY_ARGUMENTS_ALLOCATION && initial) {
- // When using lazy arguments allocation, we store the hole value
+ // When using lazy arguments allocation, we store the arguments marker value
// as a sentinel indicating that the arguments object hasn't been
// allocated yet.
- frame_->Push(Factory::the_hole_value());
+ frame_->Push(Factory::arguments_marker());
} else {
ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
frame_->PushFunction();
@@ -655,9 +655,9 @@ Result CodeGenerator::StoreArgumentsObject(bool initial) {
if (probe.is_constant()) {
// We have to skip updating the arguments object if it has
// been assigned a proper value.
- skip_arguments = !probe.handle()->IsTheHole();
+ skip_arguments = !probe.handle()->IsArgumentsMarker();
} else {
- __ CompareRoot(probe.reg(), Heap::kTheHoleValueRootIndex);
+ __ CompareRoot(probe.reg(), Heap::kArgumentsMarkerRootIndex);
probe.Unuse();
done.Branch(not_equal);
}
@@ -2516,9 +2516,9 @@ void CodeGenerator::CallApplyLazy(Expression* applicand,
Label slow, done;
bool try_lazy = true;
if (probe.is_constant()) {
- try_lazy = probe.handle()->IsTheHole();
+ try_lazy = probe.handle()->IsArgumentsMarker();
} else {
- __ CompareRoot(probe.reg(), Heap::kTheHoleValueRootIndex);
+ __ CompareRoot(probe.reg(), Heap::kArgumentsMarkerRootIndex);
probe.Unuse();
__ j(not_equal, &slow);
}
@@ -4417,7 +4417,7 @@ void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
// If the loaded value is a constant, we know if the arguments
// object has been lazily loaded yet.
if (value.is_constant()) {
- if (value.handle()->IsTheHole()) {
+ if (value.handle()->IsArgumentsMarker()) {
Result arguments = StoreArgumentsObject(false);
frame_->Push(&arguments);
} else {
@@ -4430,7 +4430,7 @@ void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
// indicates that we haven't loaded the arguments object yet, we
// need to do it now.
JumpTarget exit;
- __ CompareRoot(value.reg(), Heap::kTheHoleValueRootIndex);
+ __ CompareRoot(value.reg(), Heap::kArgumentsMarkerRootIndex);
frame_->Push(&value);
exit.Branch(not_equal);
Result arguments = StoreArgumentsObject(false);
@@ -6813,12 +6813,8 @@ void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) {
// (or them and test against Smi mask.)
__ movq(tmp2.reg(), tmp1.reg());
- RecordWriteStub recordWrite1(tmp2.reg(), index1.reg(), object.reg());
- __ CallStub(&recordWrite1);
-
- RecordWriteStub recordWrite2(tmp1.reg(), index2.reg(), object.reg());
- __ CallStub(&recordWrite2);
-
+ __ RecordWriteHelper(tmp1.reg(), index1.reg(), object.reg());
+ __ RecordWriteHelper(tmp2.reg(), index2.reg(), object.reg());
__ bind(&done);
deferred->BindExit();
@@ -8812,11 +8808,6 @@ ModuloFunction CreateModuloFunction() {
#undef __
-void RecordWriteStub::Generate(MacroAssembler* masm) {
- masm->RecordWriteHelper(object_, addr_, scratch_);
- masm->ret(0);
-}
-
} } // namespace v8::internal
#endif // V8_TARGET_ARCH_X64
diff --git a/deps/v8/src/x64/debug-x64.cc b/deps/v8/src/x64/debug-x64.cc
index 2c1056f579..4218647f33 100644
--- a/deps/v8/src/x64/debug-x64.cc
+++ b/deps/v8/src/x64/debug-x64.cc
@@ -25,7 +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 "v8.h"
#if defined(V8_TARGET_ARCH_X64)
@@ -39,13 +38,61 @@ namespace internal {
#ifdef ENABLE_DEBUGGER_SUPPORT
+bool BreakLocationIterator::IsDebugBreakAtReturn() {
+ return Debug::IsDebugBreakAtReturn(rinfo());
+}
+
+
+// Patch the JS frame exit code with a debug break call. See
+// CodeGenerator::VisitReturnStatement and VirtualFrame::Exit in codegen-x64.cc
+// for the precise return instructions sequence.
+void BreakLocationIterator::SetDebugBreakAtReturn() {
+ ASSERT(Assembler::kJSReturnSequenceLength >=
+ Assembler::kCallInstructionLength);
+ rinfo()->PatchCodeWithCall(Debug::debug_break_return()->entry(),
+ Assembler::kJSReturnSequenceLength - Assembler::kCallInstructionLength);
+}
+
+
+// Restore the JS frame exit code.
+void BreakLocationIterator::ClearDebugBreakAtReturn() {
+ rinfo()->PatchCode(original_rinfo()->pc(),
+ Assembler::kJSReturnSequenceLength);
+}
+
+
+// A debug break in the frame exit code is identified by the JS frame exit code
+// having been patched with a call instruction.
bool Debug::IsDebugBreakAtReturn(v8::internal::RelocInfo* rinfo) {
ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()));
return rinfo->IsPatchedReturnSequence();
}
+
+bool BreakLocationIterator::IsDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ // Check whether the debug break slot instructions have been patched.
+ return !Assembler::IsNop(rinfo()->pc());
+}
+
+
+void BreakLocationIterator::SetDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ rinfo()->PatchCodeWithCall(
+ Debug::debug_break_slot()->entry(),
+ Assembler::kDebugBreakSlotLength - Assembler::kCallInstructionLength);
+}
+
+
+void BreakLocationIterator::ClearDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ rinfo()->PatchCode(original_rinfo()->pc(), Assembler::kDebugBreakSlotLength);
+}
+
+
#define __ ACCESS_MASM(masm)
+
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
RegList object_regs,
RegList non_object_regs,
@@ -55,7 +102,7 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
// Store the registers containing live values on the expression stack to
// make sure that these are correctly updated during GC. Non object values
- // are stored as as two smi causing it to be untouched by GC.
+ // are stored as as two smis causing it to be untouched by GC.
ASSERT((object_regs & ~kJSCallerSaved) == 0);
ASSERT((non_object_regs & ~kJSCallerSaved) == 0);
ASSERT((object_regs & non_object_regs) == 0);
@@ -80,7 +127,7 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
#ifdef DEBUG
__ RecordComment("// Calling from debug break to runtime - come in - over");
#endif
- __ xor_(rax, rax); // No arguments (argc == 0).
+ __ Set(rax, 0); // No arguments (argc == 0).
__ movq(rbx, ExternalReference::debug_break());
CEntryStub ceb(1);
@@ -126,24 +173,25 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
}
-void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) {
- // Register state for IC call call (from ic-x64.cc)
+void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) {
+ // Register state for IC load call (from ic-x64.cc).
// ----------- S t a t e -------------
- // -- rcx: function name
+ // -- rax : receiver
+ // -- rcx : name
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, rcx.bit(), 0, false);
+ Generate_DebugBreakCallHelper(masm, rax.bit() | rcx.bit(), 0, false);
}
-void Debug::GenerateConstructCallDebugBreak(MacroAssembler* masm) {
- // Register state just before return from JS function (from codegen-x64.cc).
- // rax is the actual number of arguments not encoded as a smi, see comment
- // above IC call.
+void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) {
+ // Register state for IC store call (from ic-x64.cc).
// ----------- S t a t e -------------
- // -- rax: number of arguments
+ // -- rax : value
+ // -- rcx : name
+ // -- rdx : receiver
// -----------------------------------
- // The number of arguments in rax is not smi encoded.
- Generate_DebugBreakCallHelper(masm, rdi.bit(), rax.bit(), false);
+ Generate_DebugBreakCallHelper(
+ masm, rax.bit() | rcx.bit() | rdx.bit(), 0, false);
}
@@ -169,34 +217,33 @@ void Debug::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) {
}
-void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) {
- // Register state for IC load call (from ic-x64.cc).
+void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) {
+ // Register state for IC call call (from ic-x64.cc)
// ----------- S t a t e -------------
- // -- rax : receiver
- // -- rcx : name
+ // -- rcx: function name
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, rax.bit() | rcx.bit(), 0, false);
+ Generate_DebugBreakCallHelper(masm, rcx.bit(), 0, false);
}
-void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) {
+void Debug::GenerateConstructCallDebugBreak(MacroAssembler* masm) {
// Register state just before return from JS function (from codegen-x64.cc).
+ // rax is the actual number of arguments not encoded as a smi, see comment
+ // above IC call.
// ----------- S t a t e -------------
- // -- rax: return value
+ // -- rax: number of arguments
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, rax.bit(), 0, true);
+ // The number of arguments in rax is not smi encoded.
+ Generate_DebugBreakCallHelper(masm, rdi.bit(), rax.bit(), false);
}
-void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) {
- // Register state for IC store call (from ic-x64.cc).
+void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) {
+ // Register state just before return from JS function (from codegen-x64.cc).
// ----------- S t a t e -------------
- // -- rax : value
- // -- rcx : name
- // -- rdx : receiver
+ // -- rax: return value
// -----------------------------------
- Generate_DebugBreakCallHelper(
- masm, rax.bit() | rcx.bit() | rdx.bit(), 0, false);
+ Generate_DebugBreakCallHelper(masm, rax.bit(), 0, true);
}
@@ -262,49 +309,6 @@ const bool Debug::kFrameDropperSupported = true;
#undef __
-
-
-
-void BreakLocationIterator::ClearDebugBreakAtReturn() {
- rinfo()->PatchCode(original_rinfo()->pc(),
- Assembler::kJSReturnSequenceLength);
-}
-
-
-bool BreakLocationIterator::IsDebugBreakAtReturn() {
- return Debug::IsDebugBreakAtReturn(rinfo());
-}
-
-
-void BreakLocationIterator::SetDebugBreakAtReturn() {
- ASSERT(Assembler::kJSReturnSequenceLength >=
- Assembler::kCallInstructionLength);
- rinfo()->PatchCodeWithCall(Debug::debug_break_return()->entry(),
- Assembler::kJSReturnSequenceLength - Assembler::kCallInstructionLength);
-}
-
-
-bool BreakLocationIterator::IsDebugBreakAtSlot() {
- ASSERT(IsDebugBreakSlot());
- // Check whether the debug break slot instructions have been patched.
- return !Assembler::IsNop(rinfo()->pc());
-}
-
-
-void BreakLocationIterator::SetDebugBreakAtSlot() {
- ASSERT(IsDebugBreakSlot());
- rinfo()->PatchCodeWithCall(
- Debug::debug_break_slot()->entry(),
- Assembler::kDebugBreakSlotLength - Assembler::kCallInstructionLength);
-}
-
-
-void BreakLocationIterator::ClearDebugBreakAtSlot() {
- ASSERT(IsDebugBreakSlot());
- rinfo()->PatchCode(original_rinfo()->pc(), Assembler::kDebugBreakSlotLength);
-}
-
-
#endif // ENABLE_DEBUGGER_SUPPORT
} } // namespace v8::internal
diff --git a/deps/v8/src/x64/deoptimizer-x64.cc b/deps/v8/src/x64/deoptimizer-x64.cc
index 4e890cd4c0..8bb3ac09fd 100644
--- a/deps/v8/src/x64/deoptimizer-x64.cc
+++ b/deps/v8/src/x64/deoptimizer-x64.cc
@@ -27,6 +27,8 @@
#include "v8.h"
+#if defined(V8_TARGET_ARCH_X64)
+
#include "codegen.h"
#include "deoptimizer.h"
#include "full-codegen.h"
@@ -75,3 +77,5 @@ void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
}
} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/deps/v8/src/x64/full-codegen-x64.cc b/deps/v8/src/x64/full-codegen-x64.cc
index dd28d4d8c1..724a7c598a 100644
--- a/deps/v8/src/x64/full-codegen-x64.cc
+++ b/deps/v8/src/x64/full-codegen-x64.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -199,7 +199,7 @@ void FullCodeGenerator::Generate(CompilationInfo* info) {
void FullCodeGenerator::ClearAccumulator() {
- __ xor_(rax, rax);
+ __ Set(rax, 0);
}
@@ -210,10 +210,17 @@ void FullCodeGenerator::EmitStackCheck(IterationStatement* stmt) {
__ j(above_equal, &ok);
StackCheckStub stub;
__ CallStub(&stub);
+ // Record a mapping of this PC offset to the OSR id. This is used to find
+ // the AST id from the unoptimized code in order to use it as a key into
+ // the deoptimization input data found in the optimized code.
+ RecordStackCheck(stmt->OsrEntryId());
+
__ bind(&ok);
PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
+ // Record a mapping of the OSR id to this PC. This is used if the OSR
+ // entry becomes the target of a bailout. We don't expect it to be, but
+ // we want it to work if it is.
PrepareForBailoutForId(stmt->OsrEntryId(), NO_REGISTERS);
- RecordStackCheck(stmt->OsrEntryId());
}
@@ -459,7 +466,10 @@ void FullCodeGenerator::StackValueContext::Plug(bool flag) const {
void FullCodeGenerator::TestContext::Plug(bool flag) const {
- codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL);
+ codegen()->PrepareForBailoutBeforeSplit(TOS_REG,
+ true,
+ true_label_,
+ false_label_);
if (flag) {
if (true_label_ != fall_through_) __ jmp(true_label_);
} else {
@@ -555,6 +565,25 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state,
bool should_normalize,
Label* if_true,
Label* if_false) {
+ // Only prepare for bailouts before splits if we're in a test
+ // context. Otherwise, we let the Visit function deal with the
+ // preparation to avoid preparing with the same AST id twice.
+ if (!context()->IsTest() || !info_->IsOptimizable()) return;
+
+ NearLabel skip;
+ if (should_normalize) __ jmp(&skip);
+
+ ForwardBailoutStack* current = forward_bailout_stack_;
+ while (current != NULL) {
+ PrepareForBailout(current->expr(), state);
+ current = current->parent();
+ }
+
+ if (should_normalize) {
+ __ CompareRoot(rax, Heap::kTrueValueRootIndex);
+ Split(equal, if_true, if_false, NULL);
+ __ bind(&skip);
+ }
}
@@ -669,8 +698,10 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
Comment cmnt(masm_, "[ SwitchStatement");
Breakable nested_statement(this, stmt);
SetStatementPosition(stmt);
+
// Keep the switch value on the stack until a case matches.
VisitForStackValue(stmt->tag());
+ PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
ZoneList<CaseClause*>* clauses = stmt->cases();
CaseClause* default_clause = NULL; // Can occur anywhere in the list.
@@ -735,6 +766,7 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
}
__ bind(nested_statement.break_target());
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
}
@@ -1224,6 +1256,7 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
if (property->emit_store()) {
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ PrepareForBailoutForId(key->id(), NO_REGISTERS);
}
break;
}
@@ -1311,6 +1344,8 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
// Update the write barrier for the array store.
__ RecordWrite(rbx, offset, result_register(), rcx);
+
+ PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS);
}
if (result_saved) {
@@ -1355,17 +1390,34 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) {
VisitForStackValue(property->obj());
}
break;
- case KEYED_PROPERTY:
+ case KEYED_PROPERTY: {
if (expr->is_compound()) {
- VisitForStackValue(property->obj());
- VisitForAccumulatorValue(property->key());
+ if (property->is_arguments_access()) {
+ VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
+ MemOperand slot_operand =
+ EmitSlotSearch(obj_proxy->var()->AsSlot(), rcx);
+ __ push(slot_operand);
+ __ Move(rax, property->key()->AsLiteral()->handle());
+ } else {
+ VisitForStackValue(property->obj());
+ VisitForAccumulatorValue(property->key());
+ }
__ movq(rdx, Operand(rsp, 0));
__ push(rax);
} else {
- VisitForStackValue(property->obj());
- VisitForStackValue(property->key());
+ if (property->is_arguments_access()) {
+ VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
+ MemOperand slot_operand =
+ EmitSlotSearch(obj_proxy->var()->AsSlot(), rcx);
+ __ push(slot_operand);
+ __ Push(property->key()->AsLiteral()->handle());
+ } else {
+ VisitForStackValue(property->obj());
+ VisitForStackValue(property->key());
+ }
}
break;
+ }
}
if (expr->is_compound()) {
@@ -1383,6 +1435,12 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) {
}
}
+ // For property compound assignments we need another deoptimization
+ // point after the property load.
+ if (property != NULL) {
+ PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
+ }
+
Token::Value op = expr->binary_op();
ConstantOperand constant = ShouldInlineSmiCase(op)
? GetConstantOperand(op, expr->target(), expr->value())
@@ -1408,6 +1466,8 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) {
} else {
EmitBinaryOp(op, mode);
}
+ // Deoptimization point in case the binary operation may have side effects.
+ PrepareForBailout(expr->binary_operation(), TOS_REG);
} else {
VisitForAccumulatorValue(expr->value());
}
@@ -1420,6 +1480,7 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) {
case VARIABLE:
EmitVariableAssignment(expr->target()->AsVariableProxy()->var(),
expr->op());
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
context()->Plug(rax);
break;
case NAMED_PROPERTY:
@@ -1529,7 +1590,7 @@ void FullCodeGenerator::EmitBinaryOp(Token::Value op,
}
-void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_id) {
+void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) {
// Invalid left-hand sides are rewritten to have a 'throw
// ReferenceError' on the left-hand side.
if (!expr->IsValidLeftHandSide()) {
@@ -1577,6 +1638,7 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_id) {
break;
}
}
+ PrepareForBailoutForId(bailout_ast_id, TOS_REG);
context()->Plug(rax);
}
@@ -1688,6 +1750,7 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) {
__ pop(rax);
__ Drop(1);
}
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
context()->Plug(rax);
}
@@ -1726,6 +1789,7 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) {
__ pop(rax);
}
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
context()->Plug(rax);
}
@@ -1766,6 +1830,7 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr,
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic = StubCache::ComputeCallInitialize(arg_count, in_loop);
EmitCallIC(ic, mode);
+ RecordJSReturnSite(expr);
// Restore context register.
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
context()->Plug(rax);
@@ -1799,6 +1864,7 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
Handle<Code> ic = StubCache::ComputeKeyedCallInitialize(arg_count, in_loop);
__ movq(rcx, Operand(rsp, (arg_count + 1) * kPointerSize)); // Key.
EmitCallIC(ic, mode);
+ RecordJSReturnSite(expr);
// Restore context register.
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
context()->DropAndPlug(1, rax); // Drop the key still on the stack.
@@ -1819,6 +1885,7 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) {
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
__ CallStub(&stub);
+ RecordJSReturnSite(expr);
// Restore context register.
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
// Discard the function left on TOS.
@@ -1827,6 +1894,12 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) {
void FullCodeGenerator::VisitCall(Call* expr) {
+#ifdef DEBUG
+ // We want to verify that RecordJSReturnSite gets called on all paths
+ // through this function. Avoid early returns.
+ expr->return_is_recorded_ = false;
+#endif
+
Comment cmnt(masm_, "[ Call");
Expression* fun = expr->expression();
Variable* var = fun->AsVariableProxy()->AsVariable();
@@ -1834,7 +1907,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
if (var != NULL && var->is_possibly_eval()) {
// In a call to eval, we first call %ResolvePossiblyDirectEval to
// resolve the function we need to call and the receiver of the
- // call. The we call the resolved function using the given
+ // call. Then we call the resolved function using the given
// arguments.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
@@ -1871,6 +1944,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
__ CallStub(&stub);
+ RecordJSReturnSite(expr);
// Restore context register.
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
context()->DropAndPlug(1, rax);
@@ -1893,32 +1967,31 @@ void FullCodeGenerator::VisitCall(Call* expr) {
&done);
__ bind(&slow);
- // Call the runtime to find the function to call (returned in rax)
- // and the object holding it (returned in rdx).
- __ push(context_register());
- __ Push(var->name());
- __ CallRuntime(Runtime::kLoadContextSlot, 2);
- __ push(rax); // Function.
- __ push(rdx); // Receiver.
-
- // If fast case code has been generated, emit code to push the
- // function and receiver and have the slow path jump around this
- // code.
- if (done.is_linked()) {
- NearLabel call;
- __ jmp(&call);
- __ bind(&done);
- // Push function.
- __ push(rax);
- // Push global receiver.
+ }
+ // Call the runtime to find the function to call (returned in rax)
+ // and the object holding it (returned in rdx).
+ __ push(context_register());
+ __ Push(var->name());
+ __ CallRuntime(Runtime::kLoadContextSlot, 2);
+ __ push(rax); // Function.
+ __ push(rdx); // Receiver.
+
+ // If fast case code has been generated, emit code to push the
+ // function and receiver and have the slow path jump around this
+ // code.
+ if (done.is_linked()) {
+ NearLabel call;
+ __ jmp(&call);
+ __ bind(&done);
+ // Push function.
+ __ push(rax);
+ // Push global receiver.
__ movq(rbx, GlobalObjectOperand());
__ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
__ bind(&call);
- }
}
EmitCallWithStub(expr);
-
} else if (fun->AsProperty() != NULL) {
// Call to an object property.
Property* prop = fun->AsProperty();
@@ -1932,24 +2005,23 @@ void FullCodeGenerator::VisitCall(Call* expr) {
} else {
// Call to a keyed property.
// For a synthetic property use keyed load IC followed by function call,
- // for a regular property use KeyedCallIC.
+ // for a regular property use keyed EmitCallIC.
{ PreservePositionScope scope(masm()->positions_recorder());
VisitForStackValue(prop->obj());
}
if (prop->is_synthetic()) {
{ PreservePositionScope scope(masm()->positions_recorder());
VisitForAccumulatorValue(prop->key());
- __ movq(rdx, Operand(rsp, 0));
}
// Record source code position for IC call.
SetSourcePosition(prop->position());
+ __ pop(rdx); // We do not need to keep the receiver.
+
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
EmitCallIC(ic, RelocInfo::CODE_TARGET);
- // Pop receiver.
- __ pop(rbx);
// Push result (function).
__ push(rax);
- // Push receiver object on stack.
+ // Push Global receiver.
__ movq(rcx, GlobalObjectOperand());
__ push(FieldOperand(rcx, GlobalObject::kGlobalReceiverOffset));
EmitCallWithStub(expr);
@@ -1960,7 +2032,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
} else {
// Call to some other expression. If the expression is an anonymous
// function literal not called in a loop, mark it as one that should
- // also use the fast code generator.
+ // also use the full code generator.
FunctionLiteral* lit = fun->AsFunctionLiteral();
if (lit != NULL &&
lit->name()->Equals(Heap::empty_string()) &&
@@ -1976,6 +2048,11 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// Emit function call.
EmitCallWithStub(expr);
}
+
+#ifdef DEBUG
+ // RecordJSReturnSite should have been called.
+ ASSERT(expr->return_is_recorded_);
+#endif
}
@@ -2023,6 +2100,7 @@ void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) {
context()->PrepareTest(&materialize_true, &materialize_false,
&if_true, &if_false, &fall_through);
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
__ JumpIfSmi(rax, if_true);
__ jmp(if_false);
@@ -2042,6 +2120,7 @@ void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) {
context()->PrepareTest(&materialize_true, &materialize_false,
&if_true, &if_false, &fall_through);
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
Condition non_negative_smi = masm()->CheckNonNegativeSmi(rax);
Split(non_negative_smi, if_true, if_false, fall_through);
@@ -2073,6 +2152,7 @@ void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) {
__ cmpq(rbx, Immediate(FIRST_JS_OBJECT_TYPE));
__ j(below, if_false);
__ cmpq(rbx, Immediate(LAST_JS_OBJECT_TYPE));
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
Split(below_equal, if_true, if_false, fall_through);
context()->Plug(if_true, if_false);
@@ -2093,6 +2173,7 @@ void FullCodeGenerator::EmitIsSpecObject(ZoneList<Expression*>* args) {
__ JumpIfSmi(rax, if_false);
__ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rbx);
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
Split(above_equal, if_true, if_false, fall_through);
context()->Plug(if_true, if_false);
@@ -2115,6 +2196,7 @@ void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) {
__ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
__ testb(FieldOperand(rbx, Map::kBitFieldOffset),
Immediate(1 << Map::kIsUndetectable));
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
Split(not_zero, if_true, if_false, fall_through);
context()->Plug(if_true, if_false);
@@ -2137,6 +2219,7 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf(
// Just indicate false, as %_IsStringWrapperSafeForDefaultValueOf() is only
// used in a few functions in runtime.js which should not normally be hit by
// this compiler.
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
__ jmp(if_false);
context()->Plug(if_true, if_false);
}
@@ -2156,6 +2239,7 @@ void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) {
__ JumpIfSmi(rax, if_false);
__ CmpObjectType(rax, JS_FUNCTION_TYPE, rbx);
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
Split(equal, if_true, if_false, fall_through);
context()->Plug(if_true, if_false);
@@ -2176,6 +2260,7 @@ void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) {
__ JumpIfSmi(rax, if_false);
__ CmpObjectType(rax, JS_ARRAY_TYPE, rbx);
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
Split(equal, if_true, if_false, fall_through);
context()->Plug(if_true, if_false);
@@ -2196,6 +2281,7 @@ void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) {
__ JumpIfSmi(rax, if_false);
__ CmpObjectType(rax, JS_REGEXP_TYPE, rbx);
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
Split(equal, if_true, if_false, fall_through);
context()->Plug(if_true, if_false);
@@ -2227,6 +2313,7 @@ void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) {
__ bind(&check_frame_marker);
__ SmiCompare(Operand(rax, StandardFrameConstants::kMarkerOffset),
Smi::FromInt(StackFrame::CONSTRUCT));
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
Split(equal, if_true, if_false, fall_through);
context()->Plug(if_true, if_false);
@@ -2249,6 +2336,7 @@ void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) {
__ pop(rbx);
__ cmpq(rax, rbx);
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
Split(equal, if_true, if_false, fall_through);
context()->Plug(if_true, if_false);
@@ -2822,6 +2910,7 @@ void FullCodeGenerator::EmitHasCachedArrayIndex(ZoneList<Expression*>* args) {
__ testl(FieldOperand(rax, String::kHashFieldOffset),
Immediate(String::kContainsCachedArrayIndexMask));
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
__ j(zero, if_true);
__ jmp(if_false);
@@ -2943,6 +3032,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
// Notice that the labels are swapped.
context()->PrepareTest(&materialize_true, &materialize_false,
&if_false, &if_true, &fall_through);
+ if (context()->IsTest()) ForwardBailoutToChild(expr);
VisitForControl(expr->expression(), if_true, if_false, fall_through);
context()->Plug(if_false, if_true); // Labels swapped.
break;
@@ -3056,14 +3146,26 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
__ push(rax); // Copy of receiver, needed for later store.
EmitNamedPropertyLoad(prop);
} else {
- VisitForStackValue(prop->obj());
- VisitForAccumulatorValue(prop->key());
+ if (prop->is_arguments_access()) {
+ VariableProxy* obj_proxy = prop->obj()->AsVariableProxy();
+ MemOperand slot_operand =
+ EmitSlotSearch(obj_proxy->var()->AsSlot(), rcx);
+ __ push(slot_operand);
+ __ Move(rax, prop->key()->AsLiteral()->handle());
+ } else {
+ VisitForStackValue(prop->obj());
+ VisitForAccumulatorValue(prop->key());
+ }
__ movq(rdx, Operand(rsp, 0)); // Leave receiver on stack
__ push(rax); // Copy of key, needed for later store.
EmitKeyedPropertyLoad(prop);
}
}
+ // We need a second deoptimization point after loading the value
+ // in case evaluating the property load my have a side effect.
+ PrepareForBailout(expr->increment(), TOS_REG);
+
// Call ToNumber only if operand is not a smi.
NearLabel no_conversion;
Condition is_smi;
@@ -3133,6 +3235,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
{ EffectContext context(this);
EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
Token::ASSIGN);
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
context.Plug(rax);
}
// For all contexts except kEffect: We have the result on
@@ -3144,6 +3247,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
// Perform the assignment as if via '='.
EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
Token::ASSIGN);
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
context()->Plug(rax);
}
break;
@@ -3152,6 +3256,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
__ pop(rdx);
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
if (expr->is_postfix()) {
if (!context()->IsEffect()) {
context()->PlugTOS();
@@ -3166,6 +3271,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
__ pop(rdx);
Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
if (expr->is_postfix()) {
if (!context()->IsEffect()) {
context()->PlugTOS();
@@ -3192,6 +3298,7 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) {
// Use a regular load, not a contextual load, to avoid a reference
// error.
EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ PrepareForBailout(expr, TOS_REG);
context()->Plug(rax);
} else if (proxy != NULL &&
proxy->var()->AsSlot() != NULL &&
@@ -3207,12 +3314,13 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) {
__ push(rsi);
__ Push(proxy->name());
__ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
+ PrepareForBailout(expr, TOS_REG);
__ bind(&done);
context()->Plug(rax);
} else {
// This expression cannot throw a reference error at the top level.
- Visit(expr);
+ context()->HandleExpression(expr);
}
}
@@ -3237,6 +3345,7 @@ bool FullCodeGenerator::TryLiteralCompare(Token::Value op,
{ AccumulatorValueContext context(this);
VisitForTypeofValue(left_unary->expression());
}
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
if (check->Equals(Heap::number_symbol())) {
Condition is_smi = masm_->CheckSmi(rax);
@@ -3330,6 +3439,7 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
case Token::IN:
VisitForStackValue(expr->right());
__ InvokeBuiltin(Builtins::IN, CALL_FUNCTION);
+ PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL);
__ CompareRoot(rax, Heap::kTrueValueRootIndex);
Split(equal, if_true, if_false, fall_through);
break;
@@ -3338,6 +3448,7 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
VisitForStackValue(expr->right());
InstanceofStub stub(InstanceofStub::kNoFlags);
__ CallStub(&stub);
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
__ testq(rax, rax);
// The stub returns 0 for true.
Split(zero, if_true, if_false, fall_through);
@@ -3396,6 +3507,8 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
: NO_COMPARE_FLAGS;
CompareStub stub(cc, strict, flags);
__ CallStub(&stub);
+
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
__ testq(rax, rax);
Split(cc, if_true, if_false, fall_through);
}
@@ -3417,6 +3530,7 @@ void FullCodeGenerator::VisitCompareToNull(CompareToNull* expr) {
&if_true, &if_false, &fall_through);
VisitForAccumulatorValue(expr->expression());
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
__ CompareRoot(rax, Heap::kNullValueRootIndex);
if (expr->is_strict()) {
Split(equal, if_true, if_false, fall_through);
diff --git a/deps/v8/src/x64/ic-x64.cc b/deps/v8/src/x64/ic-x64.cc
index aff778a5de..29cbed05e1 100644
--- a/deps/v8/src/x64/ic-x64.cc
+++ b/deps/v8/src/x64/ic-x64.cc
@@ -378,81 +378,50 @@ static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
}
-// One byte opcode for test rax,0xXXXXXXXX.
-static const byte kTestEaxByte = 0xA9;
-
-
-static bool PatchInlinedMapCheck(Address address, Object* map) {
- if (V8::UseCrankshaft()) return false;
-
- // Arguments are address of start of call sequence that called
- // the IC,
- Address test_instruction_address =
- address + Assembler::kCallTargetAddressOffset;
- // The keyed load has a fast inlined case if the IC call instruction
- // is immediately followed by a test instruction.
- if (*test_instruction_address != kTestEaxByte) return false;
-
- // Fetch the offset from the test instruction to the map compare
- // instructions (starting with the 64-bit immediate mov of the map
- // address). This offset is stored in the last 4 bytes of the 5
- // byte test instruction.
- Address delta_address = test_instruction_address + 1;
- int delta = *reinterpret_cast<int*>(delta_address);
- // Compute the map address. The map address is in the last 8 bytes
- // of the 10-byte immediate mov instruction (incl. REX prefix), so we add 2
- // to the offset to get the map address.
- Address map_address = test_instruction_address + delta + 2;
- // Patch the map check.
- *(reinterpret_cast<Object**>(map_address)) = map;
- return true;
-}
-
+// The offset from the inlined patch site to the start of the inlined
+// load instruction.
+const int LoadIC::kOffsetToLoadInstruction = 20;
-bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) {
- return PatchInlinedMapCheck(address, map);
-}
+void LoadIC::GenerateArrayLength(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : receiver
+ // -- rcx : name
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label miss;
-bool KeyedStoreIC::PatchInlinedStore(Address address, Object* map) {
- return PatchInlinedMapCheck(address, map);
+ StubCompiler::GenerateLoadArrayLength(masm, rax, rdx, &miss);
+ __ bind(&miss);
+ StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
}
-void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
+void LoadIC::GenerateStringLength(MacroAssembler* masm) {
// ----------- S t a t e -------------
- // -- rax : key
- // -- rdx : receiver
- // -- rsp[0] : return address
+ // -- rax : receiver
+ // -- rcx : name
+ // -- rsp[0] : return address
// -----------------------------------
+ Label miss;
- __ IncrementCounter(&Counters::keyed_load_miss, 1);
-
- __ pop(rbx);
- __ push(rdx); // receiver
- __ push(rax); // name
- __ push(rbx); // return address
-
- // Perform tail call to the entry.
- ExternalReference ref = ExternalReference(IC_Utility(kKeyedLoadIC_Miss));
- __ TailCallExternalReference(ref, 2, 1);
+ StubCompiler::GenerateLoadStringLength(masm, rax, rdx, rbx, &miss);
+ __ bind(&miss);
+ StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
}
-void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
+void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) {
// ----------- S t a t e -------------
- // -- rax : key
- // -- rdx : receiver
- // -- rsp[0] : return address
+ // -- rax : receiver
+ // -- rcx : name
+ // -- rsp[0] : return address
// -----------------------------------
+ Label miss;
- __ pop(rbx);
- __ push(rdx); // receiver
- __ push(rax); // name
- __ push(rbx); // return address
-
- // Perform tail call to the entry.
- __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
+ StubCompiler::GenerateLoadFunctionPrototype(masm, rax, rdx, rbx, &miss);
+ __ bind(&miss);
+ StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
}
@@ -923,45 +892,6 @@ void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
}
-void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
- // ----------- S t a t e -------------
- // -- rax : value
- // -- rcx : key
- // -- rdx : receiver
- // -- rsp[0] : return address
- // -----------------------------------
-
- __ pop(rbx);
- __ push(rdx); // receiver
- __ push(rcx); // key
- __ push(rax); // value
- __ push(rbx); // return address
-
- // Do tail-call to runtime routine.
- ExternalReference ref = ExternalReference(IC_Utility(kKeyedStoreIC_Miss));
- __ TailCallExternalReference(ref, 3, 1);
-}
-
-
-void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) {
- // ----------- S t a t e -------------
- // -- rax : value
- // -- rcx : key
- // -- rdx : receiver
- // -- rsp[0] : return address
- // -----------------------------------
-
- __ pop(rbx);
- __ push(rdx); // receiver
- __ push(rcx); // key
- __ push(rax); // value
- __ push(rbx); // return address
-
- // Do tail-call to runtime routine.
- __ TailCallRuntime(Runtime::kSetProperty, 3, 1);
-}
-
-
void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : value
@@ -1236,71 +1166,6 @@ void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm,
}
-// Defined in ic.cc.
-Object* CallIC_Miss(Arguments args);
-
-
-static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) {
- // ----------- S t a t e -------------
- // rcx : function name
- // rsp[0] : return address
- // rsp[8] : argument argc
- // rsp[16] : argument argc - 1
- // ...
- // rsp[argc * 8] : argument 1
- // rsp[(argc + 1) * 8] : argument 0 = receiver
- // -----------------------------------
-
- if (id == IC::kCallIC_Miss) {
- __ IncrementCounter(&Counters::call_miss, 1);
- } else {
- __ IncrementCounter(&Counters::keyed_call_miss, 1);
- }
-
- // Get the receiver of the function from the stack; 1 ~ return address.
- __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
-
- // Enter an internal frame.
- __ EnterInternalFrame();
-
- // Push the receiver and the name of the function.
- __ push(rdx);
- __ push(rcx);
-
- // Call the entry.
- CEntryStub stub(1);
- __ movq(rax, Immediate(2));
- __ movq(rbx, ExternalReference(IC_Utility(id)));
- __ CallStub(&stub);
-
- // Move result to rdi and exit the internal frame.
- __ movq(rdi, rax);
- __ LeaveInternalFrame();
-
- // Check if the receiver is a global object of some sort.
- // This can happen only for regular CallIC but not KeyedCallIC.
- if (id == IC::kCallIC_Miss) {
- Label invoke, global;
- __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); // receiver
- __ JumpIfSmi(rdx, &invoke);
- __ CmpObjectType(rdx, JS_GLOBAL_OBJECT_TYPE, rcx);
- __ j(equal, &global);
- __ CmpInstanceType(rcx, JS_BUILTINS_OBJECT_TYPE);
- __ j(not_equal, &invoke);
-
- // Patch the receiver on the stack.
- __ bind(&global);
- __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
- __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx);
- __ bind(&invoke);
- }
-
- // Invoke the function.
- ParameterCount actual(argc);
- __ InvokeFunction(rdi, actual, JUMP_FUNCTION);
-}
-
-
// The generated code does not accept smi keys.
// The generated code falls through if both probes miss.
static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
@@ -1409,7 +1274,7 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) {
}
-void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
+static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) {
// ----------- S t a t e -------------
// rcx : function name
// rsp[0] : return address
@@ -1419,7 +1284,54 @@ void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
// rsp[argc * 8] : argument 1
// rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
- GenerateCallMiss(masm, argc, IC::kCallIC_Miss);
+
+ if (id == IC::kCallIC_Miss) {
+ __ IncrementCounter(&Counters::call_miss, 1);
+ } else {
+ __ IncrementCounter(&Counters::keyed_call_miss, 1);
+ }
+
+ // Get the receiver of the function from the stack; 1 ~ return address.
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+
+ // Enter an internal frame.
+ __ EnterInternalFrame();
+
+ // Push the receiver and the name of the function.
+ __ push(rdx);
+ __ push(rcx);
+
+ // Call the entry.
+ CEntryStub stub(1);
+ __ movq(rax, Immediate(2));
+ __ movq(rbx, ExternalReference(IC_Utility(id)));
+ __ CallStub(&stub);
+
+ // Move result to rdi and exit the internal frame.
+ __ movq(rdi, rax);
+ __ LeaveInternalFrame();
+
+ // Check if the receiver is a global object of some sort.
+ // This can happen only for regular CallIC but not KeyedCallIC.
+ if (id == IC::kCallIC_Miss) {
+ Label invoke, global;
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); // receiver
+ __ JumpIfSmi(rdx, &invoke);
+ __ CmpObjectType(rdx, JS_GLOBAL_OBJECT_TYPE, rcx);
+ __ j(equal, &global);
+ __ CmpInstanceType(rcx, JS_BUILTINS_OBJECT_TYPE);
+ __ j(not_equal, &invoke);
+
+ // Patch the receiver on the stack.
+ __ bind(&global);
+ __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
+ __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx);
+ __ bind(&invoke);
+ }
+
+ // Invoke the function.
+ ParameterCount actual(argc);
+ __ InvokeFunction(rdi, actual, JUMP_FUNCTION);
}
@@ -1457,7 +1369,7 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
}
-void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) {
+void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
// ----------- S t a t e -------------
// rcx : function name
// rsp[0] : return address
@@ -1468,7 +1380,7 @@ void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) {
// rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
- GenerateCallMiss(masm, argc, IC::kKeyedCallIC_Miss);
+ GenerateCallMiss(masm, argc, IC::kCallIC_Miss);
}
@@ -1594,56 +1506,18 @@ void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) {
}
-// The offset from the inlined patch site to the start of the inlined
-// load instruction.
-const int LoadIC::kOffsetToLoadInstruction = 20;
-
-
-void LoadIC::GenerateMiss(MacroAssembler* masm) {
- // ----------- S t a t e -------------
- // -- rax : receiver
- // -- rcx : name
- // -- rsp[0] : return address
- // -----------------------------------
-
- __ IncrementCounter(&Counters::load_miss, 1);
-
- __ pop(rbx);
- __ push(rax); // receiver
- __ push(rcx); // name
- __ push(rbx); // return address
-
- // Perform tail call to the entry.
- ExternalReference ref = ExternalReference(IC_Utility(kLoadIC_Miss));
- __ TailCallExternalReference(ref, 2, 1);
-}
-
-
-void LoadIC::GenerateArrayLength(MacroAssembler* masm) {
- // ----------- S t a t e -------------
- // -- rax : receiver
- // -- rcx : name
- // -- rsp[0] : return address
- // -----------------------------------
- Label miss;
-
- StubCompiler::GenerateLoadArrayLength(masm, rax, rdx, &miss);
- __ bind(&miss);
- StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
-}
-
-
-void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) {
+void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) {
// ----------- S t a t e -------------
- // -- rax : receiver
- // -- rcx : name
- // -- rsp[0] : return address
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
+ // ...
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
- Label miss;
- StubCompiler::GenerateLoadFunctionPrototype(masm, rax, rdx, rbx, &miss);
- __ bind(&miss);
- StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
+ GenerateCallMiss(masm, argc, IC::kKeyedCallIC_Miss);
}
@@ -1686,17 +1560,23 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
}
-void LoadIC::GenerateStringLength(MacroAssembler* masm) {
+void LoadIC::GenerateMiss(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : receiver
// -- rcx : name
// -- rsp[0] : return address
// -----------------------------------
- Label miss;
- StubCompiler::GenerateLoadStringLength(masm, rax, rdx, rbx, &miss);
- __ bind(&miss);
- StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
+ __ IncrementCounter(&Counters::load_miss, 1);
+
+ __ pop(rbx);
+ __ push(rax); // receiver
+ __ push(rcx); // name
+ __ push(rbx); // return address
+
+ // Perform tail call to the entry.
+ ExternalReference ref = ExternalReference(IC_Utility(kLoadIC_Miss));
+ __ TailCallExternalReference(ref, 2, 1);
}
@@ -1708,7 +1588,7 @@ bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) {
address + Assembler::kCallTargetAddressOffset;
// If the instruction following the call is not a test rax, nothing
// was inlined.
- if (*test_instruction_address != kTestEaxByte) return false;
+ if (*test_instruction_address != Assembler::kTestEaxByte) return false;
Address delta_address = test_instruction_address + 1;
// The delta to the start of the map check instruction.
@@ -1739,11 +1619,6 @@ bool LoadIC::PatchInlinedContextualLoad(Address address,
}
-// The offset from the inlined patch site to the start of the inlined
-// store instruction.
-const int StoreIC::kOffsetToStoreInstruction = 20;
-
-
bool StoreIC::PatchInlinedStore(Address address, Object* map, int offset) {
if (V8::UseCrankshaft()) return false;
@@ -1753,7 +1628,7 @@ bool StoreIC::PatchInlinedStore(Address address, Object* map, int offset) {
// If the instruction following the call is not a test rax, nothing
// was inlined.
- if (*test_instruction_address != kTestEaxByte) return false;
+ if (*test_instruction_address != Assembler::kTestEaxByte) return false;
// Extract the encoded deltas from the test rax instruction.
Address encoded_offsets_address = test_instruction_address + 1;
@@ -1792,23 +1667,77 @@ bool StoreIC::PatchInlinedStore(Address address, Object* map, int offset) {
}
-void StoreIC::GenerateMiss(MacroAssembler* masm) {
+static bool PatchInlinedMapCheck(Address address, Object* map) {
+ if (V8::UseCrankshaft()) return false;
+
+ // Arguments are address of start of call sequence that called
+ // the IC,
+ Address test_instruction_address =
+ address + Assembler::kCallTargetAddressOffset;
+ // The keyed load has a fast inlined case if the IC call instruction
+ // is immediately followed by a test instruction.
+ if (*test_instruction_address != Assembler::kTestEaxByte) return false;
+
+ // Fetch the offset from the test instruction to the map compare
+ // instructions (starting with the 64-bit immediate mov of the map
+ // address). This offset is stored in the last 4 bytes of the 5
+ // byte test instruction.
+ Address delta_address = test_instruction_address + 1;
+ int delta = *reinterpret_cast<int*>(delta_address);
+ // Compute the map address. The map address is in the last 8 bytes
+ // of the 10-byte immediate mov instruction (incl. REX prefix), so we add 2
+ // to the offset to get the map address.
+ Address map_address = test_instruction_address + delta + 2;
+ // Patch the map check.
+ *(reinterpret_cast<Object**>(map_address)) = map;
+ return true;
+}
+
+
+bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) {
+ return PatchInlinedMapCheck(address, map);
+}
+
+
+bool KeyedStoreIC::PatchInlinedStore(Address address, Object* map) {
+ return PatchInlinedMapCheck(address, map);
+}
+
+
+void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
// ----------- S t a t e -------------
- // -- rax : value
- // -- rcx : name
+ // -- rax : key
// -- rdx : receiver
- // -- rsp[0] : return address
+ // -- rsp[0] : return address
// -----------------------------------
+ __ IncrementCounter(&Counters::keyed_load_miss, 1);
+
__ pop(rbx);
__ push(rdx); // receiver
- __ push(rcx); // name
- __ push(rax); // value
+ __ push(rax); // name
__ push(rbx); // return address
// Perform tail call to the entry.
- ExternalReference ref = ExternalReference(IC_Utility(kStoreIC_Miss));
- __ TailCallExternalReference(ref, 3, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kKeyedLoadIC_Miss));
+ __ TailCallExternalReference(ref, 2, 1);
+}
+
+
+void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ __ pop(rbx);
+ __ push(rdx); // receiver
+ __ push(rax); // name
+ __ push(rbx); // return address
+
+ // Perform tail call to the entry.
+ __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
}
@@ -1831,6 +1760,31 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm) {
}
+void StoreIC::GenerateMiss(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : name
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ __ pop(rbx);
+ __ push(rdx); // receiver
+ __ push(rcx); // name
+ __ push(rax); // value
+ __ push(rbx); // return address
+
+ // Perform tail call to the entry.
+ ExternalReference ref = ExternalReference(IC_Utility(kStoreIC_Miss));
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+// The offset from the inlined patch site to the start of the inlined
+// store instruction.
+const int StoreIC::kOffsetToStoreInstruction = 20;
+
+
void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : value
@@ -1923,6 +1877,45 @@ void StoreIC::GenerateGlobalProxy(MacroAssembler* masm) {
}
+void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ __ pop(rbx);
+ __ push(rdx); // receiver
+ __ push(rcx); // key
+ __ push(rax); // value
+ __ push(rbx); // return address
+
+ // Do tail-call to runtime routine.
+ __ TailCallRuntime(Runtime::kSetProperty, 3, 1);
+}
+
+
+void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ __ pop(rbx);
+ __ push(rdx); // receiver
+ __ push(rcx); // key
+ __ push(rax); // value
+ __ push(rbx); // return address
+
+ // Do tail-call to runtime routine.
+ ExternalReference ref = ExternalReference(IC_Utility(kKeyedStoreIC_Miss));
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
#undef __
@@ -1976,6 +1969,7 @@ void PatchInlinedSmiCode(Address address) {
UNIMPLEMENTED();
}
+
} } // namespace v8::internal
#endif // V8_TARGET_ARCH_X64
diff --git a/deps/v8/src/x64/lithium-codegen-x64.cc b/deps/v8/src/x64/lithium-codegen-x64.cc
new file mode 100644
index 0000000000..e586851e8e
--- /dev/null
+++ b/deps/v8/src/x64/lithium-codegen-x64.cc
@@ -0,0 +1,1475 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "v8.h"
+
+#if defined(V8_TARGET_ARCH_X64)
+
+#include "x64/lithium-codegen-x64.h"
+#include "code-stubs.h"
+#include "stub-cache.h"
+
+namespace v8 {
+namespace internal {
+
+
+class LGapNode: public ZoneObject {
+ public:
+ explicit LGapNode(LOperand* operand)
+ : operand_(operand), resolved_(false), visited_id_(-1) { }
+
+ LOperand* operand() const { return operand_; }
+ bool IsResolved() const { return !IsAssigned() || resolved_; }
+ void MarkResolved() {
+ ASSERT(!IsResolved());
+ resolved_ = true;
+ }
+ int visited_id() const { return visited_id_; }
+ void set_visited_id(int id) {
+ ASSERT(id > visited_id_);
+ visited_id_ = id;
+ }
+
+ bool IsAssigned() const { return assigned_from_.is_set(); }
+ LGapNode* assigned_from() const { return assigned_from_.get(); }
+ void set_assigned_from(LGapNode* n) { assigned_from_.set(n); }
+
+ private:
+ LOperand* operand_;
+ SetOncePointer<LGapNode> assigned_from_;
+ bool resolved_;
+ int visited_id_;
+};
+
+
+LGapResolver::LGapResolver()
+ : nodes_(32),
+ identified_cycles_(4),
+ result_(16),
+ next_visited_id_(0) {
+}
+
+
+const ZoneList<LMoveOperands>* LGapResolver::Resolve(
+ const ZoneList<LMoveOperands>* moves,
+ LOperand* marker_operand) {
+ nodes_.Rewind(0);
+ identified_cycles_.Rewind(0);
+ result_.Rewind(0);
+ next_visited_id_ = 0;
+
+ for (int i = 0; i < moves->length(); ++i) {
+ LMoveOperands move = moves->at(i);
+ if (!move.IsRedundant()) RegisterMove(move);
+ }
+
+ for (int i = 0; i < identified_cycles_.length(); ++i) {
+ ResolveCycle(identified_cycles_[i], marker_operand);
+ }
+
+ int unresolved_nodes;
+ do {
+ unresolved_nodes = 0;
+ for (int j = 0; j < nodes_.length(); j++) {
+ LGapNode* node = nodes_[j];
+ if (!node->IsResolved() && node->assigned_from()->IsResolved()) {
+ AddResultMove(node->assigned_from(), node);
+ node->MarkResolved();
+ }
+ if (!node->IsResolved()) ++unresolved_nodes;
+ }
+ } while (unresolved_nodes > 0);
+ return &result_;
+}
+
+
+void LGapResolver::AddResultMove(LGapNode* from, LGapNode* to) {
+ AddResultMove(from->operand(), to->operand());
+}
+
+
+void LGapResolver::AddResultMove(LOperand* from, LOperand* to) {
+ result_.Add(LMoveOperands(from, to));
+}
+
+
+void LGapResolver::ResolveCycle(LGapNode* start, LOperand* marker_operand) {
+ ZoneList<LOperand*> cycle_operands(8);
+ cycle_operands.Add(marker_operand);
+ LGapNode* cur = start;
+ do {
+ cur->MarkResolved();
+ cycle_operands.Add(cur->operand());
+ cur = cur->assigned_from();
+ } while (cur != start);
+ cycle_operands.Add(marker_operand);
+
+ for (int i = cycle_operands.length() - 1; i > 0; --i) {
+ LOperand* from = cycle_operands[i];
+ LOperand* to = cycle_operands[i - 1];
+ AddResultMove(from, to);
+ }
+}
+
+
+bool LGapResolver::CanReach(LGapNode* a, LGapNode* b, int visited_id) {
+ ASSERT(a != b);
+ LGapNode* cur = a;
+ while (cur != b && cur->visited_id() != visited_id && cur->IsAssigned()) {
+ cur->set_visited_id(visited_id);
+ cur = cur->assigned_from();
+ }
+
+ return cur == b;
+}
+
+
+bool LGapResolver::CanReach(LGapNode* a, LGapNode* b) {
+ ASSERT(a != b);
+ return CanReach(a, b, next_visited_id_++);
+}
+
+
+void LGapResolver::RegisterMove(LMoveOperands move) {
+ if (move.from()->IsConstantOperand()) {
+ // Constant moves should be last in the machine code. Therefore add them
+ // first to the result set.
+ AddResultMove(move.from(), move.to());
+ } else {
+ LGapNode* from = LookupNode(move.from());
+ LGapNode* to = LookupNode(move.to());
+ if (to->IsAssigned() && to->assigned_from() == from) {
+ move.Eliminate();
+ return;
+ }
+ ASSERT(!to->IsAssigned());
+ if (CanReach(from, to)) {
+ // This introduces a cycle. Save.
+ identified_cycles_.Add(from);
+ }
+ to->set_assigned_from(from);
+ }
+}
+
+
+LGapNode* LGapResolver::LookupNode(LOperand* operand) {
+ for (int i = 0; i < nodes_.length(); ++i) {
+ if (nodes_[i]->operand()->Equals(operand)) return nodes_[i];
+ }
+
+ // No node found => create a new one.
+ LGapNode* result = new LGapNode(operand);
+ nodes_.Add(result);
+ return result;
+}
+
+
+#define __ masm()->
+
+bool LCodeGen::GenerateCode() {
+ HPhase phase("Code generation", chunk());
+ ASSERT(is_unused());
+ status_ = GENERATING;
+ return GeneratePrologue() &&
+ GenerateBody() &&
+ GenerateDeferredCode() &&
+ GenerateSafepointTable();
+}
+
+
+void LCodeGen::FinishCode(Handle<Code> code) {
+ ASSERT(is_done());
+ code->set_stack_slots(StackSlotCount());
+ code->set_safepoint_table_start(safepoints_.GetCodeOffset());
+ PopulateDeoptimizationData(code);
+}
+
+
+void LCodeGen::Abort(const char* format, ...) {
+ if (FLAG_trace_bailout) {
+ SmartPointer<char> debug_name = graph()->debug_name()->ToCString();
+ PrintF("Aborting LCodeGen in @\"%s\": ", *debug_name);
+ va_list arguments;
+ va_start(arguments, format);
+ OS::VPrint(format, arguments);
+ va_end(arguments);
+ PrintF("\n");
+ }
+ status_ = ABORTED;
+}
+
+
+void LCodeGen::Comment(const char* format, ...) {
+ if (!FLAG_code_comments) return;
+ char buffer[4 * KB];
+ StringBuilder builder(buffer, ARRAY_SIZE(buffer));
+ va_list arguments;
+ va_start(arguments, format);
+ builder.AddFormattedList(format, arguments);
+ va_end(arguments);
+
+ // Copy the string before recording it in the assembler to avoid
+ // issues when the stack allocated buffer goes out of scope.
+ int length = builder.position();
+ Vector<char> copy = Vector<char>::New(length + 1);
+ memcpy(copy.start(), builder.Finalize(), copy.length());
+ masm()->RecordComment(copy.start());
+}
+
+
+bool LCodeGen::GeneratePrologue() {
+ ASSERT(is_generating());
+
+#ifdef DEBUG
+ if (strlen(FLAG_stop_at) > 0 &&
+ info_->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
+ __ int3();
+ }
+#endif
+
+ __ push(rbp); // Caller's frame pointer.
+ __ movq(rbp, rsp);
+ __ push(rsi); // Callee's context.
+ __ push(rdi); // Callee's JS function.
+
+ // Reserve space for the stack slots needed by the code.
+ int slots = StackSlotCount();
+ if (slots > 0) {
+ if (FLAG_debug_code) {
+ __ movl(rax, Immediate(slots));
+ __ movq(kScratchRegister, kSlotsZapValue, RelocInfo::NONE);
+ Label loop;
+ __ bind(&loop);
+ __ push(kScratchRegister);
+ __ decl(rax);
+ __ j(not_zero, &loop);
+ } else {
+ __ subq(rsp, Immediate(slots * kPointerSize));
+#ifdef _MSC_VER
+ // On windows, you may not access the stack more than one page below
+ // the most recently mapped page. To make the allocated area randomly
+ // accessible, we write to each page in turn (the value is irrelevant).
+ const int kPageSize = 4 * KB;
+ for (int offset = slots * kPointerSize - kPageSize;
+ offset > 0;
+ offset -= kPageSize) {
+ __ movq(Operand(rsp, offset), rax);
+ }
+#endif
+ }
+ }
+
+ // Trace the call.
+ if (FLAG_trace) {
+ __ CallRuntime(Runtime::kTraceEnter, 0);
+ }
+ return !is_aborted();
+}
+
+
+bool LCodeGen::GenerateBody() {
+ ASSERT(is_generating());
+ bool emit_instructions = true;
+ for (current_instruction_ = 0;
+ !is_aborted() && current_instruction_ < instructions_->length();
+ current_instruction_++) {
+ LInstruction* instr = instructions_->at(current_instruction_);
+ if (instr->IsLabel()) {
+ LLabel* label = LLabel::cast(instr);
+ emit_instructions = !label->HasReplacement();
+ }
+
+ if (emit_instructions) {
+ Comment(";;; @%d: %s.", current_instruction_, instr->Mnemonic());
+ instr->CompileToNative(this);
+ }
+ }
+ return !is_aborted();
+}
+
+
+LInstruction* LCodeGen::GetNextInstruction() {
+ if (current_instruction_ < instructions_->length() - 1) {
+ return instructions_->at(current_instruction_ + 1);
+ } else {
+ return NULL;
+ }
+}
+
+
+bool LCodeGen::GenerateDeferredCode() {
+ ASSERT(is_generating());
+ for (int i = 0; !is_aborted() && i < deferred_.length(); i++) {
+ LDeferredCode* code = deferred_[i];
+ __ bind(code->entry());
+ code->Generate();
+ __ jmp(code->exit());
+ }
+
+ // Deferred code is the last part of the instruction sequence. Mark
+ // the generated code as done unless we bailed out.
+ if (!is_aborted()) status_ = DONE;
+ return !is_aborted();
+}
+
+
+bool LCodeGen::GenerateSafepointTable() {
+ Abort("Unimplemented: %s", "GeneratePrologue");
+ return false;
+}
+
+
+Register LCodeGen::ToRegister(int index) const {
+ return Register::FromAllocationIndex(index);
+}
+
+
+XMMRegister LCodeGen::ToDoubleRegister(int index) const {
+ return XMMRegister::FromAllocationIndex(index);
+}
+
+
+Register LCodeGen::ToRegister(LOperand* op) const {
+ ASSERT(op->IsRegister());
+ return ToRegister(op->index());
+}
+
+
+XMMRegister LCodeGen::ToDoubleRegister(LOperand* op) const {
+ ASSERT(op->IsDoubleRegister());
+ return ToDoubleRegister(op->index());
+}
+
+
+bool LCodeGen::IsInteger32Constant(LConstantOperand* op) const {
+ return op->IsConstantOperand() &&
+ chunk_->LookupLiteralRepresentation(op).IsInteger32();
+}
+
+
+bool LCodeGen::IsTaggedConstant(LConstantOperand* op) const {
+ return op->IsConstantOperand() &&
+ chunk_->LookupLiteralRepresentation(op).IsTagged();
+}
+
+
+int LCodeGen::ToInteger32(LConstantOperand* op) const {
+ Handle<Object> value = chunk_->LookupLiteral(op);
+ ASSERT(chunk_->LookupLiteralRepresentation(op).IsInteger32());
+ ASSERT(static_cast<double>(static_cast<int32_t>(value->Number())) ==
+ value->Number());
+ return static_cast<int32_t>(value->Number());
+}
+
+
+Handle<Object> LCodeGen::ToHandle(LConstantOperand* op) const {
+ Handle<Object> literal = chunk_->LookupLiteral(op);
+ Representation r = chunk_->LookupLiteralRepresentation(op);
+ ASSERT(r.IsTagged());
+ return literal;
+}
+
+
+Operand LCodeGen::ToOperand(LOperand* op) const {
+ // Does not handle registers. In X64 assembler, plain registers are not
+ // representable as an Operand.
+ ASSERT(op->IsStackSlot() || op->IsDoubleStackSlot());
+ int index = op->index();
+ if (index >= 0) {
+ // Local or spill slot. Skip the frame pointer, function, and
+ // context in the fixed part of the frame.
+ return Operand(rbp, -(index + 3) * kPointerSize);
+ } else {
+ // Incoming parameter. Skip the return address.
+ return Operand(rbp, -(index - 1) * kPointerSize);
+ }
+}
+
+
+void LCodeGen::WriteTranslation(LEnvironment* environment,
+ Translation* translation) {
+ if (environment == NULL) return;
+
+ // The translation includes one command per value in the environment.
+ int translation_size = environment->values()->length();
+ // The output frame height does not include the parameters.
+ int height = translation_size - environment->parameter_count();
+
+ WriteTranslation(environment->outer(), translation);
+ int closure_id = DefineDeoptimizationLiteral(environment->closure());
+ translation->BeginFrame(environment->ast_id(), closure_id, height);
+ for (int i = 0; i < translation_size; ++i) {
+ LOperand* value = environment->values()->at(i);
+ // spilled_registers_ and spilled_double_registers_ are either
+ // both NULL or both set.
+ if (environment->spilled_registers() != NULL && value != NULL) {
+ if (value->IsRegister() &&
+ environment->spilled_registers()[value->index()] != NULL) {
+ translation->MarkDuplicate();
+ AddToTranslation(translation,
+ environment->spilled_registers()[value->index()],
+ environment->HasTaggedValueAt(i));
+ } else if (
+ value->IsDoubleRegister() &&
+ environment->spilled_double_registers()[value->index()] != NULL) {
+ translation->MarkDuplicate();
+ AddToTranslation(
+ translation,
+ environment->spilled_double_registers()[value->index()],
+ false);
+ }
+ }
+
+ AddToTranslation(translation, value, environment->HasTaggedValueAt(i));
+ }
+}
+
+
+void LCodeGen::AddToTranslation(Translation* translation,
+ LOperand* op,
+ bool is_tagged) {
+ if (op == NULL) {
+ // TODO(twuerthinger): Introduce marker operands to indicate that this value
+ // is not present and must be reconstructed from the deoptimizer. Currently
+ // this is only used for the arguments object.
+ translation->StoreArgumentsObject();
+ } else if (op->IsStackSlot()) {
+ if (is_tagged) {
+ translation->StoreStackSlot(op->index());
+ } else {
+ translation->StoreInt32StackSlot(op->index());
+ }
+ } else if (op->IsDoubleStackSlot()) {
+ translation->StoreDoubleStackSlot(op->index());
+ } else if (op->IsArgument()) {
+ ASSERT(is_tagged);
+ int src_index = StackSlotCount() + op->index();
+ translation->StoreStackSlot(src_index);
+ } else if (op->IsRegister()) {
+ Register reg = ToRegister(op);
+ if (is_tagged) {
+ translation->StoreRegister(reg);
+ } else {
+ translation->StoreInt32Register(reg);
+ }
+ } else if (op->IsDoubleRegister()) {
+ XMMRegister reg = ToDoubleRegister(op);
+ translation->StoreDoubleRegister(reg);
+ } else if (op->IsConstantOperand()) {
+ Handle<Object> literal = chunk()->LookupLiteral(LConstantOperand::cast(op));
+ int src_index = DefineDeoptimizationLiteral(literal);
+ translation->StoreLiteral(src_index);
+ } else {
+ UNREACHABLE();
+ }
+}
+
+
+void LCodeGen::CallCode(Handle<Code> code,
+ RelocInfo::Mode mode,
+ LInstruction* instr) {
+ Abort("Unimplemented: %s", "CallCode");
+}
+
+
+void LCodeGen::CallRuntime(Runtime::Function* function,
+ int num_arguments,
+ LInstruction* instr) {
+ Abort("Unimplemented: %s", "CallRuntime");
+}
+
+
+void LCodeGen::RegisterLazyDeoptimization(LInstruction* instr) {
+ // Create the environment to bailout to. If the call has side effects
+ // execution has to continue after the call otherwise execution can continue
+ // from a previous bailout point repeating the call.
+ LEnvironment* deoptimization_environment;
+ if (instr->HasDeoptimizationEnvironment()) {
+ deoptimization_environment = instr->deoptimization_environment();
+ } else {
+ deoptimization_environment = instr->environment();
+ }
+
+ RegisterEnvironmentForDeoptimization(deoptimization_environment);
+ RecordSafepoint(instr->pointer_map(),
+ deoptimization_environment->deoptimization_index());
+}
+
+
+void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment) {
+ Abort("Unimplemented: %s", "RegisterEnvironmentForDeoptimization");
+}
+
+
+void LCodeGen::DeoptimizeIf(Condition cc, LEnvironment* environment) {
+ Abort("Unimplemented: %s", "Deoptimiz");
+}
+
+
+void LCodeGen::PopulateDeoptimizationData(Handle<Code> code) {
+ int length = deoptimizations_.length();
+ if (length == 0) return;
+ ASSERT(FLAG_deopt);
+ Handle<DeoptimizationInputData> data =
+ Factory::NewDeoptimizationInputData(length, TENURED);
+
+ data->SetTranslationByteArray(*translations_.CreateByteArray());
+ data->SetInlinedFunctionCount(Smi::FromInt(inlined_function_count_));
+
+ Handle<FixedArray> literals =
+ Factory::NewFixedArray(deoptimization_literals_.length(), TENURED);
+ for (int i = 0; i < deoptimization_literals_.length(); i++) {
+ literals->set(i, *deoptimization_literals_[i]);
+ }
+ data->SetLiteralArray(*literals);
+
+ data->SetOsrAstId(Smi::FromInt(info_->osr_ast_id()));
+ data->SetOsrPcOffset(Smi::FromInt(osr_pc_offset_));
+
+ // Populate the deoptimization entries.
+ for (int i = 0; i < length; i++) {
+ LEnvironment* env = deoptimizations_[i];
+ data->SetAstId(i, Smi::FromInt(env->ast_id()));
+ data->SetTranslationIndex(i, Smi::FromInt(env->translation_index()));
+ data->SetArgumentsStackHeight(i,
+ Smi::FromInt(env->arguments_stack_height()));
+ }
+ code->set_deoptimization_data(*data);
+}
+
+
+int LCodeGen::DefineDeoptimizationLiteral(Handle<Object> literal) {
+ int result = deoptimization_literals_.length();
+ for (int i = 0; i < deoptimization_literals_.length(); ++i) {
+ if (deoptimization_literals_[i].is_identical_to(literal)) return i;
+ }
+ deoptimization_literals_.Add(literal);
+ return result;
+}
+
+
+void LCodeGen::PopulateDeoptimizationLiteralsWithInlinedFunctions() {
+ ASSERT(deoptimization_literals_.length() == 0);
+
+ const ZoneList<Handle<JSFunction> >* inlined_closures =
+ chunk()->inlined_closures();
+
+ for (int i = 0, length = inlined_closures->length();
+ i < length;
+ i++) {
+ DefineDeoptimizationLiteral(inlined_closures->at(i));
+ }
+
+ inlined_function_count_ = deoptimization_literals_.length();
+}
+
+
+void LCodeGen::RecordSafepoint(LPointerMap* pointers,
+ int deoptimization_index) {
+ const ZoneList<LOperand*>* operands = pointers->operands();
+ Safepoint safepoint = safepoints_.DefineSafepoint(masm(),
+ deoptimization_index);
+ for (int i = 0; i < operands->length(); i++) {
+ LOperand* pointer = operands->at(i);
+ if (pointer->IsStackSlot()) {
+ safepoint.DefinePointerSlot(pointer->index());
+ }
+ }
+}
+
+
+void LCodeGen::RecordSafepointWithRegisters(LPointerMap* pointers,
+ int arguments,
+ int deoptimization_index) {
+ const ZoneList<LOperand*>* operands = pointers->operands();
+ Safepoint safepoint =
+ safepoints_.DefineSafepointWithRegisters(
+ masm(), arguments, deoptimization_index);
+ for (int i = 0; i < operands->length(); i++) {
+ LOperand* pointer = operands->at(i);
+ if (pointer->IsStackSlot()) {
+ safepoint.DefinePointerSlot(pointer->index());
+ } else if (pointer->IsRegister()) {
+ safepoint.DefinePointerRegister(ToRegister(pointer));
+ }
+ }
+ // Register rsi always contains a pointer to the context.
+ safepoint.DefinePointerRegister(rsi);
+}
+
+
+void LCodeGen::RecordPosition(int position) {
+ if (!FLAG_debug_info || position == RelocInfo::kNoPosition) return;
+ masm()->positions_recorder()->RecordPosition(position);
+}
+
+
+void LCodeGen::DoLabel(LLabel* label) {
+ if (label->is_loop_header()) {
+ Comment(";;; B%d - LOOP entry", label->block_id());
+ } else {
+ Comment(";;; B%d", label->block_id());
+ }
+ __ bind(label->label());
+ current_block_ = label->block_id();
+ LCodeGen::DoGap(label);
+}
+
+
+void LCodeGen::DoParallelMove(LParallelMove* move) {
+ // xmm0 must always be a scratch register.
+ XMMRegister xmm_scratch = xmm0;
+ LUnallocated marker_operand(LUnallocated::NONE);
+
+ Register cpu_scratch = kScratchRegister;
+
+ const ZoneList<LMoveOperands>* moves =
+ resolver_.Resolve(move->move_operands(), &marker_operand);
+ for (int i = moves->length() - 1; i >= 0; --i) {
+ LMoveOperands move = moves->at(i);
+ LOperand* from = move.from();
+ LOperand* to = move.to();
+ ASSERT(!from->IsDoubleRegister() ||
+ !ToDoubleRegister(from).is(xmm_scratch));
+ ASSERT(!to->IsDoubleRegister() || !ToDoubleRegister(to).is(xmm_scratch));
+ ASSERT(!from->IsRegister() || !ToRegister(from).is(cpu_scratch));
+ ASSERT(!to->IsRegister() || !ToRegister(to).is(cpu_scratch));
+ if (from->IsConstantOperand()) {
+ LConstantOperand* constant_from = LConstantOperand::cast(from);
+ if (to->IsRegister()) {
+ if (IsInteger32Constant(constant_from)) {
+ __ movl(ToRegister(to), Immediate(ToInteger32(constant_from)));
+ } else {
+ __ Move(ToRegister(to), ToHandle(constant_from));
+ }
+ } else {
+ if (IsInteger32Constant(constant_from)) {
+ __ movl(ToOperand(to), Immediate(ToInteger32(constant_from)));
+ } else {
+ __ Move(ToOperand(to), ToHandle(constant_from));
+ }
+ }
+ } else if (from == &marker_operand) {
+ if (to->IsRegister()) {
+ __ movq(ToRegister(to), cpu_scratch);
+ } else if (to->IsStackSlot()) {
+ __ movq(ToOperand(to), cpu_scratch);
+ } else if (to->IsDoubleRegister()) {
+ __ movsd(ToDoubleRegister(to), xmm_scratch);
+ } else {
+ ASSERT(to->IsDoubleStackSlot());
+ __ movsd(ToOperand(to), xmm_scratch);
+ }
+ } else if (to == &marker_operand) {
+ if (from->IsRegister()) {
+ __ movq(cpu_scratch, ToRegister(from));
+ } else if (from->IsStackSlot()) {
+ __ movq(cpu_scratch, ToOperand(from));
+ } else if (from->IsDoubleRegister()) {
+ __ movsd(xmm_scratch, ToDoubleRegister(from));
+ } else {
+ ASSERT(from->IsDoubleStackSlot());
+ __ movsd(xmm_scratch, ToOperand(from));
+ }
+ } else if (from->IsRegister()) {
+ if (to->IsRegister()) {
+ __ movq(ToRegister(to), ToRegister(from));
+ } else {
+ __ movq(ToOperand(to), ToRegister(from));
+ }
+ } else if (to->IsRegister()) {
+ __ movq(ToRegister(to), ToOperand(from));
+ } else if (from->IsStackSlot()) {
+ ASSERT(to->IsStackSlot());
+ __ push(rax);
+ __ movq(rax, ToOperand(from));
+ __ movq(ToOperand(to), rax);
+ __ pop(rax);
+ } else if (from->IsDoubleRegister()) {
+ ASSERT(to->IsDoubleStackSlot());
+ __ movsd(ToOperand(to), ToDoubleRegister(from));
+ } else if (to->IsDoubleRegister()) {
+ ASSERT(from->IsDoubleStackSlot());
+ __ movsd(ToDoubleRegister(to), ToOperand(from));
+ } else {
+ ASSERT(to->IsDoubleStackSlot() && from->IsDoubleStackSlot());
+ __ movsd(xmm_scratch, ToOperand(from));
+ __ movsd(ToOperand(to), xmm_scratch);
+ }
+ }
+}
+
+
+void LCodeGen::DoGap(LGap* gap) {
+ for (int i = LGap::FIRST_INNER_POSITION;
+ i <= LGap::LAST_INNER_POSITION;
+ i++) {
+ LGap::InnerPosition inner_pos = static_cast<LGap::InnerPosition>(i);
+ LParallelMove* move = gap->GetParallelMove(inner_pos);
+ if (move != NULL) DoParallelMove(move);
+ }
+
+ LInstruction* next = GetNextInstruction();
+ if (next != NULL && next->IsLazyBailout()) {
+ int pc = masm()->pc_offset();
+ safepoints_.SetPcAfterGap(pc);
+ }
+}
+
+
+void LCodeGen::DoParameter(LParameter* instr) {
+ // Nothing to do.
+}
+
+
+void LCodeGen::DoCallStub(LCallStub* instr) {
+ Abort("Unimplemented: %s", "DoCallStub");
+}
+
+
+void LCodeGen::DoUnknownOSRValue(LUnknownOSRValue* instr) {
+ // Nothing to do.
+}
+
+
+void LCodeGen::DoModI(LModI* instr) {
+ Abort("Unimplemented: %s", "DoModI");
+}
+
+
+void LCodeGen::DoDivI(LDivI* instr) {
+ Abort("Unimplemented: %s", "DoDivI");}
+
+
+void LCodeGen::DoMulI(LMulI* instr) {
+ Abort("Unimplemented: %s", "DoMultI");}
+
+
+void LCodeGen::DoBitI(LBitI* instr) {
+ Abort("Unimplemented: %s", "DoBitI");}
+
+
+void LCodeGen::DoShiftI(LShiftI* instr) {
+ Abort("Unimplemented: %s", "DoShiftI");
+}
+
+
+void LCodeGen::DoSubI(LSubI* instr) {
+ Abort("Unimplemented: %s", "DoSubI");
+}
+
+
+void LCodeGen::DoConstantI(LConstantI* instr) {
+ Abort("Unimplemented: %s", "DoConstantI");
+}
+
+
+void LCodeGen::DoConstantD(LConstantD* instr) {
+ Abort("Unimplemented: %s", "DoConstantI");
+}
+
+
+void LCodeGen::DoConstantT(LConstantT* instr) {
+ ASSERT(instr->result()->IsRegister());
+ __ Move(ToRegister(instr->result()), instr->value());
+}
+
+
+void LCodeGen::DoJSArrayLength(LJSArrayLength* instr) {
+ Abort("Unimplemented: %s", "DoJSArrayLength");
+}
+
+
+void LCodeGen::DoFixedArrayLength(LFixedArrayLength* instr) {
+ Abort("Unimplemented: %s", "DoFixedArrayLength");
+}
+
+
+void LCodeGen::DoValueOf(LValueOf* instr) {
+ Abort("Unimplemented: %s", "DoValueOf");
+}
+
+
+void LCodeGen::DoBitNotI(LBitNotI* instr) {
+ Abort("Unimplemented: %s", "DoBitNotI");
+}
+
+
+void LCodeGen::DoThrow(LThrow* instr) {
+ Abort("Unimplemented: %s", "DoThrow");
+}
+
+
+void LCodeGen::DoAddI(LAddI* instr) {
+ Abort("Unimplemented: %s", "DoAddI");
+}
+
+
+void LCodeGen::DoArithmeticD(LArithmeticD* instr) {
+ Abort("Unimplemented: %s", "DoArithmeticD");
+}
+
+
+void LCodeGen::DoArithmeticT(LArithmeticT* instr) {
+ Abort("Unimplemented: %s", "DoArithmeticT");
+}
+
+
+int LCodeGen::GetNextEmittedBlock(int block) {
+ for (int i = block + 1; i < graph()->blocks()->length(); ++i) {
+ LLabel* label = chunk_->GetLabel(i);
+ if (!label->HasReplacement()) return i;
+ }
+ return -1;
+}
+
+
+void LCodeGen::EmitBranch(int left_block, int right_block, Condition cc) {
+ Abort("Unimplemented: %s", "EmitBranch");
+}
+
+
+void LCodeGen::DoBranch(LBranch* instr) {
+ Abort("Unimplemented: %s", "DoBranch");
+}
+
+
+void LCodeGen::EmitGoto(int block, LDeferredCode* deferred_stack_check) {
+ Abort("Unimplemented: %s", "EmitGoto");
+}
+
+
+void LCodeGen::DoDeferredStackCheck(LGoto* instr) {
+ Abort("Unimplemented: %s", "DoDeferredStackCheck");
+}
+
+
+void LCodeGen::DoGoto(LGoto* instr) {
+ class DeferredStackCheck: public LDeferredCode {
+ public:
+ DeferredStackCheck(LCodeGen* codegen, LGoto* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() { codegen()->DoDeferredStackCheck(instr_); }
+ private:
+ LGoto* instr_;
+ };
+
+ DeferredStackCheck* deferred = NULL;
+ if (instr->include_stack_check()) {
+ deferred = new DeferredStackCheck(this, instr);
+ }
+ EmitGoto(instr->block_id(), deferred);
+}
+
+
+Condition LCodeGen::TokenToCondition(Token::Value op, bool is_unsigned) {
+ Condition cond = no_condition;
+ switch (op) {
+ case Token::EQ:
+ case Token::EQ_STRICT:
+ cond = equal;
+ break;
+ case Token::LT:
+ cond = is_unsigned ? below : less;
+ break;
+ case Token::GT:
+ cond = is_unsigned ? above : greater;
+ break;
+ case Token::LTE:
+ cond = is_unsigned ? below_equal : less_equal;
+ break;
+ case Token::GTE:
+ cond = is_unsigned ? above_equal : greater_equal;
+ break;
+ case Token::IN:
+ case Token::INSTANCEOF:
+ default:
+ UNREACHABLE();
+ }
+ return cond;
+}
+
+
+void LCodeGen::EmitCmpI(LOperand* left, LOperand* right) {
+ Abort("Unimplemented: %s", "EmitCmpI");
+}
+
+
+void LCodeGen::DoCmpID(LCmpID* instr) {
+ Abort("Unimplemented: %s", "DoCmpID");
+}
+
+
+void LCodeGen::DoCmpIDAndBranch(LCmpIDAndBranch* instr) {
+ Abort("Unimplemented: %s", "DoCmpIDAndBranch");
+}
+
+
+void LCodeGen::DoCmpJSObjectEq(LCmpJSObjectEq* instr) {
+ Abort("Unimplemented: %s", "DoCmpJSObjectEq");
+}
+
+
+void LCodeGen::DoCmpJSObjectEqAndBranch(LCmpJSObjectEqAndBranch* instr) {
+ Abort("Unimplemented: %s", "DoCmpJSObjectAndBranch");
+}
+
+
+void LCodeGen::DoIsNull(LIsNull* instr) {
+ Abort("Unimplemented: %s", "DoIsNull");
+}
+
+
+void LCodeGen::DoIsNullAndBranch(LIsNullAndBranch* instr) {
+ Abort("Unimplemented: %s", "DoIsNullAndBranch");
+}
+
+
+Condition LCodeGen::EmitIsObject(Register input,
+ Register temp1,
+ Register temp2,
+ Label* is_not_object,
+ Label* is_object) {
+ Abort("Unimplemented: %s", "EmitIsObject");
+ return below_equal;
+}
+
+
+void LCodeGen::DoIsObject(LIsObject* instr) {
+ Abort("Unimplemented: %s", "DoIsObject");
+}
+
+
+void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) {
+ Abort("Unimplemented: %s", "DoIsObjectAndBranch");
+}
+
+
+void LCodeGen::DoIsSmi(LIsSmi* instr) {
+ Abort("Unimplemented: %s", "DoIsSmi");
+}
+
+
+void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) {
+ Abort("Unimplemented: %s", "DoIsSmiAndBranch");
+}
+
+
+InstanceType LHasInstanceType::TestType() {
+ InstanceType from = hydrogen()->from();
+ InstanceType to = hydrogen()->to();
+ if (from == FIRST_TYPE) return to;
+ ASSERT(from == to || to == LAST_TYPE);
+ return from;
+}
+
+
+
+Condition LHasInstanceType::BranchCondition() {
+ InstanceType from = hydrogen()->from();
+ InstanceType to = hydrogen()->to();
+ if (from == to) return equal;
+ if (to == LAST_TYPE) return above_equal;
+ if (from == FIRST_TYPE) return below_equal;
+ UNREACHABLE();
+ return equal;
+}
+
+
+void LCodeGen::DoHasInstanceType(LHasInstanceType* instr) {
+ Abort("Unimplemented: %s", "DoHasInstanceType");
+}
+
+
+void LCodeGen::DoHasInstanceTypeAndBranch(LHasInstanceTypeAndBranch* instr) {
+ Abort("Unimplemented: %s", "DoHasInstanceTypeAndBranch");
+}
+
+
+void LCodeGen::DoHasCachedArrayIndex(LHasCachedArrayIndex* instr) {
+ Abort("Unimplemented: %s", "DoHasCachedArrayIndex");
+}
+
+
+void LCodeGen::DoHasCachedArrayIndexAndBranch(
+ LHasCachedArrayIndexAndBranch* instr) {
+ Abort("Unimplemented: %s", "DoHasCachedArrayIndexAndBranch");
+}
+
+
+// Branches to a label or falls through with the answer in the z flag. Trashes
+// the temp registers, but not the input. Only input and temp2 may alias.
+void LCodeGen::EmitClassOfTest(Label* is_true,
+ Label* is_false,
+ Handle<String>class_name,
+ Register input,
+ Register temp,
+ Register temp2) {
+ Abort("Unimplemented: %s", "EmitClassOfTest");
+}
+
+
+void LCodeGen::DoClassOfTest(LClassOfTest* instr) {
+ Abort("Unimplemented: %s", "DoClassOfTest");
+}
+
+
+void LCodeGen::DoClassOfTestAndBranch(LClassOfTestAndBranch* instr) {
+ Abort("Unimplemented: %s", "DoClassOfTestAndBranch");
+}
+
+
+void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) {
+ Abort("Unimplemented: %s", "DoCmpMapAndBranch");
+}
+
+
+void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
+ Abort("Unimplemented: %s", "DoInstanceOf");
+}
+
+
+void LCodeGen::DoInstanceOfAndBranch(LInstanceOfAndBranch* instr) {
+ Abort("Unimplemented: %s", "DoInstanceOfAndBranch");
+}
+
+
+void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) {
+ Abort("Unimplemented: %s", "DoInstanceOfKnowGLobal");
+}
+
+
+void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
+ Label* map_check) {
+ Abort("Unimplemented: %s", "DoDeferredLInstanceOfKnownGlobakl");
+}
+
+
+void LCodeGen::DoCmpT(LCmpT* instr) {
+ Abort("Unimplemented: %s", "DoCmpT");
+}
+
+
+void LCodeGen::DoCmpTAndBranch(LCmpTAndBranch* instr) {
+ Abort("Unimplemented: %s", "DoCmpTAndBranch");
+}
+
+
+void LCodeGen::DoReturn(LReturn* instr) {
+ if (FLAG_trace) {
+ // Preserve the return value on the stack and rely on the runtime
+ // call to return the value in the same register.
+ __ push(rax);
+ __ CallRuntime(Runtime::kTraceExit, 1);
+ }
+ __ movq(rsp, rbp);
+ __ pop(rbp);
+ __ ret((ParameterCount() + 1) * kPointerSize);
+}
+
+
+void LCodeGen::DoLoadGlobal(LLoadGlobal* instr) {
+ Abort("Unimplemented: %s", "DoLoadGlobal");
+}
+
+
+void LCodeGen::DoStoreGlobal(LStoreGlobal* instr) {
+ Abort("Unimplemented: %s", "DoStoreGlobal");
+}
+
+
+void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
+ Abort("Unimplemented: %s", "DoLoadContextSlot");
+}
+
+
+void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
+ Abort("Unimplemented: %s", "DoLoadNamedField");
+}
+
+
+void LCodeGen::DoLoadNamedGeneric(LLoadNamedGeneric* instr) {
+ Abort("Unimplemented: %s", "DoLoadNamedGeneric");
+}
+
+
+void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
+ Abort("Unimplemented: %s", "DoLoadFunctionPrototype");
+}
+
+
+void LCodeGen::DoLoadElements(LLoadElements* instr) {
+ Abort("Unimplemented: %s", "DoLoadElements");
+}
+
+
+void LCodeGen::DoAccessArgumentsAt(LAccessArgumentsAt* instr) {
+ Abort("Unimplemented: %s", "DoAccessArgumentsAt");
+}
+
+
+void LCodeGen::DoLoadKeyedFastElement(LLoadKeyedFastElement* instr) {
+ Abort("Unimplemented: %s", "DoLoadKeyedFastElement");
+}
+
+
+void LCodeGen::DoLoadKeyedGeneric(LLoadKeyedGeneric* instr) {
+ Abort("Unimplemented: %s", "DoLoadKeyedGeneric");
+}
+
+
+void LCodeGen::DoArgumentsElements(LArgumentsElements* instr) {
+ Abort("Unimplemented: %s", "DoArgumentsElements");
+}
+
+
+void LCodeGen::DoArgumentsLength(LArgumentsLength* instr) {
+ Abort("Unimplemented: %s", "DoArgumentsLength");
+}
+
+
+void LCodeGen::DoApplyArguments(LApplyArguments* instr) {
+ Abort("Unimplemented: %s", "DoApplyArguments");
+}
+
+
+void LCodeGen::DoPushArgument(LPushArgument* instr) {
+ Abort("Unimplemented: %s", "DoPushArgument");
+}
+
+
+void LCodeGen::DoGlobalObject(LGlobalObject* instr) {
+ Abort("Unimplemented: %s", "DoGlobalObject");
+}
+
+
+void LCodeGen::DoGlobalReceiver(LGlobalReceiver* instr) {
+ Abort("Unimplemented: %s", "DoGlobalReceiver");
+}
+
+
+void LCodeGen::CallKnownFunction(Handle<JSFunction> function,
+ int arity,
+ LInstruction* instr) {
+ Abort("Unimplemented: %s", "CallKnownFunction");
+}
+
+
+void LCodeGen::DoCallConstantFunction(LCallConstantFunction* instr) {
+ Abort("Unimplemented: %s", "DoCallConstantFunction");
+}
+
+
+void LCodeGen::DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr) {
+ Abort("Unimplemented: %s", "DoDeferredMathAbsTaggedHeapNumber");
+}
+
+
+void LCodeGen::DoMathAbs(LUnaryMathOperation* instr) {
+ Abort("Unimplemented: %s", "DoMathAbs");
+}
+
+
+void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) {
+ Abort("Unimplemented: %s", "DoMathFloor");
+}
+
+
+void LCodeGen::DoMathRound(LUnaryMathOperation* instr) {
+ Abort("Unimplemented: %s", "DoMathRound");
+}
+
+
+void LCodeGen::DoMathSqrt(LUnaryMathOperation* instr) {
+ Abort("Unimplemented: %s", "DoMathSqrt");
+}
+
+
+void LCodeGen::DoMathPowHalf(LUnaryMathOperation* instr) {
+ Abort("Unimplemented: %s", "DoMathPowHalf");
+}
+
+
+void LCodeGen::DoPower(LPower* instr) {
+ Abort("Unimplemented: %s", "DoPower");
+}
+
+
+void LCodeGen::DoMathLog(LUnaryMathOperation* instr) {
+ Abort("Unimplemented: %s", "DoMathLog");
+}
+
+
+void LCodeGen::DoMathCos(LUnaryMathOperation* instr) {
+ Abort("Unimplemented: %s", "DoMathCos");
+}
+
+
+void LCodeGen::DoMathSin(LUnaryMathOperation* instr) {
+ Abort("Unimplemented: %s", "DoMathSin");
+}
+
+
+void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) {
+ Abort("Unimplemented: %s", "DoUnaryMathOperation");
+}
+
+
+void LCodeGen::DoCallKeyed(LCallKeyed* instr) {
+ Abort("Unimplemented: %s", "DoCallKeyed");
+}
+
+
+void LCodeGen::DoCallNamed(LCallNamed* instr) {
+ Abort("Unimplemented: %s", "DoCallNamed");
+}
+
+
+void LCodeGen::DoCallFunction(LCallFunction* instr) {
+ Abort("Unimplemented: %s", "DoCallFunction");
+}
+
+
+void LCodeGen::DoCallGlobal(LCallGlobal* instr) {
+ Abort("Unimplemented: %s", "DoCallGlobal");
+}
+
+
+void LCodeGen::DoCallKnownGlobal(LCallKnownGlobal* instr) {
+ Abort("Unimplemented: %s", "DoCallKnownGlobal");
+}
+
+
+void LCodeGen::DoCallNew(LCallNew* instr) {
+ Abort("Unimplemented: %s", "DoCallNew");
+}
+
+
+void LCodeGen::DoCallRuntime(LCallRuntime* instr) {
+ Abort("Unimplemented: %s", "DoCallRuntime");
+}
+
+
+void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
+ Abort("Unimplemented: %s", "DoStoreNamedField");
+}
+
+
+void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) {
+ Abort("Unimplemented: %s", "DoStoreNamedGeneric");
+}
+
+
+void LCodeGen::DoBoundsCheck(LBoundsCheck* instr) {
+ Abort("Unimplemented: %s", "DoBoundsCheck");
+}
+
+
+void LCodeGen::DoStoreKeyedFastElement(LStoreKeyedFastElement* instr) {
+ Abort("Unimplemented: %s", "DoStoreKeyedFastElement");
+}
+
+
+void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) {
+ Abort("Unimplemented: %s", "DoStoreKeyedGeneric");
+}
+
+
+void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) {
+ Abort("Unimplemented: %s", "DoInteger32ToDouble");
+}
+
+
+void LCodeGen::DoNumberTagI(LNumberTagI* instr) {
+ Abort("Unimplemented: %s", "DoNumberTagI");
+}
+
+
+void LCodeGen::DoDeferredNumberTagI(LNumberTagI* instr) {
+ Abort("Unimplemented: %s", "DoDeferredNumberTagI");
+}
+
+
+void LCodeGen::DoNumberTagD(LNumberTagD* instr) {
+ Abort("Unimplemented: %s", "DoNumberTagD");
+}
+
+
+void LCodeGen::DoDeferredNumberTagD(LNumberTagD* instr) {
+ Abort("Unimplemented: %s", "DoDeferredNumberTagD");
+}
+
+
+void LCodeGen::DoSmiTag(LSmiTag* instr) {
+ Abort("Unimplemented: %s", "DoSmiTag");
+}
+
+
+void LCodeGen::DoSmiUntag(LSmiUntag* instr) {
+ Abort("Unimplemented: %s", "DoSmiUntag");
+}
+
+
+void LCodeGen::EmitNumberUntagD(Register input_reg,
+ XMMRegister result_reg,
+ LEnvironment* env) {
+ Abort("Unimplemented: %s", "EmitNumberUntagD");
+}
+
+
+void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
+ Abort("Unimplemented: %s", "DoDeferredTaggedToI");
+}
+
+
+void LCodeGen::DoTaggedToI(LTaggedToI* instr) {
+ Abort("Unimplemented: %s", "DoTaggedToI");
+}
+
+
+void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) {
+ Abort("Unimplemented: %s", "DoNumberUntagD");
+}
+
+
+void LCodeGen::DoDoubleToI(LDoubleToI* instr) {
+ Abort("Unimplemented: %s", "DoDoubleToI");
+}
+
+
+void LCodeGen::DoCheckSmi(LCheckSmi* instr) {
+ Abort("Unimplemented: %s", "DoCheckSmi");
+}
+
+
+void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
+ Abort("Unimplemented: %s", "DoCheckInstanceType");
+}
+
+
+void LCodeGen::DoCheckFunction(LCheckFunction* instr) {
+ Abort("Unimplemented: %s", "DoCheckFunction");
+}
+
+
+void LCodeGen::DoCheckMap(LCheckMap* instr) {
+ Abort("Unimplemented: %s", "DoCheckMap");
+}
+
+
+void LCodeGen::LoadHeapObject(Register result, Handle<HeapObject> object) {
+ Abort("Unimplemented: %s", "LoadHeapObject");
+}
+
+
+void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) {
+ Abort("Unimplemented: %s", "DoCheckPrototypeMaps");
+}
+
+
+void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) {
+ Abort("Unimplemented: %s", "DoArrayLiteral");
+}
+
+
+void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) {
+ Abort("Unimplemented: %s", "DoObjectLiteral");
+}
+
+
+void LCodeGen::DoRegExpLiteral(LRegExpLiteral* instr) {
+ Abort("Unimplemented: %s", "DoRegExpLiteral");
+}
+
+
+void LCodeGen::DoFunctionLiteral(LFunctionLiteral* instr) {
+ Abort("Unimplemented: %s", "DoFunctionLiteral");
+}
+
+
+void LCodeGen::DoTypeof(LTypeof* instr) {
+ Abort("Unimplemented: %s", "DoTypeof");
+}
+
+
+void LCodeGen::DoTypeofIs(LTypeofIs* instr) {
+ Abort("Unimplemented: %s", "DoTypeofIs");
+}
+
+
+void LCodeGen::DoTypeofIsAndBranch(LTypeofIsAndBranch* instr) {
+ Abort("Unimplemented: %s", "DoTypeofIsAndBranch");
+}
+
+
+Condition LCodeGen::EmitTypeofIs(Label* true_label,
+ Label* false_label,
+ Register input,
+ Handle<String> type_name) {
+ Abort("Unimplemented: %s", "EmitTypeofIs");
+ return no_condition;
+}
+
+
+void LCodeGen::DoLazyBailout(LLazyBailout* instr) {
+ // No code for lazy bailout instruction. Used to capture environment after a
+ // call for populating the safepoint data with deoptimization data.
+}
+
+
+void LCodeGen::DoDeoptimize(LDeoptimize* instr) {
+ DeoptimizeIf(no_condition, instr->environment());
+}
+
+
+void LCodeGen::DoDeleteProperty(LDeleteProperty* instr) {
+ Abort("Unimplemented: %s", "DoDeleteProperty");
+}
+
+
+void LCodeGen::DoStackCheck(LStackCheck* instr) {
+ // Perform stack overflow check.
+ NearLabel done;
+ ExternalReference stack_limit = ExternalReference::address_of_stack_limit();
+ __ CompareRoot(rsp, Heap::kStackLimitRootIndex);
+ __ j(above_equal, &done);
+
+ StackCheckStub stub;
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoOsrEntry(LOsrEntry* instr) {
+ Abort("Unimplemented: %s", "DoOsrEntry");
+}
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/deps/v8/src/x64/lithium-codegen-x64.h b/deps/v8/src/x64/lithium-codegen-x64.h
index cd1f08dea3..8d1c5c4ed2 100644
--- a/deps/v8/src/x64/lithium-codegen-x64.h
+++ b/deps/v8/src/x64/lithium-codegen-x64.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -30,6 +30,7 @@
#include "x64/lithium-x64.h"
+#include "checks.h"
#include "deoptimizer.h"
#include "safepoint-table.h"
#include "scopes.h"
@@ -39,22 +40,256 @@ namespace internal {
// Forward declarations.
class LDeferredCode;
+class LGapNode;
+class SafepointGenerator;
+
+class LGapResolver BASE_EMBEDDED {
+ public:
+ LGapResolver();
+ const ZoneList<LMoveOperands>* Resolve(const ZoneList<LMoveOperands>* moves,
+ LOperand* marker_operand);
+
+ private:
+ LGapNode* LookupNode(LOperand* operand);
+ bool CanReach(LGapNode* a, LGapNode* b, int visited_id);
+ bool CanReach(LGapNode* a, LGapNode* b);
+ void RegisterMove(LMoveOperands move);
+ void AddResultMove(LOperand* from, LOperand* to);
+ void AddResultMove(LGapNode* from, LGapNode* to);
+ void ResolveCycle(LGapNode* start, LOperand* marker_operand);
+
+ ZoneList<LGapNode*> nodes_;
+ ZoneList<LGapNode*> identified_cycles_;
+ ZoneList<LMoveOperands> result_;
+ int next_visited_id_;
+};
+
class LCodeGen BASE_EMBEDDED {
public:
- LCodeGen(LChunk* chunk, MacroAssembler* assembler, CompilationInfo* info) { }
+ LCodeGen(LChunk* chunk, MacroAssembler* assembler, CompilationInfo* info)
+ : chunk_(chunk),
+ masm_(assembler),
+ info_(info),
+ current_block_(-1),
+ current_instruction_(-1),
+ instructions_(chunk->instructions()),
+ deoptimizations_(4),
+ deoptimization_literals_(8),
+ inlined_function_count_(0),
+ scope_(chunk->graph()->info()->scope()),
+ status_(UNUSED),
+ deferred_(8),
+ osr_pc_offset_(-1) {
+ PopulateDeoptimizationLiteralsWithInlinedFunctions();
+ }
// Try to generate code for the entire chunk, but it may fail if the
// chunk contains constructs we cannot handle. Returns true if the
// code generation attempt succeeded.
- bool GenerateCode() {
- UNIMPLEMENTED();
- return false;
- }
+ bool GenerateCode();
// Finish the code by setting stack height, safepoint, and bailout
// information on it.
- void FinishCode(Handle<Code> code) { UNIMPLEMENTED(); }
+ void FinishCode(Handle<Code> code);
+
+ // Deferred code support.
+ void DoDeferredNumberTagD(LNumberTagD* instr);
+ void DoDeferredNumberTagI(LNumberTagI* instr);
+ void DoDeferredTaggedToI(LTaggedToI* instr);
+ void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr);
+ void DoDeferredStackCheck(LGoto* instr);
+ void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
+ Label* map_check);
+
+ // Parallel move support.
+ void DoParallelMove(LParallelMove* move);
+
+ // Emit frame translation commands for an environment.
+ void WriteTranslation(LEnvironment* environment, Translation* translation);
+
+ // Declare methods that deal with the individual node types.
+#define DECLARE_DO(type) void Do##type(L##type* node);
+ LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_DO)
+#undef DECLARE_DO
+
+ private:
+ enum Status {
+ UNUSED,
+ GENERATING,
+ DONE,
+ ABORTED
+ };
+
+ bool is_unused() const { return status_ == UNUSED; }
+ bool is_generating() const { return status_ == GENERATING; }
+ bool is_done() const { return status_ == DONE; }
+ bool is_aborted() const { return status_ == ABORTED; }
+
+ LChunk* chunk() const { return chunk_; }
+ Scope* scope() const { return scope_; }
+ HGraph* graph() const { return chunk_->graph(); }
+ MacroAssembler* masm() const { return masm_; }
+
+ int GetNextEmittedBlock(int block);
+ LInstruction* GetNextInstruction();
+
+ void EmitClassOfTest(Label* if_true,
+ Label* if_false,
+ Handle<String> class_name,
+ Register input,
+ Register temporary,
+ Register temporary2);
+
+ int StackSlotCount() const { return chunk()->spill_slot_count(); }
+ int ParameterCount() const { return scope()->num_parameters(); }
+
+ void Abort(const char* format, ...);
+ void Comment(const char* format, ...);
+
+ void AddDeferredCode(LDeferredCode* code) { deferred_.Add(code); }
+
+ // Code generation passes. Returns true if code generation should
+ // continue.
+ bool GeneratePrologue();
+ bool GenerateBody();
+ bool GenerateDeferredCode();
+ bool GenerateSafepointTable();
+
+ void CallCode(Handle<Code> code,
+ RelocInfo::Mode mode,
+ LInstruction* instr);
+ void CallRuntime(Runtime::Function* function,
+ int num_arguments,
+ LInstruction* instr);
+ void CallRuntime(Runtime::FunctionId id,
+ int num_arguments,
+ LInstruction* instr) {
+ Runtime::Function* function = Runtime::FunctionForId(id);
+ CallRuntime(function, num_arguments, instr);
+ }
+
+ // Generate a direct call to a known function. Expects the function
+ // to be in edi.
+ void CallKnownFunction(Handle<JSFunction> function,
+ int arity,
+ LInstruction* instr);
+
+ void LoadHeapObject(Register result, Handle<HeapObject> object);
+
+ void RegisterLazyDeoptimization(LInstruction* instr);
+ void RegisterEnvironmentForDeoptimization(LEnvironment* environment);
+ void DeoptimizeIf(Condition cc, LEnvironment* environment);
+
+ void AddToTranslation(Translation* translation,
+ LOperand* op,
+ bool is_tagged);
+ void PopulateDeoptimizationData(Handle<Code> code);
+ int DefineDeoptimizationLiteral(Handle<Object> literal);
+
+ void PopulateDeoptimizationLiteralsWithInlinedFunctions();
+
+ Register ToRegister(int index) const;
+ XMMRegister ToDoubleRegister(int index) const;
+ Register ToRegister(LOperand* op) const;
+ XMMRegister ToDoubleRegister(LOperand* op) const;
+ bool IsInteger32Constant(LConstantOperand* op) const;
+ int ToInteger32(LConstantOperand* op) const;
+ bool IsTaggedConstant(LConstantOperand* op) const;
+ Handle<Object> ToHandle(LConstantOperand* op) const;
+ Operand ToOperand(LOperand* op) const;
+
+ // Specific math operations - used from DoUnaryMathOperation.
+ void DoMathAbs(LUnaryMathOperation* instr);
+ void DoMathFloor(LUnaryMathOperation* instr);
+ void DoMathRound(LUnaryMathOperation* instr);
+ void DoMathSqrt(LUnaryMathOperation* instr);
+ void DoMathPowHalf(LUnaryMathOperation* instr);
+ void DoMathLog(LUnaryMathOperation* instr);
+ void DoMathCos(LUnaryMathOperation* instr);
+ void DoMathSin(LUnaryMathOperation* instr);
+
+ // Support for recording safepoint and position information.
+ void RecordSafepoint(LPointerMap* pointers, int deoptimization_index);
+ void RecordSafepointWithRegisters(LPointerMap* pointers,
+ int arguments,
+ int deoptimization_index);
+ void RecordPosition(int position);
+
+ static Condition TokenToCondition(Token::Value op, bool is_unsigned);
+ void EmitGoto(int block, LDeferredCode* deferred_stack_check = NULL);
+ void EmitBranch(int left_block, int right_block, Condition cc);
+ void EmitCmpI(LOperand* left, LOperand* right);
+ void EmitNumberUntagD(Register input, XMMRegister result, LEnvironment* env);
+
+ // Emits optimized code for typeof x == "y". Modifies input register.
+ // Returns the condition on which a final split to
+ // true and false label should be made, to optimize fallthrough.
+ Condition EmitTypeofIs(Label* true_label, Label* false_label,
+ Register input, Handle<String> type_name);
+
+ // Emits optimized code for %_IsObject(x). Preserves input register.
+ // Returns the condition on which a final split to
+ // true and false label should be made, to optimize fallthrough.
+ Condition EmitIsObject(Register input,
+ Register temp1,
+ Register temp2,
+ Label* is_not_object,
+ Label* is_object);
+
+ LChunk* const chunk_;
+ MacroAssembler* const masm_;
+ CompilationInfo* const info_;
+
+ int current_block_;
+ int current_instruction_;
+ const ZoneList<LInstruction*>* instructions_;
+ ZoneList<LEnvironment*> deoptimizations_;
+ ZoneList<Handle<Object> > deoptimization_literals_;
+ int inlined_function_count_;
+ Scope* const scope_;
+ Status status_;
+ TranslationBuffer translations_;
+ ZoneList<LDeferredCode*> deferred_;
+ int osr_pc_offset_;
+
+ // Builder that keeps track of safepoints in the code. The table
+ // itself is emitted at the end of the generated code.
+ SafepointTableBuilder safepoints_;
+
+ // Compiler from a set of parallel moves to a sequential list of moves.
+ LGapResolver resolver_;
+
+ friend class LDeferredCode;
+ friend class LEnvironment;
+ friend class SafepointGenerator;
+ DISALLOW_COPY_AND_ASSIGN(LCodeGen);
+};
+
+
+class LDeferredCode: public ZoneObject {
+ public:
+ explicit LDeferredCode(LCodeGen* codegen)
+ : codegen_(codegen), external_exit_(NULL) {
+ codegen->AddDeferredCode(this);
+ }
+
+ virtual ~LDeferredCode() { }
+ virtual void Generate() = 0;
+
+ void SetExit(Label *exit) { external_exit_ = exit; }
+ Label* entry() { return &entry_; }
+ Label* exit() { return external_exit_ != NULL ? external_exit_ : &exit_; }
+
+ protected:
+ LCodeGen* codegen() const { return codegen_; }
+ MacroAssembler* masm() const { return codegen_->masm(); }
+
+ private:
+ LCodeGen* codegen_;
+ Label entry_;
+ Label exit_;
+ Label* external_exit_;
};
} } // namespace v8::internal
diff --git a/deps/v8/src/x64/lithium-x64.cc b/deps/v8/src/x64/lithium-x64.cc
new file mode 100644
index 0000000000..25a048bad0
--- /dev/null
+++ b/deps/v8/src/x64/lithium-x64.cc
@@ -0,0 +1,1435 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "v8.h"
+
+#if defined(V8_TARGET_ARCH_X64)
+
+#include "x64/lithium-x64.h"
+#include "x64/lithium-codegen-x64.h"
+
+namespace v8 {
+namespace internal {
+
+#define DEFINE_COMPILE(type) \
+ void L##type::CompileToNative(LCodeGen* generator) { \
+ generator->Do##type(this); \
+ }
+LITHIUM_CONCRETE_INSTRUCTION_LIST(DEFINE_COMPILE)
+#undef DEFINE_COMPILE
+
+LOsrEntry::LOsrEntry() {
+ for (int i = 0; i < Register::kNumAllocatableRegisters; ++i) {
+ register_spills_[i] = NULL;
+ }
+ for (int i = 0; i < DoubleRegister::kNumAllocatableRegisters; ++i) {
+ double_register_spills_[i] = NULL;
+ }
+}
+
+
+void LOsrEntry::MarkSpilledRegister(int allocation_index,
+ LOperand* spill_operand) {
+ ASSERT(spill_operand->IsStackSlot());
+ ASSERT(register_spills_[allocation_index] == NULL);
+ register_spills_[allocation_index] = spill_operand;
+}
+
+
+void LOsrEntry::MarkSpilledDoubleRegister(int allocation_index,
+ LOperand* spill_operand) {
+ ASSERT(spill_operand->IsDoubleStackSlot());
+ ASSERT(double_register_spills_[allocation_index] == NULL);
+ double_register_spills_[allocation_index] = spill_operand;
+}
+
+
+void LInstruction::PrintTo(StringStream* stream) {
+ stream->Add("%s ", this->Mnemonic());
+ if (HasResult()) {
+ PrintOutputOperandTo(stream);
+ }
+
+ PrintDataTo(stream);
+
+ if (HasEnvironment()) {
+ stream->Add(" ");
+ environment()->PrintTo(stream);
+ }
+
+ if (HasPointerMap()) {
+ stream->Add(" ");
+ pointer_map()->PrintTo(stream);
+ }
+}
+
+
+template<int R, int I, int T>
+void LTemplateInstruction<R, I, T>::PrintDataTo(StringStream* stream) {
+ for (int i = 0; i < I; i++) {
+ stream->Add(i == 0 ? "= " : " ");
+ inputs_.at(i)->PrintTo(stream);
+ }
+}
+
+
+template<int R, int I, int T>
+void LTemplateInstruction<R, I, T>::PrintOutputOperandTo(StringStream* stream) {
+ if (this->HasResult()) {
+ this->result()->PrintTo(stream);
+ stream->Add(" ");
+ }
+}
+
+
+void LLabel::PrintDataTo(StringStream* stream) {
+ LGap::PrintDataTo(stream);
+ LLabel* rep = replacement();
+ if (rep != NULL) {
+ stream->Add(" Dead block replaced with B%d", rep->block_id());
+ }
+}
+
+
+bool LGap::IsRedundant() const {
+ for (int i = 0; i < 4; i++) {
+ if (parallel_moves_[i] != NULL && !parallel_moves_[i]->IsRedundant()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+void LGap::PrintDataTo(StringStream* stream) {
+ for (int i = 0; i < 4; i++) {
+ stream->Add("(");
+ if (parallel_moves_[i] != NULL) {
+ parallel_moves_[i]->PrintDataTo(stream);
+ }
+ stream->Add(") ");
+ }
+}
+
+
+const char* LArithmeticD::Mnemonic() const {
+ switch (op()) {
+ case Token::ADD: return "add-d";
+ case Token::SUB: return "sub-d";
+ case Token::MUL: return "mul-d";
+ case Token::DIV: return "div-d";
+ case Token::MOD: return "mod-d";
+ default:
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
+const char* LArithmeticT::Mnemonic() const {
+ switch (op()) {
+ case Token::ADD: return "add-t";
+ case Token::SUB: return "sub-t";
+ case Token::MUL: return "mul-t";
+ case Token::MOD: return "mod-t";
+ case Token::DIV: return "div-t";
+ default:
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
+void LGoto::PrintDataTo(StringStream* stream) {
+ stream->Add("B%d", block_id());
+}
+
+
+void LBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("B%d | B%d on ", true_block_id(), false_block_id());
+ input()->PrintTo(stream);
+}
+
+
+void LCmpIDAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("if ");
+ left()->PrintTo(stream);
+ stream->Add(" %s ", Token::String(op()));
+ right()->PrintTo(stream);
+ stream->Add(" then B%d else B%d", true_block_id(), false_block_id());
+}
+
+
+void LIsNullAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("if ");
+ input()->PrintTo(stream);
+ stream->Add(is_strict() ? " === null" : " == null");
+ stream->Add(" then B%d else B%d", true_block_id(), false_block_id());
+}
+
+
+void LIsObjectAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("if is_object(");
+ input()->PrintTo(stream);
+ stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
+}
+
+
+void LIsSmiAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("if is_smi(");
+ input()->PrintTo(stream);
+ stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
+}
+
+
+void LHasInstanceTypeAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("if has_instance_type(");
+ input()->PrintTo(stream);
+ stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
+}
+
+
+void LHasCachedArrayIndexAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("if has_cached_array_index(");
+ input()->PrintTo(stream);
+ stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
+}
+
+
+void LClassOfTestAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("if class_of_test(");
+ input()->PrintTo(stream);
+ stream->Add(", \"%o\") then B%d else B%d",
+ *hydrogen()->class_name(),
+ true_block_id(),
+ false_block_id());
+}
+
+
+void LTypeofIs::PrintDataTo(StringStream* stream) {
+ input()->PrintTo(stream);
+ stream->Add(" == \"%s\"", *hydrogen()->type_literal()->ToCString());
+}
+
+
+void LTypeofIsAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("if typeof ");
+ input()->PrintTo(stream);
+ stream->Add(" == \"%s\" then B%d else B%d",
+ *hydrogen()->type_literal()->ToCString(),
+ true_block_id(), false_block_id());
+}
+
+
+void LCallConstantFunction::PrintDataTo(StringStream* stream) {
+ stream->Add("#%d / ", arity());
+}
+
+
+void LUnaryMathOperation::PrintDataTo(StringStream* stream) {
+ stream->Add("/%s ", hydrogen()->OpName());
+ input()->PrintTo(stream);
+}
+
+
+void LLoadContextSlot::PrintDataTo(StringStream* stream) {
+ stream->Add("(%d, %d)", context_chain_length(), slot_index());
+}
+
+
+void LCallKeyed::PrintDataTo(StringStream* stream) {
+ stream->Add("[rcx] #%d / ", arity());
+}
+
+
+void LCallNamed::PrintDataTo(StringStream* stream) {
+ SmartPointer<char> name_string = name()->ToCString();
+ stream->Add("%s #%d / ", *name_string, arity());
+}
+
+
+void LCallGlobal::PrintDataTo(StringStream* stream) {
+ SmartPointer<char> name_string = name()->ToCString();
+ stream->Add("%s #%d / ", *name_string, arity());
+}
+
+
+void LCallKnownGlobal::PrintDataTo(StringStream* stream) {
+ stream->Add("#%d / ", arity());
+}
+
+
+void LCallNew::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ input()->PrintTo(stream);
+ stream->Add(" #%d / ", arity());
+}
+
+
+void LClassOfTest::PrintDataTo(StringStream* stream) {
+ stream->Add("= class_of_test(");
+ input()->PrintTo(stream);
+ stream->Add(", \"%o\")", *hydrogen()->class_name());
+}
+
+
+void LAccessArgumentsAt::PrintDataTo(StringStream* stream) {
+ arguments()->PrintTo(stream);
+
+ stream->Add(" length ");
+ length()->PrintTo(stream);
+
+ stream->Add(" index ");
+ index()->PrintTo(stream);
+}
+
+
+int LChunk::GetNextSpillIndex(bool is_double) {
+ return spill_slot_count_++;
+}
+
+
+LOperand* LChunk::GetNextSpillSlot(bool is_double) {
+ // All stack slots are Double stack slots on x64.
+ // Alternatively, at some point, start using half-size
+ // stack slots for int32 values.
+ int index = GetNextSpillIndex(is_double);
+ if (is_double) {
+ return LDoubleStackSlot::Create(index);
+ } else {
+ return LStackSlot::Create(index);
+ }
+}
+
+
+void LChunk::MarkEmptyBlocks() {
+ HPhase phase("Mark empty blocks", this);
+ for (int i = 0; i < graph()->blocks()->length(); ++i) {
+ HBasicBlock* block = graph()->blocks()->at(i);
+ int first = block->first_instruction_index();
+ int last = block->last_instruction_index();
+ LInstruction* first_instr = instructions()->at(first);
+ LInstruction* last_instr = instructions()->at(last);
+
+ LLabel* label = LLabel::cast(first_instr);
+ if (last_instr->IsGoto()) {
+ LGoto* goto_instr = LGoto::cast(last_instr);
+ if (!goto_instr->include_stack_check() &&
+ label->IsRedundant() &&
+ !label->is_loop_header()) {
+ bool can_eliminate = true;
+ for (int i = first + 1; i < last && can_eliminate; ++i) {
+ LInstruction* cur = instructions()->at(i);
+ if (cur->IsGap()) {
+ LGap* gap = LGap::cast(cur);
+ if (!gap->IsRedundant()) {
+ can_eliminate = false;
+ }
+ } else {
+ can_eliminate = false;
+ }
+ }
+
+ if (can_eliminate) {
+ label->set_replacement(GetLabel(goto_instr->block_id()));
+ }
+ }
+ }
+ }
+}
+
+
+void LStoreNamed::PrintDataTo(StringStream* stream) {
+ object()->PrintTo(stream);
+ stream->Add(".");
+ stream->Add(*String::cast(*name())->ToCString());
+ stream->Add(" <- ");
+ value()->PrintTo(stream);
+}
+
+
+void LStoreKeyed::PrintDataTo(StringStream* stream) {
+ object()->PrintTo(stream);
+ stream->Add("[");
+ key()->PrintTo(stream);
+ stream->Add("] <- ");
+ value()->PrintTo(stream);
+}
+
+
+int LChunk::AddInstruction(LInstruction* instr, HBasicBlock* block) {
+ LGap* gap = new LGap(block);
+ int index = -1;
+ if (instr->IsControl()) {
+ instructions_.Add(gap);
+ index = instructions_.length();
+ instructions_.Add(instr);
+ } else {
+ index = instructions_.length();
+ instructions_.Add(instr);
+ instructions_.Add(gap);
+ }
+ if (instr->HasPointerMap()) {
+ pointer_maps_.Add(instr->pointer_map());
+ instr->pointer_map()->set_lithium_position(index);
+ }
+ return index;
+}
+
+
+LConstantOperand* LChunk::DefineConstantOperand(HConstant* constant) {
+ return LConstantOperand::Create(constant->id());
+}
+
+
+int LChunk::GetParameterStackSlot(int index) const {
+ // The receiver is at index 0, the first parameter at index 1, so we
+ // shift all parameter indexes down by the number of parameters, and
+ // make sure they end up negative so they are distinguishable from
+ // spill slots.
+ int result = index - graph()->info()->scope()->num_parameters() - 1;
+ ASSERT(result < 0);
+ return result;
+}
+
+// A parameter relative to ebp in the arguments stub.
+int LChunk::ParameterAt(int index) {
+ ASSERT(-1 <= index); // -1 is the receiver.
+ return (1 + graph()->info()->scope()->num_parameters() - index) *
+ kPointerSize;
+}
+
+
+LGap* LChunk::GetGapAt(int index) const {
+ return LGap::cast(instructions_[index]);
+}
+
+
+bool LChunk::IsGapAt(int index) const {
+ return instructions_[index]->IsGap();
+}
+
+
+int LChunk::NearestGapPos(int index) const {
+ while (!IsGapAt(index)) index--;
+ return index;
+}
+
+
+void LChunk::AddGapMove(int index, LOperand* from, LOperand* to) {
+ GetGapAt(index)->GetOrCreateParallelMove(LGap::START)->AddMove(from, to);
+}
+
+
+Handle<Object> LChunk::LookupLiteral(LConstantOperand* operand) const {
+ return HConstant::cast(graph_->LookupValue(operand->index()))->handle();
+}
+
+
+Representation LChunk::LookupLiteralRepresentation(
+ LConstantOperand* operand) const {
+ return graph_->LookupValue(operand->index())->representation();
+}
+
+
+LChunk* LChunkBuilder::Build() {
+ ASSERT(is_unused());
+ chunk_ = new LChunk(graph());
+ HPhase phase("Building chunk", chunk_);
+ status_ = BUILDING;
+ const ZoneList<HBasicBlock*>* blocks = graph()->blocks();
+ for (int i = 0; i < blocks->length(); i++) {
+ HBasicBlock* next = NULL;
+ if (i < blocks->length() - 1) next = blocks->at(i + 1);
+ DoBasicBlock(blocks->at(i), next);
+ if (is_aborted()) return NULL;
+ }
+ status_ = DONE;
+ return chunk_;
+}
+
+
+void LChunkBuilder::Abort(const char* format, ...) {
+ if (FLAG_trace_bailout) {
+ SmartPointer<char> debug_name = graph()->debug_name()->ToCString();
+ PrintF("Aborting LChunk building in @\"%s\": ", *debug_name);
+ va_list arguments;
+ va_start(arguments, format);
+ OS::VPrint(format, arguments);
+ va_end(arguments);
+ PrintF("\n");
+ }
+ status_ = ABORTED;
+}
+
+
+LRegister* LChunkBuilder::ToOperand(Register reg) {
+ return LRegister::Create(Register::ToAllocationIndex(reg));
+}
+
+
+LUnallocated* LChunkBuilder::ToUnallocated(Register reg) {
+ return new LUnallocated(LUnallocated::FIXED_REGISTER,
+ Register::ToAllocationIndex(reg));
+}
+
+
+LUnallocated* LChunkBuilder::ToUnallocated(XMMRegister reg) {
+ return new LUnallocated(LUnallocated::FIXED_DOUBLE_REGISTER,
+ XMMRegister::ToAllocationIndex(reg));
+}
+
+
+LOperand* LChunkBuilder::UseFixed(HValue* value, Register fixed_register) {
+ return Use(value, ToUnallocated(fixed_register));
+}
+
+
+LOperand* LChunkBuilder::UseFixedDouble(HValue* value, XMMRegister reg) {
+ return Use(value, ToUnallocated(reg));
+}
+
+
+LOperand* LChunkBuilder::UseRegister(HValue* value) {
+ return Use(value, new LUnallocated(LUnallocated::MUST_HAVE_REGISTER));
+}
+
+
+LOperand* LChunkBuilder::UseRegisterAtStart(HValue* value) {
+ return Use(value,
+ new LUnallocated(LUnallocated::MUST_HAVE_REGISTER,
+ LUnallocated::USED_AT_START));
+}
+
+
+LOperand* LChunkBuilder::UseTempRegister(HValue* value) {
+ return Use(value, new LUnallocated(LUnallocated::WRITABLE_REGISTER));
+}
+
+
+LOperand* LChunkBuilder::Use(HValue* value) {
+ return Use(value, new LUnallocated(LUnallocated::NONE));
+}
+
+
+LOperand* LChunkBuilder::UseAtStart(HValue* value) {
+ return Use(value, new LUnallocated(LUnallocated::NONE,
+ LUnallocated::USED_AT_START));
+}
+
+
+LOperand* LChunkBuilder::UseOrConstant(HValue* value) {
+ return value->IsConstant()
+ ? chunk_->DefineConstantOperand(HConstant::cast(value))
+ : Use(value);
+}
+
+
+LOperand* LChunkBuilder::UseOrConstantAtStart(HValue* value) {
+ return value->IsConstant()
+ ? chunk_->DefineConstantOperand(HConstant::cast(value))
+ : UseAtStart(value);
+}
+
+
+LOperand* LChunkBuilder::UseRegisterOrConstant(HValue* value) {
+ return value->IsConstant()
+ ? chunk_->DefineConstantOperand(HConstant::cast(value))
+ : UseRegister(value);
+}
+
+
+LOperand* LChunkBuilder::UseRegisterOrConstantAtStart(HValue* value) {
+ return value->IsConstant()
+ ? chunk_->DefineConstantOperand(HConstant::cast(value))
+ : UseRegisterAtStart(value);
+}
+
+
+LOperand* LChunkBuilder::Use(HValue* value, LUnallocated* operand) {
+ if (value->EmitAtUses()) {
+ HInstruction* instr = HInstruction::cast(value);
+ VisitInstruction(instr);
+ }
+ allocator_->RecordUse(value, operand);
+ return operand;
+}
+
+
+template<int I, int T>
+LInstruction* LChunkBuilder::Define(LTemplateInstruction<1, I, T>* instr,
+ LUnallocated* result) {
+ allocator_->RecordDefinition(current_instruction_, result);
+ instr->set_result(result);
+ return instr;
+}
+
+
+template<int I, int T>
+LInstruction* LChunkBuilder::Define(LTemplateInstruction<1, I, T>* instr) {
+ return Define(instr, new LUnallocated(LUnallocated::NONE));
+}
+
+
+template<int I, int T>
+LInstruction* LChunkBuilder::DefineAsRegister(
+ LTemplateInstruction<1, I, T>* instr) {
+ return Define(instr, new LUnallocated(LUnallocated::MUST_HAVE_REGISTER));
+}
+
+
+template<int I, int T>
+LInstruction* LChunkBuilder::DefineAsSpilled(
+ LTemplateInstruction<1, I, T>* instr,
+ int index) {
+ return Define(instr, new LUnallocated(LUnallocated::FIXED_SLOT, index));
+}
+
+
+template<int I, int T>
+LInstruction* LChunkBuilder::DefineSameAsFirst(
+ LTemplateInstruction<1, I, T>* instr) {
+ return Define(instr, new LUnallocated(LUnallocated::SAME_AS_FIRST_INPUT));
+}
+
+
+template<int I, int T>
+LInstruction* LChunkBuilder::DefineFixed(LTemplateInstruction<1, I, T>* instr,
+ Register reg) {
+ return Define(instr, ToUnallocated(reg));
+}
+
+
+template<int I, int T>
+LInstruction* LChunkBuilder::DefineFixedDouble(
+ LTemplateInstruction<1, I, T>* instr,
+ XMMRegister reg) {
+ return Define(instr, ToUnallocated(reg));
+}
+
+
+LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) {
+ HEnvironment* hydrogen_env = current_block_->last_environment();
+ instr->set_environment(CreateEnvironment(hydrogen_env));
+ return instr;
+}
+
+
+LInstruction* LChunkBuilder::SetInstructionPendingDeoptimizationEnvironment(
+ LInstruction* instr, int ast_id) {
+ ASSERT(instructions_pending_deoptimization_environment_ == NULL);
+ ASSERT(pending_deoptimization_ast_id_ == AstNode::kNoNumber);
+ instructions_pending_deoptimization_environment_ = instr;
+ pending_deoptimization_ast_id_ = ast_id;
+ return instr;
+}
+
+
+void LChunkBuilder::ClearInstructionPendingDeoptimizationEnvironment() {
+ instructions_pending_deoptimization_environment_ = NULL;
+ pending_deoptimization_ast_id_ = AstNode::kNoNumber;
+}
+
+
+LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr,
+ HInstruction* hinstr,
+ CanDeoptimize can_deoptimize) {
+ allocator_->MarkAsCall();
+ instr = AssignPointerMap(instr);
+
+ if (hinstr->HasSideEffects()) {
+ ASSERT(hinstr->next()->IsSimulate());
+ HSimulate* sim = HSimulate::cast(hinstr->next());
+ instr = SetInstructionPendingDeoptimizationEnvironment(
+ instr, sim->ast_id());
+ }
+
+ // If instruction does not have side-effects lazy deoptimization
+ // after the call will try to deoptimize to the point before the call.
+ // Thus we still need to attach environment to this call even if
+ // call sequence can not deoptimize eagerly.
+ bool needs_environment =
+ (can_deoptimize == CAN_DEOPTIMIZE_EAGERLY) || !hinstr->HasSideEffects();
+ if (needs_environment && !instr->HasEnvironment()) {
+ instr = AssignEnvironment(instr);
+ }
+
+ return instr;
+}
+
+
+LInstruction* LChunkBuilder::MarkAsSaveDoubles(LInstruction* instr) {
+ allocator_->MarkAsSaveDoubles();
+ return instr;
+}
+
+
+LInstruction* LChunkBuilder::AssignPointerMap(LInstruction* instr) {
+ ASSERT(!instr->HasPointerMap());
+ instr->set_pointer_map(new LPointerMap(position_));
+ return instr;
+}
+
+
+LUnallocated* LChunkBuilder::TempRegister() {
+ LUnallocated* operand = new LUnallocated(LUnallocated::MUST_HAVE_REGISTER);
+ allocator_->RecordTemporary(operand);
+ return operand;
+}
+
+
+LOperand* LChunkBuilder::FixedTemp(Register reg) {
+ LUnallocated* operand = ToUnallocated(reg);
+ allocator_->RecordTemporary(operand);
+ return operand;
+}
+
+
+LOperand* LChunkBuilder::FixedTemp(XMMRegister reg) {
+ LUnallocated* operand = ToUnallocated(reg);
+ allocator_->RecordTemporary(operand);
+ return operand;
+}
+
+
+LInstruction* LChunkBuilder::DoBlockEntry(HBlockEntry* instr) {
+ return new LLabel(instr->block());
+}
+
+
+LInstruction* LChunkBuilder::DoDeoptimize(HDeoptimize* instr) {
+ return AssignEnvironment(new LDeoptimize);
+}
+
+
+LInstruction* LChunkBuilder::DoBit(Token::Value op,
+ HBitwiseBinaryOperation* instr) {
+ Abort("Unimplemented: %s", "DoBit");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoArithmeticD(Token::Value op,
+ HArithmeticBinaryOperation* instr) {
+ Abort("Unimplemented: %s", "DoArithmeticD");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoArithmeticT(Token::Value op,
+ HArithmeticBinaryOperation* instr) {
+ Abort("Unimplemented: %s", "DoArithmeticT");
+ return NULL;
+}
+
+
+void LChunkBuilder::DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block) {
+ ASSERT(is_building());
+ current_block_ = block;
+ next_block_ = next_block;
+ if (block->IsStartBlock()) {
+ block->UpdateEnvironment(graph_->start_environment());
+ argument_count_ = 0;
+ } else if (block->predecessors()->length() == 1) {
+ // We have a single predecessor => copy environment and outgoing
+ // argument count from the predecessor.
+ ASSERT(block->phis()->length() == 0);
+ HBasicBlock* pred = block->predecessors()->at(0);
+ HEnvironment* last_environment = pred->last_environment();
+ ASSERT(last_environment != NULL);
+ // Only copy the environment, if it is later used again.
+ if (pred->end()->SecondSuccessor() == NULL) {
+ ASSERT(pred->end()->FirstSuccessor() == block);
+ } else {
+ if (pred->end()->FirstSuccessor()->block_id() > block->block_id() ||
+ pred->end()->SecondSuccessor()->block_id() > block->block_id()) {
+ last_environment = last_environment->Copy();
+ }
+ }
+ block->UpdateEnvironment(last_environment);
+ ASSERT(pred->argument_count() >= 0);
+ argument_count_ = pred->argument_count();
+ } else {
+ // We are at a state join => process phis.
+ HBasicBlock* pred = block->predecessors()->at(0);
+ // No need to copy the environment, it cannot be used later.
+ HEnvironment* last_environment = pred->last_environment();
+ for (int i = 0; i < block->phis()->length(); ++i) {
+ HPhi* phi = block->phis()->at(i);
+ last_environment->SetValueAt(phi->merged_index(), phi);
+ }
+ for (int i = 0; i < block->deleted_phis()->length(); ++i) {
+ last_environment->SetValueAt(block->deleted_phis()->at(i),
+ graph_->GetConstantUndefined());
+ }
+ block->UpdateEnvironment(last_environment);
+ // Pick up the outgoing argument count of one of the predecessors.
+ argument_count_ = pred->argument_count();
+ }
+ HInstruction* current = block->first();
+ int start = chunk_->instructions()->length();
+ while (current != NULL && !is_aborted()) {
+ // Code for constants in registers is generated lazily.
+ if (!current->EmitAtUses()) {
+ VisitInstruction(current);
+ }
+ current = current->next();
+ }
+ int end = chunk_->instructions()->length() - 1;
+ if (end >= start) {
+ block->set_first_instruction_index(start);
+ block->set_last_instruction_index(end);
+ }
+ block->set_argument_count(argument_count_);
+ next_block_ = NULL;
+ current_block_ = NULL;
+}
+
+
+void LChunkBuilder::VisitInstruction(HInstruction* current) {
+ HInstruction* old_current = current_instruction_;
+ current_instruction_ = current;
+ allocator_->BeginInstruction();
+ if (current->has_position()) position_ = current->position();
+ LInstruction* instr = current->CompileToLithium(this);
+
+ if (instr != NULL) {
+ if (FLAG_stress_pointer_maps && !instr->HasPointerMap()) {
+ instr = AssignPointerMap(instr);
+ }
+ if (FLAG_stress_environments && !instr->HasEnvironment()) {
+ instr = AssignEnvironment(instr);
+ }
+ if (current->IsBranch()) {
+ instr->set_hydrogen_value(HBranch::cast(current)->value());
+ } else {
+ instr->set_hydrogen_value(current);
+ }
+
+ int index = chunk_->AddInstruction(instr, current_block_);
+ allocator_->SummarizeInstruction(index);
+ } else {
+ // This instruction should be omitted.
+ allocator_->OmitInstruction();
+ }
+ current_instruction_ = old_current;
+}
+
+
+LEnvironment* LChunkBuilder::CreateEnvironment(HEnvironment* hydrogen_env) {
+ if (hydrogen_env == NULL) return NULL;
+
+ LEnvironment* outer = CreateEnvironment(hydrogen_env->outer());
+ int ast_id = hydrogen_env->ast_id();
+ ASSERT(ast_id != AstNode::kNoNumber);
+ int value_count = hydrogen_env->length();
+ LEnvironment* result = new LEnvironment(hydrogen_env->closure(),
+ ast_id,
+ hydrogen_env->parameter_count(),
+ argument_count_,
+ value_count,
+ outer);
+ int argument_index = 0;
+ for (int i = 0; i < value_count; ++i) {
+ HValue* value = hydrogen_env->values()->at(i);
+ LOperand* op = NULL;
+ if (value->IsArgumentsObject()) {
+ op = NULL;
+ } else if (value->IsPushArgument()) {
+ op = new LArgument(argument_index++);
+ } else {
+ op = UseOrConstant(value);
+ if (op->IsUnallocated()) {
+ LUnallocated* unalloc = LUnallocated::cast(op);
+ unalloc->set_policy(LUnallocated::ANY);
+ }
+ }
+ result->AddValue(op, value->representation());
+ }
+
+ return result;
+}
+
+
+LInstruction* LChunkBuilder::DoGoto(HGoto* instr) {
+ LGoto* result = new LGoto(instr->FirstSuccessor()->block_id(),
+ instr->include_stack_check());
+ return (instr->include_stack_check())
+ ? AssignPointerMap(result)
+ : result;
+}
+
+
+LInstruction* LChunkBuilder::DoBranch(HBranch* instr) {
+ Abort("Unimplemented: %s", "DoBranch");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCompareMapAndBranch(
+ HCompareMapAndBranch* instr) {
+ Abort("Unimplemented: %s", "DoCompareMapAndBranch");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoArgumentsLength(HArgumentsLength* length) {
+ Abort("Unimplemented: %s", "DoArgumentsLength");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) {
+ Abort("Unimplemented: %s", "DoArgumentsElements");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
+ Abort("Unimplemented: %s", "DoInstanceOf");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal(
+ HInstanceOfKnownGlobal* instr) {
+ Abort("Unimplemented: %s", "DoInstanceOfKnownGlobal");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoApplyArguments(HApplyArguments* instr) {
+ Abort("Unimplemented: %s", "DoApplyArguments");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoPushArgument(HPushArgument* instr) {
+ Abort("Unimplemented: %s", "DoPushArgument");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoGlobalObject(HGlobalObject* instr) {
+ Abort("Unimplemented: %s", "DoGlobalObject");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoGlobalReceiver(HGlobalReceiver* instr) {
+ Abort("Unimplemented: %s", "DoGlobalReceiver");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCallConstantFunction(
+ HCallConstantFunction* instr) {
+ Abort("Unimplemented: %s", "DoCallConstantFunction");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
+ Abort("Unimplemented: %s", "DoUnaryMathOperation");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCallKeyed(HCallKeyed* instr) {
+ Abort("Unimplemented: %s", "DoCallKeyed");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCallNamed(HCallNamed* instr) {
+ Abort("Unimplemented: %s", "DoCallNamed");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCallGlobal(HCallGlobal* instr) {
+ Abort("Unimplemented: %s", "DoCallGlobal");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCallKnownGlobal(HCallKnownGlobal* instr) {
+ Abort("Unimplemented: %s", "DoCallKnownGlobal");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCallNew(HCallNew* instr) {
+ Abort("Unimplemented: %s", "DoCallNew");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCallFunction(HCallFunction* instr) {
+ Abort("Unimplemented: %s", "DoCallFunction");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCallRuntime(HCallRuntime* instr) {
+ Abort("Unimplemented: %s", "DoCallRuntime");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoShr(HShr* instr) {
+ Abort("Unimplemented: %s", "DoShr");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoSar(HSar* instr) {
+ Abort("Unimplemented: %s", "DoSar");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoShl(HShl* instr) {
+ Abort("Unimplemented: %s", "DoShl");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoBitAnd(HBitAnd* instr) {
+ Abort("Unimplemented: %s", "DoBitAnd");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoBitNot(HBitNot* instr) {
+ Abort("Unimplemented: %s", "DoBitNot");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoBitOr(HBitOr* instr) {
+ Abort("Unimplemented: %s", "DoBitOr");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoBitXor(HBitXor* instr) {
+ Abort("Unimplemented: %s", "DoBitXor");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoDiv(HDiv* instr) {
+ Abort("Unimplemented: %s", "DoDiv");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoMod(HMod* instr) {
+ Abort("Unimplemented: %s", "DoMod");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoMul(HMul* instr) {
+ Abort("Unimplemented: %s", "DoMul");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoSub(HSub* instr) {
+ Abort("Unimplemented: %s", "DoSub");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoAdd(HAdd* instr) {
+ Abort("Unimplemented: %s", "DoAdd");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoPower(HPower* instr) {
+ Abort("Unimplemented: %s", "DoPower");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCompare(HCompare* instr) {
+ Abort("Unimplemented: %s", "DoCompare");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCompareJSObjectEq(
+ HCompareJSObjectEq* instr) {
+ Abort("Unimplemented: %s", "DoCompareJSObjectEq");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoIsNull(HIsNull* instr) {
+ Abort("Unimplemented: %s", "DoIsNull");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoIsObject(HIsObject* instr) {
+ Abort("Unimplemented: %s", "DoIsObject");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoIsSmi(HIsSmi* instr) {
+ Abort("Unimplemented: %s", "DoIsSmi");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoHasInstanceType(HHasInstanceType* instr) {
+ Abort("Unimplemented: %s", "DoHasInstanceType");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoHasCachedArrayIndex(
+ HHasCachedArrayIndex* instr) {
+ Abort("Unimplemented: %s", "DoHasCachedArrayIndex");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoClassOfTest(HClassOfTest* instr) {
+ Abort("Unimplemented: %s", "DoClassOfTest");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoJSArrayLength(HJSArrayLength* instr) {
+ Abort("Unimplemented: %s", "DoJSArrayLength");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoFixedArrayLength(HFixedArrayLength* instr) {
+ Abort("Unimplemented: %s", "DoFixedArrayLength");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) {
+ Abort("Unimplemented: %s", "DoValueOf");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoBoundsCheck(HBoundsCheck* instr) {
+ Abort("Unimplemented: %s", "DoBoundsCheck");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoThrow(HThrow* instr) {
+ Abort("Unimplemented: %s", "DoThrow");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoChange(HChange* instr) {
+ Abort("Unimplemented: %s", "DoChange");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCheckNonSmi(HCheckNonSmi* instr) {
+ Abort("Unimplemented: %s", "DoCheckNonSmi");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCheckInstanceType(HCheckInstanceType* instr) {
+ Abort("Unimplemented: %s", "DoCheckInstanceType");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCheckPrototypeMaps(HCheckPrototypeMaps* instr) {
+ Abort("Unimplemented: %s", "DoCheckPrototypeMaps");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCheckSmi(HCheckSmi* instr) {
+ Abort("Unimplemented: %s", "DoCheckSmi");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCheckFunction(HCheckFunction* instr) {
+ Abort("Unimplemented: %s", "DoCheckFunction");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCheckMap(HCheckMap* instr) {
+ Abort("Unimplemented: %s", "DoCheckMap");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoReturn(HReturn* instr) {
+ return new LReturn(UseFixed(instr->value(), rax));
+}
+
+
+LInstruction* LChunkBuilder::DoConstant(HConstant* instr) {
+ Representation r = instr->representation();
+ if (r.IsInteger32()) {
+ int32_t value = instr->Integer32Value();
+ return DefineAsRegister(new LConstantI(value));
+ } else if (r.IsDouble()) {
+ double value = instr->DoubleValue();
+ return DefineAsRegister(new LConstantD(value));
+ } else if (r.IsTagged()) {
+ return DefineAsRegister(new LConstantT(instr->handle()));
+ } else {
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoLoadGlobal(HLoadGlobal* instr) {
+ Abort("Unimplemented: %s", "DoLoadGlobal");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoStoreGlobal(HStoreGlobal* instr) {
+ Abort("Unimplemented: %s", "DoStoreGlobal");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) {
+ Abort("Unimplemented: %s", "DoLoadContextSlot");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoLoadNamedField(HLoadNamedField* instr) {
+ Abort("Unimplemented: %s", "DoLoadNamedField");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoLoadNamedGeneric(HLoadNamedGeneric* instr) {
+ Abort("Unimplemented: %s", "DoLoadNamedGeneric");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoLoadFunctionPrototype(
+ HLoadFunctionPrototype* instr) {
+ Abort("Unimplemented: %s", "DoLoadFunctionPrototype");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoLoadElements(HLoadElements* instr) {
+ Abort("Unimplemented: %s", "DoLoadElements");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoLoadKeyedFastElement(
+ HLoadKeyedFastElement* instr) {
+ Abort("Unimplemented: %s", "DoLoadKeyedFastElement");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoLoadKeyedGeneric(HLoadKeyedGeneric* instr) {
+ Abort("Unimplemented: %s", "DoLoadKeyedGeneric");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoStoreKeyedFastElement(
+ HStoreKeyedFastElement* instr) {
+ Abort("Unimplemented: %s", "DoStoreKeyedFastElement");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) {
+ Abort("Unimplemented: %s", "DoStoreKeyedGeneric");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
+ Abort("Unimplemented: %s", "DoStoreNamedField");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
+ Abort("Unimplemented: %s", "DoStoreNamedGeneric");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) {
+ Abort("Unimplemented: %s", "DoArrayLiteral");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoObjectLiteral(HObjectLiteral* instr) {
+ Abort("Unimplemented: %s", "DoObjectLiteral");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoRegExpLiteral(HRegExpLiteral* instr) {
+ Abort("Unimplemented: %s", "DoRegExpLiteral");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoFunctionLiteral(HFunctionLiteral* instr) {
+ Abort("Unimplemented: %s", "DoFunctionLiteral");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoDeleteProperty(HDeleteProperty* instr) {
+ Abort("Unimplemented: %s", "DoDeleteProperty");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoOsrEntry(HOsrEntry* instr) {
+ Abort("Unimplemented: %s", "DoOsrEntry");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoParameter(HParameter* instr) {
+ int spill_index = chunk()->GetParameterStackSlot(instr->index());
+ return DefineAsSpilled(new LParameter, spill_index);
+}
+
+
+LInstruction* LChunkBuilder::DoUnknownOSRValue(HUnknownOSRValue* instr) {
+ Abort("Unimplemented: %s", "DoUnknownOSRValue");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCallStub(HCallStub* instr) {
+ Abort("Unimplemented: %s", "DoCallStub");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoArgumentsObject(HArgumentsObject* instr) {
+ Abort("Unimplemented: %s", "DoArgumentsObject");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) {
+ Abort("Unimplemented: %s", "DoAccessArgumentsAt");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoTypeof(HTypeof* instr) {
+ Abort("Unimplemented: %s", "DoTypeof");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoTypeofIs(HTypeofIs* instr) {
+ Abort("Unimplemented: %s", "DoTypeofIs");
+ return NULL;
+}
+
+LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
+ HEnvironment* env = current_block_->last_environment();
+ ASSERT(env != NULL);
+
+ env->set_ast_id(instr->ast_id());
+
+ env->Drop(instr->pop_count());
+ for (int i = 0; i < instr->values()->length(); ++i) {
+ HValue* value = instr->values()->at(i);
+ if (instr->HasAssignedIndexAt(i)) {
+ env->Bind(instr->GetAssignedIndexAt(i), value);
+ } else {
+ env->Push(value);
+ }
+ }
+ ASSERT(env->length() == instr->environment_length());
+
+ // If there is an instruction pending deoptimization environment create a
+ // lazy bailout instruction to capture the environment.
+ if (pending_deoptimization_ast_id_ == instr->ast_id()) {
+ LLazyBailout* lazy_bailout = new LLazyBailout;
+ LInstruction* result = AssignEnvironment(lazy_bailout);
+ instructions_pending_deoptimization_environment_->
+ set_deoptimization_environment(result->environment());
+ ClearInstructionPendingDeoptimizationEnvironment();
+ return result;
+ }
+
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoStackCheck(HStackCheck* instr) {
+ return MarkAsCall(new LStackCheck, instr);
+}
+
+
+LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
+ Abort("Unimplemented: %s", "DoEnterInlined");
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) {
+ Abort("Unimplemented: %s", "DoLeaveInlined");
+ return NULL;
+}
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/deps/v8/src/x64/lithium-x64.h b/deps/v8/src/x64/lithium-x64.h
index f66ec1689c..f3023f9792 100644
--- a/deps/v8/src/x64/lithium-x64.h
+++ b/deps/v8/src/x64/lithium-x64.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -30,6 +30,7 @@
#include "hydrogen.h"
#include "lithium-allocator.h"
+#include "lithium.h"
#include "safepoint-table.h"
namespace v8 {
@@ -37,62 +38,374 @@ namespace internal {
// Forward declarations.
class LCodeGen;
-class LEnvironment;
-class Translation;
+
+
+// Type hierarchy:
+//
+// LInstruction
+// LAccessArgumentsAt
+// LArgumentsElements
+// LArgumentsLength
+// LBinaryOperation
+// LAddI
+// LApplyArguments
+// LArithmeticD
+// LArithmeticT
+// LBitI
+// LBoundsCheck
+// LCmpID
+// LCmpIDAndBranch
+// LCmpJSObjectEq
+// LCmpJSObjectEqAndBranch
+// LCmpT
+// LDivI
+// LInstanceOf
+// LInstanceOfAndBranch
+// LInstanceOfKnownGlobal
+// LLoadKeyedFastElement
+// LLoadKeyedGeneric
+// LModI
+// LMulI
+// LPower
+// LShiftI
+// LSubI
+// LCallConstantFunction
+// LCallFunction
+// LCallGlobal
+// LCallKeyed
+// LCallKnownGlobal
+// LCallNamed
+// LCallRuntime
+// LCallStub
+// LCheckPrototypeMaps
+// LConstant
+// LConstantD
+// LConstantI
+// LConstantT
+// LDeoptimize
+// LFunctionLiteral
+// LGap
+// LLabel
+// LGlobalObject
+// LGlobalReceiver
+// LGoto
+// LLazyBailout
+// LLoadContextSlot
+// LLoadGlobal
+// LMaterializedLiteral
+// LArrayLiteral
+// LObjectLiteral
+// LRegExpLiteral
+// LOsrEntry
+// LParameter
+// LRegExpConstructResult
+// LStackCheck
+// LStoreKeyed
+// LStoreKeyedFastElement
+// LStoreKeyedGeneric
+// LStoreNamed
+// LStoreNamedField
+// LStoreNamedGeneric
+// LUnaryOperation
+// LBitNotI
+// LBranch
+// LCallNew
+// LCheckFunction
+// LCheckInstanceType
+// LCheckMap
+// LCheckSmi
+// LClassOfTest
+// LClassOfTestAndBranch
+// LDeleteProperty
+// LDoubleToI
+// LFixedArrayLength
+// LHasCachedArrayIndex
+// LHasCachedArrayIndexAndBranch
+// LHasInstanceType
+// LHasInstanceTypeAndBranch
+// LInteger32ToDouble
+// LIsNull
+// LIsNullAndBranch
+// LIsObject
+// LIsObjectAndBranch
+// LIsSmi
+// LIsSmiAndBranch
+// LJSArrayLength
+// LLoadNamedField
+// LLoadNamedGeneric
+// LLoadFunctionPrototype
+// LNumberTagD
+// LNumberTagI
+// LPushArgument
+// LReturn
+// LSmiTag
+// LStoreGlobal
+// LTaggedToI
+// LThrow
+// LTypeof
+// LTypeofIs
+// LTypeofIsAndBranch
+// LUnaryMathOperation
+// LValueOf
+// LUnknownOSRValue
+
+#define LITHIUM_ALL_INSTRUCTION_LIST(V) \
+ V(BinaryOperation) \
+ V(Constant) \
+ V(Call) \
+ V(MaterializedLiteral) \
+ V(StoreKeyed) \
+ V(StoreNamed) \
+ V(UnaryOperation) \
+ LITHIUM_CONCRETE_INSTRUCTION_LIST(V)
+
+
+#define LITHIUM_CONCRETE_INSTRUCTION_LIST(V) \
+ V(AccessArgumentsAt) \
+ V(AddI) \
+ V(ApplyArguments) \
+ V(ArgumentsElements) \
+ V(ArgumentsLength) \
+ V(ArithmeticD) \
+ V(ArithmeticT) \
+ V(ArrayLiteral) \
+ V(BitI) \
+ V(BitNotI) \
+ V(BoundsCheck) \
+ V(Branch) \
+ V(CallConstantFunction) \
+ V(CallFunction) \
+ V(CallGlobal) \
+ V(CallKeyed) \
+ V(CallKnownGlobal) \
+ V(CallNamed) \
+ V(CallNew) \
+ V(CallRuntime) \
+ V(CallStub) \
+ V(CheckFunction) \
+ V(CheckInstanceType) \
+ V(CheckMap) \
+ V(CheckPrototypeMaps) \
+ V(CheckSmi) \
+ V(CmpID) \
+ V(CmpIDAndBranch) \
+ V(CmpJSObjectEq) \
+ V(CmpJSObjectEqAndBranch) \
+ V(CmpMapAndBranch) \
+ V(CmpT) \
+ V(CmpTAndBranch) \
+ V(ConstantD) \
+ V(ConstantI) \
+ V(ConstantT) \
+ V(DeleteProperty) \
+ V(Deoptimize) \
+ V(DivI) \
+ V(DoubleToI) \
+ V(FunctionLiteral) \
+ V(Gap) \
+ V(GlobalObject) \
+ V(GlobalReceiver) \
+ V(Goto) \
+ V(FixedArrayLength) \
+ V(InstanceOf) \
+ V(InstanceOfAndBranch) \
+ V(InstanceOfKnownGlobal) \
+ V(Integer32ToDouble) \
+ V(IsNull) \
+ V(IsNullAndBranch) \
+ V(IsObject) \
+ V(IsObjectAndBranch) \
+ V(IsSmi) \
+ V(IsSmiAndBranch) \
+ V(JSArrayLength) \
+ V(HasInstanceType) \
+ V(HasInstanceTypeAndBranch) \
+ V(HasCachedArrayIndex) \
+ V(HasCachedArrayIndexAndBranch) \
+ V(ClassOfTest) \
+ V(ClassOfTestAndBranch) \
+ V(Label) \
+ V(LazyBailout) \
+ V(LoadContextSlot) \
+ V(LoadElements) \
+ V(LoadGlobal) \
+ V(LoadKeyedFastElement) \
+ V(LoadKeyedGeneric) \
+ V(LoadNamedField) \
+ V(LoadNamedGeneric) \
+ V(LoadFunctionPrototype) \
+ V(ModI) \
+ V(MulI) \
+ V(NumberTagD) \
+ V(NumberTagI) \
+ V(NumberUntagD) \
+ V(ObjectLiteral) \
+ V(OsrEntry) \
+ V(Parameter) \
+ V(Power) \
+ V(PushArgument) \
+ V(RegExpLiteral) \
+ V(Return) \
+ V(ShiftI) \
+ V(SmiTag) \
+ V(SmiUntag) \
+ V(StackCheck) \
+ V(StoreGlobal) \
+ V(StoreKeyedFastElement) \
+ V(StoreKeyedGeneric) \
+ V(StoreNamedField) \
+ V(StoreNamedGeneric) \
+ V(SubI) \
+ V(TaggedToI) \
+ V(Throw) \
+ V(Typeof) \
+ V(TypeofIs) \
+ V(TypeofIsAndBranch) \
+ V(UnaryMathOperation) \
+ V(UnknownOSRValue) \
+ V(ValueOf)
+
+
+#define DECLARE_INSTRUCTION(type) \
+ virtual bool Is##type() const { return true; } \
+ static L##type* cast(LInstruction* instr) { \
+ ASSERT(instr->Is##type()); \
+ return reinterpret_cast<L##type*>(instr); \
+ }
+
+
+#define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \
+ virtual void CompileToNative(LCodeGen* generator); \
+ virtual const char* Mnemonic() const { return mnemonic; } \
+ DECLARE_INSTRUCTION(type)
+
+
+#define DECLARE_HYDROGEN_ACCESSOR(type) \
+ H##type* hydrogen() const { \
+ return H##type::cast(hydrogen_value()); \
+ }
+
class LInstruction: public ZoneObject {
public:
- LInstruction() { }
+ LInstruction()
+ : hydrogen_value_(NULL) { }
virtual ~LInstruction() { }
- // Predicates should be generated by macro as in lithium-ia32.h.
- virtual bool IsLabel() const {
- UNIMPLEMENTED();
- return false;
- }
- virtual bool IsOsrEntry() const {
- UNIMPLEMENTED();
- return false;
- }
+ virtual void CompileToNative(LCodeGen* generator) = 0;
+ virtual const char* Mnemonic() const = 0;
+ virtual void PrintTo(StringStream* stream);
+ virtual void PrintDataTo(StringStream* stream) = 0;
+ virtual void PrintOutputOperandTo(StringStream* stream) = 0;
- LPointerMap* pointer_map() const {
- UNIMPLEMENTED();
- return NULL;
- }
+ // Declare virtual type testers.
+#define DECLARE_DO(type) virtual bool Is##type() const { return false; }
+ LITHIUM_ALL_INSTRUCTION_LIST(DECLARE_DO)
+#undef DECLARE_DO
+ virtual bool IsControl() const { return false; }
+
+ void set_environment(LEnvironment* env) { environment_.set(env); }
+ LEnvironment* environment() const { return environment_.get(); }
+ bool HasEnvironment() const { return environment_.is_set(); }
+
+ void set_pointer_map(LPointerMap* p) { pointer_map_.set(p); }
+ LPointerMap* pointer_map() const { return pointer_map_.get(); }
+ bool HasPointerMap() const { return pointer_map_.is_set(); }
+
+ virtual bool HasResult() const = 0;
- bool HasPointerMap() const {
- UNIMPLEMENTED();
- return false;
+ void set_hydrogen_value(HValue* value) { hydrogen_value_ = value; }
+ HValue* hydrogen_value() const { return hydrogen_value_; }
+
+ void set_deoptimization_environment(LEnvironment* env) {
+ deoptimization_environment_.set(env);
+ }
+ LEnvironment* deoptimization_environment() const {
+ return deoptimization_environment_.get();
+ }
+ bool HasDeoptimizationEnvironment() const {
+ return deoptimization_environment_.is_set();
}
- virtual void PrintTo(StringStream* stream) const { UNIMPLEMENTED(); }
+ private:
+ SetOncePointer<LEnvironment> environment_;
+ SetOncePointer<LPointerMap> pointer_map_;
+ HValue* hydrogen_value_;
+ SetOncePointer<LEnvironment> deoptimization_environment_;
};
-class LParallelMove : public ZoneObject {
+template<typename T, int N>
+class OperandContainer {
public:
- LParallelMove() { }
-
- void AddMove(LOperand* from, LOperand* to) {
- UNIMPLEMENTED();
+ OperandContainer() {
+ for (int i = 0; i < N; i++) elems_[i] = NULL;
}
+ int length() const { return N; }
+ T at(int i) const { return elems_[i]; }
+ void set_at(int i, T value) { elems_[i] = value; }
+ private:
+ T elems_[N];
+};
+
- const ZoneList<LMoveOperands>* move_operands() const {
- UNIMPLEMENTED();
+template<typename T>
+class OperandContainer<T, 0> {
+ public:
+ int length() const { return 0; }
+ T at(int i) const {
+ UNREACHABLE();
return NULL;
}
+ void set_at(int i, T value) {
+ UNREACHABLE();
+ }
};
-class LGap: public LInstruction {
+template<int R, int I, int T>
+class LTemplateInstruction: public LInstruction {
public:
- explicit LGap(HBasicBlock* block) { }
+ // Allow 0 or 1 output operands.
+ STATIC_ASSERT(R == 0 || R == 1);
+ virtual bool HasResult() const { return R != 0; }
+ void set_result(LOperand* operand) { outputs_.set_at(0, operand); }
+ LOperand* result() const { return outputs_.at(0); }
- HBasicBlock* block() const {
- UNIMPLEMENTED();
- return NULL;
+ int InputCount() const { return inputs_.length(); }
+ LOperand* InputAt(int i) const { return inputs_.at(i); }
+ void SetInputAt(int i, LOperand* operand) { inputs_.set_at(i, operand); }
+
+ int TempCount() const { return temps_.length(); }
+ LOperand* TempAt(int i) const { return temps_.at(i); }
+
+ virtual void PrintDataTo(StringStream* stream);
+ virtual void PrintOutputOperandTo(StringStream* stream);
+
+ private:
+ OperandContainer<LOperand*, R> outputs_;
+ OperandContainer<LOperand*, I> inputs_;
+ OperandContainer<LOperand*, T> temps_;
+};
+
+
+class LGap: public LTemplateInstruction<0, 0, 0> {
+ public:
+ explicit LGap(HBasicBlock* block)
+ : block_(block) {
+ parallel_moves_[BEFORE] = NULL;
+ parallel_moves_[START] = NULL;
+ parallel_moves_[END] = NULL;
+ parallel_moves_[AFTER] = NULL;
}
+ DECLARE_CONCRETE_INSTRUCTION(Gap, "gap")
+ virtual void PrintDataTo(StringStream* stream);
+
+ bool IsRedundant() const;
+
+ HBasicBlock* block() const { return block_; }
+
enum InnerPosition {
BEFORE,
START,
@@ -102,149 +415,1642 @@ class LGap: public LInstruction {
LAST_INNER_POSITION = AFTER
};
- LParallelMove* GetOrCreateParallelMove(InnerPosition pos) {
- UNIMPLEMENTED();
- return NULL;
+ LParallelMove* GetOrCreateParallelMove(InnerPosition pos) {
+ if (parallel_moves_[pos] == NULL) parallel_moves_[pos] = new LParallelMove;
+ return parallel_moves_[pos];
}
LParallelMove* GetParallelMove(InnerPosition pos) {
- UNIMPLEMENTED();
- return NULL;
+ return parallel_moves_[pos];
}
+
+ private:
+ LParallelMove* parallel_moves_[LAST_INNER_POSITION + 1];
+ HBasicBlock* block_;
+};
+
+
+class LGoto: public LTemplateInstruction<0, 0, 0> {
+ public:
+ LGoto(int block_id, bool include_stack_check = false)
+ : block_id_(block_id), include_stack_check_(include_stack_check) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(Goto, "goto")
+ virtual void PrintDataTo(StringStream* stream);
+ virtual bool IsControl() const { return true; }
+
+ int block_id() const { return block_id_; }
+ bool include_stack_check() const { return include_stack_check_; }
+
+ private:
+ int block_id_;
+ bool include_stack_check_;
+};
+
+
+class LLazyBailout: public LTemplateInstruction<0, 0, 0> {
+ public:
+ LLazyBailout() : gap_instructions_size_(0) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(LazyBailout, "lazy-bailout")
+
+ void set_gap_instructions_size(int gap_instructions_size) {
+ gap_instructions_size_ = gap_instructions_size;
+ }
+ int gap_instructions_size() { return gap_instructions_size_; }
+
+ private:
+ int gap_instructions_size_;
+};
+
+
+class LDeoptimize: public LTemplateInstruction<0, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(Deoptimize, "deoptimize")
};
class LLabel: public LGap {
public:
- explicit LLabel(HBasicBlock* block) : LGap(block) { }
+ explicit LLabel(HBasicBlock* block)
+ : LGap(block), replacement_(NULL) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(Label, "label")
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ int block_id() const { return block()->block_id(); }
+ bool is_loop_header() const { return block()->IsLoopHeader(); }
+ Label* label() { return &label_; }
+ LLabel* replacement() const { return replacement_; }
+ void set_replacement(LLabel* label) { replacement_ = label; }
+ bool HasReplacement() const { return replacement_ != NULL; }
+
+ private:
+ Label label_;
+ LLabel* replacement_;
+};
+
+
+class LParameter: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(Parameter, "parameter")
};
-class LOsrEntry: public LInstruction {
+class LCallStub: public LTemplateInstruction<1, 0, 0> {
public:
- // Function could be generated by a macro as in lithium-ia32.h.
- static LOsrEntry* cast(LInstruction* instr) {
- UNIMPLEMENTED();
- return NULL;
+ DECLARE_CONCRETE_INSTRUCTION(CallStub, "call-stub")
+ DECLARE_HYDROGEN_ACCESSOR(CallStub)
+
+ TranscendentalCache::Type transcendental_type() {
+ return hydrogen()->transcendental_type();
}
+};
- LOperand** SpilledRegisterArray() {
- UNIMPLEMENTED();
- return NULL;
+
+class LUnknownOSRValue: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(UnknownOSRValue, "unknown-osr-value")
+};
+
+
+template<int R>
+class LUnaryOperation: public LTemplateInstruction<R, 1, 0> {
+ public:
+ explicit LUnaryOperation<R>(LOperand* input) {
+ this->SetInputAt(0, input);
}
- LOperand** SpilledDoubleRegisterArray() {
- UNIMPLEMENTED();
- return NULL;
+
+ LOperand* input() const { return this->InputAt(0); }
+
+ DECLARE_INSTRUCTION(UnaryOperation)
+};
+
+
+template<int R>
+class LBinaryOperation: public LTemplateInstruction<R, 2, 0> {
+ public:
+ LBinaryOperation(LOperand* left, LOperand* right) {
+ this->SetInputAt(0, left);
+ this->SetInputAt(1, right);
}
- void MarkSpilledRegister(int allocation_index, LOperand* spill_operand) {
- UNIMPLEMENTED();
+ DECLARE_INSTRUCTION(BinaryOperation)
+
+ LOperand* left() const { return this->InputAt(0); }
+ LOperand* right() const { return this->InputAt(1); }
+};
+
+
+class LApplyArguments: public LTemplateInstruction<1, 4, 0> {
+ public:
+ LApplyArguments(LOperand* function,
+ LOperand* receiver,
+ LOperand* length,
+ LOperand* elements) {
+ this->SetInputAt(0, function);
+ this->SetInputAt(1, receiver);
+ this->SetInputAt(2, length);
+ this->SetInputAt(3, elements);
}
- void MarkSpilledDoubleRegister(int allocation_index,
- LOperand* spill_operand) {
- UNIMPLEMENTED();
+
+ DECLARE_CONCRETE_INSTRUCTION(ApplyArguments, "apply-arguments")
+
+ LOperand* function() const { return InputAt(0); }
+ LOperand* receiver() const { return InputAt(1); }
+ LOperand* length() const { return InputAt(2); }
+ LOperand* elements() const { return InputAt(3); }
+};
+
+
+class LAccessArgumentsAt: public LTemplateInstruction<1, 3, 0> {
+ public:
+ LAccessArgumentsAt(LOperand* arguments, LOperand* length, LOperand* index) {
+ this->SetInputAt(0, arguments);
+ this->SetInputAt(1, length);
+ this->SetInputAt(2, index);
}
+
+ DECLARE_CONCRETE_INSTRUCTION(AccessArgumentsAt, "access-arguments-at")
+
+ LOperand* arguments() const { return this->InputAt(0); }
+ LOperand* length() const { return this->InputAt(1); }
+ LOperand* index() const { return this->InputAt(2); }
+
+ virtual void PrintDataTo(StringStream* stream);
};
-class LPointerMap: public ZoneObject {
+class LArgumentsLength: public LUnaryOperation<1> {
public:
- explicit LPointerMap(int position) { }
+ explicit LArgumentsLength(LOperand* elements)
+ : LUnaryOperation<1>(elements) {}
- int lithium_position() const {
- UNIMPLEMENTED();
- return 0;
- }
+ DECLARE_CONCRETE_INSTRUCTION(ArgumentsLength, "arguments-length")
+};
- void RecordPointer(LOperand* op) { UNIMPLEMENTED(); }
+
+class LArgumentsElements: public LTemplateInstruction<1, 0, 0> {
+ public:
+ LArgumentsElements() { }
+
+ DECLARE_CONCRETE_INSTRUCTION(ArgumentsElements, "arguments-elements")
};
-class LChunk: public ZoneObject {
+class LModI: public LBinaryOperation<1> {
public:
- explicit LChunk(HGraph* graph) { }
+ LModI(LOperand* left, LOperand* right) : LBinaryOperation<1>(left, right) { }
- HGraph* graph() const {
- UNIMPLEMENTED();
- return NULL;
- }
+ DECLARE_CONCRETE_INSTRUCTION(ModI, "mod-i")
+ DECLARE_HYDROGEN_ACCESSOR(Mod)
+};
- const ZoneList<LPointerMap*>* pointer_maps() const {
- UNIMPLEMENTED();
- return NULL;
- }
- LOperand* GetNextSpillSlot(bool double_slot) {
- UNIMPLEMENTED();
- return NULL;
+class LDivI: public LBinaryOperation<1> {
+ public:
+ LDivI(LOperand* left, LOperand* right)
+ : LBinaryOperation<1>(left, right) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(DivI, "div-i")
+ DECLARE_HYDROGEN_ACCESSOR(Div)
+};
+
+
+class LMulI: public LBinaryOperation<1> {
+ public:
+ LMulI(LOperand* left, LOperand* right, LOperand* temp)
+ : LBinaryOperation<1>(left, right), temp_(temp) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(MulI, "mul-i")
+ DECLARE_HYDROGEN_ACCESSOR(Mul)
+
+ LOperand* temp() const { return temp_; }
+
+ private:
+ LOperand* temp_;
+};
+
+
+class LCmpID: public LBinaryOperation<1> {
+ public:
+ LCmpID(LOperand* left, LOperand* right)
+ : LBinaryOperation<1>(left, right) { }
+
+ Token::Value op() const { return hydrogen()->token(); }
+ bool is_double() const {
+ return hydrogen()->GetInputRepresentation().IsDouble();
}
- LConstantOperand* DefineConstantOperand(HConstant* constant) {
- UNIMPLEMENTED();
- return NULL;
+ DECLARE_CONCRETE_INSTRUCTION(CmpID, "cmp-id")
+ DECLARE_HYDROGEN_ACCESSOR(Compare)
+};
+
+
+class LCmpIDAndBranch: public LCmpID {
+ public:
+ LCmpIDAndBranch(LOperand* left,
+ LOperand* right,
+ int true_block_id,
+ int false_block_id)
+ : LCmpID(left, right),
+ true_block_id_(true_block_id),
+ false_block_id_(false_block_id) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpIDAndBranch, "cmp-id-and-branch")
+ virtual void PrintDataTo(StringStream* stream);
+ virtual bool IsControl() const { return true; }
+
+ int true_block_id() const { return true_block_id_; }
+ int false_block_id() const { return false_block_id_; }
+
+ private:
+ int true_block_id_;
+ int false_block_id_;
+};
+
+
+class LUnaryMathOperation: public LUnaryOperation<1> {
+ public:
+ explicit LUnaryMathOperation(LOperand* value)
+ : LUnaryOperation<1>(value) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(UnaryMathOperation, "unary-math-operation")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation)
+
+ virtual void PrintDataTo(StringStream* stream);
+ BuiltinFunctionId op() const { return hydrogen()->op(); }
+};
+
+
+class LCmpJSObjectEq: public LBinaryOperation<1> {
+ public:
+ LCmpJSObjectEq(LOperand* left, LOperand* right)
+ : LBinaryOperation<1>(left, right) {}
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpJSObjectEq, "cmp-jsobject-eq")
+};
+
+
+class LCmpJSObjectEqAndBranch: public LCmpJSObjectEq {
+ public:
+ LCmpJSObjectEqAndBranch(LOperand* left,
+ LOperand* right,
+ int true_block_id,
+ int false_block_id)
+ : LCmpJSObjectEq(left, right),
+ true_block_id_(true_block_id),
+ false_block_id_(false_block_id) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpJSObjectEqAndBranch,
+ "cmp-jsobject-eq-and-branch")
+
+ int true_block_id() const { return true_block_id_; }
+ int false_block_id() const { return false_block_id_; }
+
+ private:
+ int true_block_id_;
+ int false_block_id_;
+};
+
+
+class LIsNull: public LUnaryOperation<1> {
+ public:
+ explicit LIsNull(LOperand* value) : LUnaryOperation<1>(value) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsNull, "is-null")
+ DECLARE_HYDROGEN_ACCESSOR(IsNull)
+
+ bool is_strict() const { return hydrogen()->is_strict(); }
+};
+
+
+class LIsNullAndBranch: public LIsNull {
+ public:
+ LIsNullAndBranch(LOperand* value,
+ LOperand* temp,
+ int true_block_id,
+ int false_block_id)
+ : LIsNull(value),
+ temp_(temp),
+ true_block_id_(true_block_id),
+ false_block_id_(false_block_id) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsNullAndBranch, "is-null-and-branch")
+ virtual void PrintDataTo(StringStream* stream);
+ virtual bool IsControl() const { return true; }
+
+ int true_block_id() const { return true_block_id_; }
+ int false_block_id() const { return false_block_id_; }
+
+ LOperand* temp() const { return temp_; }
+
+ private:
+ LOperand* temp_;
+ int true_block_id_;
+ int false_block_id_;
+};
+
+
+class LIsObject: public LUnaryOperation<1> {
+ public:
+ LIsObject(LOperand* value, LOperand* temp)
+ : LUnaryOperation<1>(value), temp_(temp) {}
+
+ DECLARE_CONCRETE_INSTRUCTION(IsObject, "is-object")
+
+ LOperand* temp() const { return temp_; }
+
+ private:
+ LOperand* temp_;
+};
+
+
+class LIsObjectAndBranch: public LIsObject {
+ public:
+ LIsObjectAndBranch(LOperand* value,
+ LOperand* temp,
+ LOperand* temp2,
+ int true_block_id,
+ int false_block_id)
+ : LIsObject(value, temp),
+ temp2_(temp2),
+ true_block_id_(true_block_id),
+ false_block_id_(false_block_id) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsObjectAndBranch, "is-object-and-branch")
+ virtual void PrintDataTo(StringStream* stream);
+ virtual bool IsControl() const { return true; }
+
+ int true_block_id() const { return true_block_id_; }
+ int false_block_id() const { return false_block_id_; }
+
+ LOperand* temp2() const { return temp2_; }
+
+ private:
+ LOperand* temp2_;
+ int true_block_id_;
+ int false_block_id_;
+};
+
+
+class LIsSmi: public LUnaryOperation<1> {
+ public:
+ explicit LIsSmi(LOperand* value) : LUnaryOperation<1>(value) {}
+
+ DECLARE_CONCRETE_INSTRUCTION(IsSmi, "is-smi")
+ DECLARE_HYDROGEN_ACCESSOR(IsSmi)
+};
+
+
+class LIsSmiAndBranch: public LIsSmi {
+ public:
+ LIsSmiAndBranch(LOperand* value,
+ int true_block_id,
+ int false_block_id)
+ : LIsSmi(value),
+ true_block_id_(true_block_id),
+ false_block_id_(false_block_id) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsSmiAndBranch, "is-smi-and-branch")
+ virtual void PrintDataTo(StringStream* stream);
+ virtual bool IsControl() const { return true; }
+
+ int true_block_id() const { return true_block_id_; }
+ int false_block_id() const { return false_block_id_; }
+
+ private:
+ int true_block_id_;
+ int false_block_id_;
+};
+
+
+class LHasInstanceType: public LUnaryOperation<1> {
+ public:
+ explicit LHasInstanceType(LOperand* value)
+ : LUnaryOperation<1>(value) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(HasInstanceType, "has-instance-type")
+ DECLARE_HYDROGEN_ACCESSOR(HasInstanceType)
+
+ InstanceType TestType(); // The type to test against when generating code.
+ Condition BranchCondition(); // The branch condition for 'true'.
+};
+
+
+class LHasInstanceTypeAndBranch: public LHasInstanceType {
+ public:
+ LHasInstanceTypeAndBranch(LOperand* value,
+ LOperand* temporary,
+ int true_block_id,
+ int false_block_id)
+ : LHasInstanceType(value),
+ temp_(temporary),
+ true_block_id_(true_block_id),
+ false_block_id_(false_block_id) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(HasInstanceTypeAndBranch,
+ "has-instance-type-and-branch")
+ virtual void PrintDataTo(StringStream* stream);
+ virtual bool IsControl() const { return true; }
+
+ int true_block_id() const { return true_block_id_; }
+ int false_block_id() const { return false_block_id_; }
+
+ LOperand* temp() { return temp_; }
+
+ private:
+ LOperand* temp_;
+ int true_block_id_;
+ int false_block_id_;
+};
+
+
+class LHasCachedArrayIndex: public LUnaryOperation<1> {
+ public:
+ explicit LHasCachedArrayIndex(LOperand* value) : LUnaryOperation<1>(value) {}
+
+ DECLARE_CONCRETE_INSTRUCTION(HasCachedArrayIndex, "has-cached-array-index")
+ DECLARE_HYDROGEN_ACCESSOR(HasCachedArrayIndex)
+};
+
+
+class LHasCachedArrayIndexAndBranch: public LHasCachedArrayIndex {
+ public:
+ LHasCachedArrayIndexAndBranch(LOperand* value,
+ int true_block_id,
+ int false_block_id)
+ : LHasCachedArrayIndex(value),
+ true_block_id_(true_block_id),
+ false_block_id_(false_block_id) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(HasCachedArrayIndexAndBranch,
+ "has-cached-array-index-and-branch")
+ virtual void PrintDataTo(StringStream* stream);
+ virtual bool IsControl() const { return true; }
+
+ int true_block_id() const { return true_block_id_; }
+ int false_block_id() const { return false_block_id_; }
+
+ private:
+ int true_block_id_;
+ int false_block_id_;
+};
+
+
+class LClassOfTest: public LUnaryOperation<1> {
+ public:
+ LClassOfTest(LOperand* value, LOperand* temp)
+ : LUnaryOperation<1>(value), temporary_(temp) {}
+
+ DECLARE_CONCRETE_INSTRUCTION(ClassOfTest, "class-of-test")
+ DECLARE_HYDROGEN_ACCESSOR(ClassOfTest)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ LOperand* temporary() { return temporary_; }
+
+ private:
+ LOperand* temporary_;
+};
+
+
+class LClassOfTestAndBranch: public LClassOfTest {
+ public:
+ LClassOfTestAndBranch(LOperand* value,
+ LOperand* temporary,
+ LOperand* temporary2,
+ int true_block_id,
+ int false_block_id)
+ : LClassOfTest(value, temporary),
+ temporary2_(temporary2),
+ true_block_id_(true_block_id),
+ false_block_id_(false_block_id) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClassOfTestAndBranch,
+ "class-of-test-and-branch")
+ virtual void PrintDataTo(StringStream* stream);
+ virtual bool IsControl() const { return true; }
+
+ int true_block_id() const { return true_block_id_; }
+ int false_block_id() const { return false_block_id_; }
+ LOperand* temporary2() { return temporary2_; }
+
+ private:
+ LOperand* temporary2_;
+ int true_block_id_;
+ int false_block_id_;
+};
+
+
+class LCmpT: public LBinaryOperation<1> {
+ public:
+ LCmpT(LOperand* left, LOperand* right) : LBinaryOperation<1>(left, right) {}
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t")
+ DECLARE_HYDROGEN_ACCESSOR(Compare)
+
+ Token::Value op() const { return hydrogen()->token(); }
+};
+
+
+class LCmpTAndBranch: public LCmpT {
+ public:
+ LCmpTAndBranch(LOperand* left,
+ LOperand* right,
+ int true_block_id,
+ int false_block_id)
+ : LCmpT(left, right),
+ true_block_id_(true_block_id),
+ false_block_id_(false_block_id) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpTAndBranch, "cmp-t-and-branch")
+
+ int true_block_id() const { return true_block_id_; }
+ int false_block_id() const { return false_block_id_; }
+
+ private:
+ int true_block_id_;
+ int false_block_id_;
+};
+
+
+class LInstanceOf: public LBinaryOperation<1> {
+ public:
+ LInstanceOf(LOperand* left, LOperand* right)
+ : LBinaryOperation<1>(left, right) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of")
+};
+
+
+class LInstanceOfAndBranch: public LInstanceOf {
+ public:
+ LInstanceOfAndBranch(LOperand* left,
+ LOperand* right,
+ int true_block_id,
+ int false_block_id)
+ : LInstanceOf(left, right),
+ true_block_id_(true_block_id),
+ false_block_id_(false_block_id) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstanceOfAndBranch, "instance-of-and-branch")
+
+ int true_block_id() const { return true_block_id_; }
+ int false_block_id() const { return false_block_id_; }
+
+ private:
+ int true_block_id_;
+ int false_block_id_;
+};
+
+
+class LInstanceOfKnownGlobal: public LUnaryOperation<1> {
+ public:
+ LInstanceOfKnownGlobal(LOperand* left, LOperand* temp)
+ : LUnaryOperation<1>(left), temp_(temp) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal,
+ "instance-of-known-global")
+ DECLARE_HYDROGEN_ACCESSOR(InstanceOfKnownGlobal)
+
+ Handle<JSFunction> function() const { return hydrogen()->function(); }
+ LOperand* temp() const { return temp_; }
+
+ private:
+ LOperand* temp_;
+};
+
+
+class LBoundsCheck: public LBinaryOperation<0> {
+ public:
+ LBoundsCheck(LOperand* index, LOperand* length)
+ : LBinaryOperation<0>(index, length) { }
+
+ LOperand* index() const { return left(); }
+ LOperand* length() const { return right(); }
+
+ DECLARE_CONCRETE_INSTRUCTION(BoundsCheck, "bounds-check")
+};
+
+
+class LBitI: public LBinaryOperation<1> {
+ public:
+ LBitI(Token::Value op, LOperand* left, LOperand* right)
+ : LBinaryOperation<1>(left, right), op_(op) { }
+
+ Token::Value op() const { return op_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(BitI, "bit-i")
+
+ private:
+ Token::Value op_;
+};
+
+
+class LShiftI: public LBinaryOperation<1> {
+ public:
+ LShiftI(Token::Value op, LOperand* left, LOperand* right, bool can_deopt)
+ : LBinaryOperation<1>(left, right), op_(op), can_deopt_(can_deopt) { }
+
+ Token::Value op() const { return op_; }
+
+ bool can_deopt() const { return can_deopt_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ShiftI, "shift-i")
+
+ private:
+ Token::Value op_;
+ bool can_deopt_;
+};
+
+
+class LSubI: public LBinaryOperation<1> {
+ public:
+ LSubI(LOperand* left, LOperand* right)
+ : LBinaryOperation<1>(left, right) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(SubI, "sub-i")
+ DECLARE_HYDROGEN_ACCESSOR(Sub)
+};
+
+
+class LConstant: public LTemplateInstruction<1, 0, 0> {
+ DECLARE_INSTRUCTION(Constant)
+};
+
+
+class LConstantI: public LConstant {
+ public:
+ explicit LConstantI(int32_t value) : value_(value) { }
+ int32_t value() const { return value_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ConstantI, "constant-i")
+
+ private:
+ int32_t value_;
+};
+
+
+class LConstantD: public LConstant {
+ public:
+ explicit LConstantD(double value) : value_(value) { }
+ double value() const { return value_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ConstantD, "constant-d")
+
+ private:
+ double value_;
+};
+
+
+class LConstantT: public LConstant {
+ public:
+ explicit LConstantT(Handle<Object> value) : value_(value) { }
+ Handle<Object> value() const { return value_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ConstantT, "constant-t")
+
+ private:
+ Handle<Object> value_;
+};
+
+
+class LBranch: public LUnaryOperation<0> {
+ public:
+ LBranch(LOperand* input, int true_block_id, int false_block_id)
+ : LUnaryOperation<0>(input),
+ true_block_id_(true_block_id),
+ false_block_id_(false_block_id) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(Branch, "branch")
+ DECLARE_HYDROGEN_ACCESSOR(Value)
+
+ virtual void PrintDataTo(StringStream* stream);
+ virtual bool IsControl() const { return true; }
+
+ int true_block_id() const { return true_block_id_; }
+ int false_block_id() const { return false_block_id_; }
+
+ private:
+ int true_block_id_;
+ int false_block_id_;
+};
+
+
+class LCmpMapAndBranch: public LUnaryOperation<0> {
+ public:
+ explicit LCmpMapAndBranch(LOperand* value) : LUnaryOperation<0>(value) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpMapAndBranch, "cmp-map-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareMapAndBranch)
+
+ virtual bool IsControl() const { return true; }
+
+ Handle<Map> map() const { return hydrogen()->map(); }
+ int true_block_id() const {
+ return hydrogen()->true_destination()->block_id();
+ }
+ int false_block_id() const {
+ return hydrogen()->false_destination()->block_id();
}
+};
- LLabel* GetLabel(int block_id) const {
- UNIMPLEMENTED();
- return NULL;
+
+class LJSArrayLength: public LUnaryOperation<1> {
+ public:
+ explicit LJSArrayLength(LOperand* input) : LUnaryOperation<1>(input) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(JSArrayLength, "js-array-length")
+ DECLARE_HYDROGEN_ACCESSOR(JSArrayLength)
+};
+
+
+class LFixedArrayLength: public LUnaryOperation<1> {
+ public:
+ explicit LFixedArrayLength(LOperand* input) : LUnaryOperation<1>(input) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(FixedArrayLength, "fixed-array-length")
+ DECLARE_HYDROGEN_ACCESSOR(FixedArrayLength)
+};
+
+
+class LValueOf: public LUnaryOperation<1> {
+ public:
+ LValueOf(LOperand* input, LOperand* temporary)
+ : LUnaryOperation<1>(input), temporary_(temporary) { }
+
+ LOperand* temporary() const { return temporary_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ValueOf, "value-of")
+ DECLARE_HYDROGEN_ACCESSOR(ValueOf)
+
+ private:
+ LOperand* temporary_;
+};
+
+
+class LThrow: public LUnaryOperation<0> {
+ public:
+ explicit LThrow(LOperand* value) : LUnaryOperation<0>(value) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(Throw, "throw")
+};
+
+
+class LBitNotI: public LUnaryOperation<1> {
+ public:
+ explicit LBitNotI(LOperand* input) : LUnaryOperation<1>(input) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(BitNotI, "bit-not-i")
+};
+
+
+class LAddI: public LBinaryOperation<1> {
+ public:
+ LAddI(LOperand* left, LOperand* right)
+ : LBinaryOperation<1>(left, right) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(AddI, "add-i")
+ DECLARE_HYDROGEN_ACCESSOR(Add)
+};
+
+
+class LPower: public LBinaryOperation<1> {
+ public:
+ LPower(LOperand* left, LOperand* right)
+ : LBinaryOperation<1>(left, right) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(Power, "power")
+ DECLARE_HYDROGEN_ACCESSOR(Power)
+};
+
+
+class LArithmeticD: public LBinaryOperation<1> {
+ public:
+ LArithmeticD(Token::Value op, LOperand* left, LOperand* right)
+ : LBinaryOperation<1>(left, right), op_(op) { }
+
+ Token::Value op() const { return op_; }
+
+ virtual void CompileToNative(LCodeGen* generator);
+ virtual const char* Mnemonic() const;
+
+ private:
+ Token::Value op_;
+};
+
+
+class LArithmeticT: public LBinaryOperation<1> {
+ public:
+ LArithmeticT(Token::Value op, LOperand* left, LOperand* right)
+ : LBinaryOperation<1>(left, right), op_(op) { }
+
+ virtual void CompileToNative(LCodeGen* generator);
+ virtual const char* Mnemonic() const;
+
+ Token::Value op() const { return op_; }
+
+ private:
+ Token::Value op_;
+};
+
+
+class LReturn: public LUnaryOperation<0> {
+ public:
+ explicit LReturn(LOperand* use) : LUnaryOperation<0>(use) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(Return, "return")
+};
+
+
+class LLoadNamedField: public LUnaryOperation<1> {
+ public:
+ explicit LLoadNamedField(LOperand* object) : LUnaryOperation<1>(object) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load-named-field")
+ DECLARE_HYDROGEN_ACCESSOR(LoadNamedField)
+};
+
+
+class LLoadNamedGeneric: public LUnaryOperation<1> {
+ public:
+ explicit LLoadNamedGeneric(LOperand* object) : LUnaryOperation<1>(object) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadNamedGeneric, "load-named-generic")
+ DECLARE_HYDROGEN_ACCESSOR(LoadNamedGeneric)
+
+ LOperand* object() const { return input(); }
+ Handle<Object> name() const { return hydrogen()->name(); }
+};
+
+
+class LLoadFunctionPrototype: public LUnaryOperation<1> {
+ public:
+ LLoadFunctionPrototype(LOperand* function, LOperand* temporary)
+ : LUnaryOperation<1>(function), temporary_(temporary) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadFunctionPrototype, "load-function-prototype")
+ DECLARE_HYDROGEN_ACCESSOR(LoadFunctionPrototype)
+
+ LOperand* function() const { return input(); }
+ LOperand* temporary() const { return temporary_; }
+
+ private:
+ LOperand* temporary_;
+};
+
+
+class LLoadElements: public LUnaryOperation<1> {
+ public:
+ explicit LLoadElements(LOperand* obj) : LUnaryOperation<1>(obj) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadElements, "load-elements")
+};
+
+
+class LLoadKeyedFastElement: public LBinaryOperation<1> {
+ public:
+ LLoadKeyedFastElement(LOperand* elements, LOperand* key)
+ : LBinaryOperation<1>(elements, key) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadKeyedFastElement, "load-keyed-fast-element")
+ DECLARE_HYDROGEN_ACCESSOR(LoadKeyedFastElement)
+
+ LOperand* elements() const { return left(); }
+ LOperand* key() const { return right(); }
+};
+
+
+class LLoadKeyedGeneric: public LBinaryOperation<1> {
+ public:
+ LLoadKeyedGeneric(LOperand* obj, LOperand* key)
+ : LBinaryOperation<1>(obj, key) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadKeyedGeneric, "load-keyed-generic")
+
+ LOperand* object() const { return left(); }
+ LOperand* key() const { return right(); }
+};
+
+
+class LLoadGlobal: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(LoadGlobal, "load-global")
+ DECLARE_HYDROGEN_ACCESSOR(LoadGlobal)
+};
+
+
+class LStoreGlobal: public LUnaryOperation<0> {
+ public:
+ explicit LStoreGlobal(LOperand* value) : LUnaryOperation<0>(value) {}
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobal, "store-global")
+ DECLARE_HYDROGEN_ACCESSOR(StoreGlobal)
+};
+
+
+class LLoadContextSlot: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(LoadContextSlot, "load-context-slot")
+ DECLARE_HYDROGEN_ACCESSOR(LoadContextSlot)
+
+ int context_chain_length() const {
+ return hydrogen()->context_chain_length();
}
+ int slot_index() const { return hydrogen()->slot_index(); }
- const ZoneList<LInstruction*>* instructions() const {
- UNIMPLEMENTED();
- return NULL;
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LPushArgument: public LUnaryOperation<0> {
+ public:
+ explicit LPushArgument(LOperand* argument) : LUnaryOperation<0>(argument) {}
+
+ DECLARE_CONCRETE_INSTRUCTION(PushArgument, "push-argument")
+};
+
+
+class LGlobalObject: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(GlobalObject, "global-object")
+};
+
+
+class LGlobalReceiver: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(GlobalReceiver, "global-receiver")
+};
+
+
+class LCallConstantFunction: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(CallConstantFunction, "call-constant-function")
+ DECLARE_HYDROGEN_ACCESSOR(CallConstantFunction)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<JSFunction> function() { return hydrogen()->function(); }
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
+class LCallKeyed: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(CallKeyed, "call-keyed")
+ DECLARE_HYDROGEN_ACCESSOR(CallKeyed)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
+class LCallNamed: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(CallNamed, "call-named")
+ DECLARE_HYDROGEN_ACCESSOR(CallNamed)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<String> name() const { return hydrogen()->name(); }
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
+class LCallFunction: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(CallFunction, "call-function")
+ DECLARE_HYDROGEN_ACCESSOR(CallFunction)
+
+ int arity() const { return hydrogen()->argument_count() - 2; }
+};
+
+
+class LCallGlobal: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(CallGlobal, "call-global")
+ DECLARE_HYDROGEN_ACCESSOR(CallGlobal)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<String> name() const {return hydrogen()->name(); }
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
+class LCallKnownGlobal: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(CallKnownGlobal, "call-known-global")
+ DECLARE_HYDROGEN_ACCESSOR(CallKnownGlobal)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<JSFunction> target() const { return hydrogen()->target(); }
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
+class LCallNew: public LUnaryOperation<1> {
+ public:
+ explicit LCallNew(LOperand* constructor) : LUnaryOperation<1>(constructor) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallNew, "call-new")
+ DECLARE_HYDROGEN_ACCESSOR(CallNew)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
+class LCallRuntime: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(CallRuntime, "call-runtime")
+ DECLARE_HYDROGEN_ACCESSOR(CallRuntime)
+
+ Runtime::Function* function() const { return hydrogen()->function(); }
+ int arity() const { return hydrogen()->argument_count(); }
+};
+
+
+class LInteger32ToDouble: public LUnaryOperation<1> {
+ public:
+ explicit LInteger32ToDouble(LOperand* use) : LUnaryOperation<1>(use) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(Integer32ToDouble, "int32-to-double")
+};
+
+
+class LNumberTagI: public LUnaryOperation<1> {
+ public:
+ explicit LNumberTagI(LOperand* use) : LUnaryOperation<1>(use) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(NumberTagI, "number-tag-i")
+};
+
+
+class LNumberTagD: public LUnaryOperation<1> {
+ public:
+ explicit LNumberTagD(LOperand* value, LOperand* temp)
+ : LUnaryOperation<1>(value), temp_(temp) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(NumberTagD, "number-tag-d")
+
+ LOperand* temp() const { return temp_; }
+
+ private:
+ LOperand* temp_;
+};
+
+
+// Sometimes truncating conversion from a tagged value to an int32.
+class LDoubleToI: public LUnaryOperation<1> {
+ public:
+ LDoubleToI(LOperand* value, LOperand* temporary)
+ : LUnaryOperation<1>(value), temporary_(temporary) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(DoubleToI, "double-to-i")
+ DECLARE_HYDROGEN_ACCESSOR(Change)
+
+ bool truncating() { return hydrogen()->CanTruncateToInt32(); }
+ LOperand* temporary() const { return temporary_; }
+
+ private:
+ LOperand* temporary_;
+};
+
+
+// Truncating conversion from a tagged value to an int32.
+class LTaggedToI: public LUnaryOperation<1> {
+ public:
+ LTaggedToI(LOperand* value, LOperand* temp)
+ : LUnaryOperation<1>(value), temp_(temp) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(TaggedToI, "tagged-to-i")
+ DECLARE_HYDROGEN_ACCESSOR(Change)
+
+ bool truncating() { return hydrogen()->CanTruncateToInt32(); }
+ LOperand* temp() const { return temp_; }
+
+ private:
+ LOperand* temp_;
+};
+
+
+class LSmiTag: public LUnaryOperation<1> {
+ public:
+ explicit LSmiTag(LOperand* use) : LUnaryOperation<1>(use) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(SmiTag, "smi-tag")
+};
+
+
+class LNumberUntagD: public LUnaryOperation<1> {
+ public:
+ explicit LNumberUntagD(LOperand* value) : LUnaryOperation<1>(value) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(NumberUntagD, "double-untag")
+};
+
+
+class LSmiUntag: public LUnaryOperation<1> {
+ public:
+ LSmiUntag(LOperand* use, bool needs_check)
+ : LUnaryOperation<1>(use), needs_check_(needs_check) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(SmiUntag, "smi-untag")
+
+ bool needs_check() const { return needs_check_; }
+
+ private:
+ bool needs_check_;
+};
+
+
+class LStoreNamed: public LTemplateInstruction<0, 2, 0> {
+ public:
+ LStoreNamed(LOperand* obj, LOperand* val) {
+ this->SetInputAt(0, obj);
+ this->SetInputAt(1, val);
}
- int GetParameterStackSlot(int index) const {
- UNIMPLEMENTED();
- return 0;
+ DECLARE_INSTRUCTION(StoreNamed)
+ DECLARE_HYDROGEN_ACCESSOR(StoreNamed)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ LOperand* object() const { return this->InputAt(0); }
+ LOperand* value() const { return this->InputAt(1); }
+ Handle<Object> name() const { return hydrogen()->name(); }
+};
+
+
+class LStoreNamedField: public LStoreNamed {
+ public:
+ LStoreNamedField(LOperand* obj, LOperand* val, LOperand* temp)
+ : LStoreNamed(obj, val), temp_(temp) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field")
+ DECLARE_HYDROGEN_ACCESSOR(StoreNamedField)
+
+ bool is_in_object() { return hydrogen()->is_in_object(); }
+ int offset() { return hydrogen()->offset(); }
+ bool needs_write_barrier() { return hydrogen()->NeedsWriteBarrier(); }
+ Handle<Map> transition() const { return hydrogen()->transition(); }
+
+ LOperand* temp() { return temp_; }
+
+ private:
+ LOperand* temp_;
+};
+
+
+class LStoreNamedGeneric: public LStoreNamed {
+ public:
+ LStoreNamedGeneric(LOperand* obj, LOperand* val)
+ : LStoreNamed(obj, val) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric, "store-named-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreNamedGeneric)
+};
+
+
+class LStoreKeyed: public LTemplateInstruction<0, 3, 0> {
+ public:
+ LStoreKeyed(LOperand* obj, LOperand* key, LOperand* val) {
+ this->SetInputAt(0, obj);
+ this->SetInputAt(1, key);
+ this->SetInputAt(2, val);
}
- void AddGapMove(int index, LOperand* from, LOperand* to) { UNIMPLEMENTED(); }
+ DECLARE_INSTRUCTION(StoreKeyed)
- LGap* GetGapAt(int index) const {
- UNIMPLEMENTED();
- return NULL;
+ virtual void PrintDataTo(StringStream* stream);
+
+ LOperand* object() const { return this->InputAt(0); }
+ LOperand* key() const { return this->InputAt(1); }
+ LOperand* value() const { return this->InputAt(2); }
+};
+
+
+class LStoreKeyedFastElement: public LStoreKeyed {
+ public:
+ LStoreKeyedFastElement(LOperand* obj, LOperand* key, LOperand* val)
+ : LStoreKeyed(obj, key, val) {}
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFastElement,
+ "store-keyed-fast-element")
+ DECLARE_HYDROGEN_ACCESSOR(StoreKeyedFastElement)
+};
+
+
+class LStoreKeyedGeneric: public LStoreKeyed {
+ public:
+ LStoreKeyedGeneric(LOperand* obj, LOperand* key, LOperand* val)
+ : LStoreKeyed(obj, key, val) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store-keyed-generic")
+};
+
+
+class LCheckFunction: public LUnaryOperation<0> {
+ public:
+ explicit LCheckFunction(LOperand* use) : LUnaryOperation<0>(use) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckFunction, "check-function")
+ DECLARE_HYDROGEN_ACCESSOR(CheckFunction)
+};
+
+
+class LCheckInstanceType: public LUnaryOperation<0> {
+ public:
+ LCheckInstanceType(LOperand* use, LOperand* temp)
+ : LUnaryOperation<0>(use), temp_(temp) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckInstanceType, "check-instance-type")
+ DECLARE_HYDROGEN_ACCESSOR(CheckInstanceType)
+
+ LOperand* temp() const { return temp_; }
+
+ private:
+ LOperand* temp_;
+};
+
+
+class LCheckMap: public LUnaryOperation<0> {
+ public:
+ explicit LCheckMap(LOperand* use) : LUnaryOperation<0>(use) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckMap, "check-map")
+ DECLARE_HYDROGEN_ACCESSOR(CheckMap)
+};
+
+
+class LCheckPrototypeMaps: public LTemplateInstruction<0, 0, 0> {
+ public:
+ explicit LCheckPrototypeMaps(LOperand* temp) : temp_(temp) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckPrototypeMaps, "check-prototype-maps")
+ DECLARE_HYDROGEN_ACCESSOR(CheckPrototypeMaps)
+
+ Handle<JSObject> prototype() const { return hydrogen()->prototype(); }
+ Handle<JSObject> holder() const { return hydrogen()->holder(); }
+
+ LOperand* temp() const { return temp_; }
+
+ private:
+ LOperand* temp_;
+};
+
+
+class LCheckSmi: public LUnaryOperation<0> {
+ public:
+ LCheckSmi(LOperand* use, Condition condition)
+ : LUnaryOperation<0>(use), condition_(condition) { }
+
+ Condition condition() const { return condition_; }
+
+ virtual void CompileToNative(LCodeGen* generator);
+ virtual const char* Mnemonic() const {
+ return (condition_ == zero) ? "check-non-smi" : "check-smi";
}
- bool IsGapAt(int index) const {
- UNIMPLEMENTED();
- return false;
+ private:
+ Condition condition_;
+};
+
+
+class LMaterializedLiteral: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_INSTRUCTION(MaterializedLiteral)
+};
+
+
+class LArrayLiteral: public LMaterializedLiteral {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(ArrayLiteral, "array-literal")
+ DECLARE_HYDROGEN_ACCESSOR(ArrayLiteral)
+};
+
+
+class LObjectLiteral: public LMaterializedLiteral {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(ObjectLiteral, "object-literal")
+ DECLARE_HYDROGEN_ACCESSOR(ObjectLiteral)
+};
+
+
+class LRegExpLiteral: public LMaterializedLiteral {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(RegExpLiteral, "regexp-literal")
+ DECLARE_HYDROGEN_ACCESSOR(RegExpLiteral)
+};
+
+
+class LFunctionLiteral: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(FunctionLiteral, "function-literal")
+ DECLARE_HYDROGEN_ACCESSOR(FunctionLiteral)
+
+ Handle<SharedFunctionInfo> shared_info() { return hydrogen()->shared_info(); }
+};
+
+
+class LTypeof: public LUnaryOperation<1> {
+ public:
+ explicit LTypeof(LOperand* input) : LUnaryOperation<1>(input) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(Typeof, "typeof")
+};
+
+
+class LTypeofIs: public LUnaryOperation<1> {
+ public:
+ explicit LTypeofIs(LOperand* input) : LUnaryOperation<1>(input) { }
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(TypeofIs, "typeof-is")
+ DECLARE_HYDROGEN_ACCESSOR(TypeofIs)
+
+ Handle<String> type_literal() { return hydrogen()->type_literal(); }
+};
+
+
+class LTypeofIsAndBranch: public LTypeofIs {
+ public:
+ LTypeofIsAndBranch(LOperand* value,
+ int true_block_id,
+ int false_block_id)
+ : LTypeofIs(value),
+ true_block_id_(true_block_id),
+ false_block_id_(false_block_id) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(TypeofIsAndBranch, "typeof-is-and-branch")
+
+ virtual void PrintDataTo(StringStream* stream);
+ virtual bool IsControl() const { return true; }
+
+ int true_block_id() const { return true_block_id_; }
+ int false_block_id() const { return false_block_id_; }
+
+ private:
+ int true_block_id_;
+ int false_block_id_;
+};
+
+
+class LDeleteProperty: public LBinaryOperation<1> {
+ public:
+ LDeleteProperty(LOperand* obj, LOperand* key)
+ : LBinaryOperation<1>(obj, key) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(DeleteProperty, "delete-property")
+
+ LOperand* object() const { return left(); }
+ LOperand* key() const { return right(); }
+};
+
+
+class LOsrEntry: public LTemplateInstruction<0, 0, 0> {
+ public:
+ LOsrEntry();
+
+ DECLARE_CONCRETE_INSTRUCTION(OsrEntry, "osr-entry")
+
+ LOperand** SpilledRegisterArray() { return register_spills_; }
+ LOperand** SpilledDoubleRegisterArray() { return double_register_spills_; }
+
+ void MarkSpilledRegister(int allocation_index, LOperand* spill_operand);
+ void MarkSpilledDoubleRegister(int allocation_index,
+ LOperand* spill_operand);
+
+ private:
+ // Arrays of spill slot operands for registers with an assigned spill
+ // slot, i.e., that must also be restored to the spill slot on OSR entry.
+ // NULL if the register has no assigned spill slot. Indexed by allocation
+ // index.
+ LOperand* register_spills_[Register::kNumAllocatableRegisters];
+ LOperand* double_register_spills_[DoubleRegister::kNumAllocatableRegisters];
+};
+
+
+class LStackCheck: public LTemplateInstruction<0, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(StackCheck, "stack-check")
+};
+
+
+class LChunkBuilder;
+class LChunk: public ZoneObject {
+ public:
+ explicit LChunk(HGraph* graph)
+ : spill_slot_count_(0),
+ graph_(graph),
+ instructions_(32),
+ pointer_maps_(8),
+ inlined_closures_(1) { }
+
+ int AddInstruction(LInstruction* instruction, HBasicBlock* block);
+ LConstantOperand* DefineConstantOperand(HConstant* constant);
+ Handle<Object> LookupLiteral(LConstantOperand* operand) const;
+ Representation LookupLiteralRepresentation(LConstantOperand* operand) const;
+
+ int GetNextSpillIndex(bool is_double);
+ LOperand* GetNextSpillSlot(bool is_double);
+
+ int ParameterAt(int index);
+ int GetParameterStackSlot(int index) const;
+ int spill_slot_count() const { return spill_slot_count_; }
+ HGraph* graph() const { return graph_; }
+ const ZoneList<LInstruction*>* instructions() const { return &instructions_; }
+ void AddGapMove(int index, LOperand* from, LOperand* to);
+ LGap* GetGapAt(int index) const;
+ bool IsGapAt(int index) const;
+ int NearestGapPos(int index) const;
+ void MarkEmptyBlocks();
+ const ZoneList<LPointerMap*>* pointer_maps() const { return &pointer_maps_; }
+ LLabel* GetLabel(int block_id) const {
+ HBasicBlock* block = graph_->blocks()->at(block_id);
+ int first_instruction = block->first_instruction_index();
+ return LLabel::cast(instructions_[first_instruction]);
+ }
+ int LookupDestination(int block_id) const {
+ LLabel* cur = GetLabel(block_id);
+ while (cur->replacement() != NULL) {
+ cur = cur->replacement();
+ }
+ return cur->block_id();
+ }
+ Label* GetAssemblyLabel(int block_id) const {
+ LLabel* label = GetLabel(block_id);
+ ASSERT(!label->HasReplacement());
+ return label->label();
}
- int NearestGapPos(int index) const {
- UNIMPLEMENTED();
- return 0;
+ const ZoneList<Handle<JSFunction> >* inlined_closures() const {
+ return &inlined_closures_;
}
- void MarkEmptyBlocks() { UNIMPLEMENTED(); }
+ void AddInlinedClosure(Handle<JSFunction> closure) {
+ inlined_closures_.Add(closure);
+ }
-#ifdef DEBUG
- void Verify() { UNIMPLEMENTED(); }
-#endif
+ private:
+ int spill_slot_count_;
+ HGraph* const graph_;
+ ZoneList<LInstruction*> instructions_;
+ ZoneList<LPointerMap*> pointer_maps_;
+ ZoneList<Handle<JSFunction> > inlined_closures_;
};
class LChunkBuilder BASE_EMBEDDED {
public:
- LChunkBuilder(HGraph* graph, LAllocator* allocator) { }
+ LChunkBuilder(HGraph* graph, LAllocator* allocator)
+ : chunk_(NULL),
+ graph_(graph),
+ status_(UNUSED),
+ current_instruction_(NULL),
+ current_block_(NULL),
+ next_block_(NULL),
+ argument_count_(0),
+ allocator_(allocator),
+ position_(RelocInfo::kNoPosition),
+ instructions_pending_deoptimization_environment_(NULL),
+ pending_deoptimization_ast_id_(AstNode::kNoNumber) { }
// Build the sequence for the graph.
- LChunk* Build() {
- UNIMPLEMENTED();
- return NULL;
- };
+ LChunk* Build();
// Declare methods that deal with the individual node types.
-#define DECLARE_DO(type) LInstruction* Do##type(H##type* node) { \
- UNIMPLEMENTED(); \
- return NULL; \
- }
+#define DECLARE_DO(type) LInstruction* Do##type(H##type* node);
HYDROGEN_CONCRETE_INSTRUCTION_LIST(DECLARE_DO)
#undef DECLARE_DO
+ private:
+ enum Status {
+ UNUSED,
+ BUILDING,
+ DONE,
+ ABORTED
+ };
+
+ LChunk* chunk() const { return chunk_; }
+ HGraph* graph() const { return graph_; }
+
+ bool is_unused() const { return status_ == UNUSED; }
+ bool is_building() const { return status_ == BUILDING; }
+ bool is_done() const { return status_ == DONE; }
+ bool is_aborted() const { return status_ == ABORTED; }
+
+ void Abort(const char* format, ...);
+
+ // Methods for getting operands for Use / Define / Temp.
+ LRegister* ToOperand(Register reg);
+ LUnallocated* ToUnallocated(Register reg);
+ LUnallocated* ToUnallocated(XMMRegister reg);
+
+ // Methods for setting up define-use relationships.
+ LOperand* Use(HValue* value, LUnallocated* operand);
+ LOperand* UseFixed(HValue* value, Register fixed_register);
+ LOperand* UseFixedDouble(HValue* value, XMMRegister fixed_register);
+
+ // A value that is guaranteed to be allocated to a register.
+ // Operand created by UseRegister is guaranteed to be live until the end of
+ // instruction. This means that register allocator will not reuse it's
+ // register for any other operand inside instruction.
+ // Operand created by UseRegisterAtStart is guaranteed to be live only at
+ // instruction start. Register allocator is free to assign the same register
+ // to some other operand used inside instruction (i.e. temporary or
+ // output).
+ LOperand* UseRegister(HValue* value);
+ LOperand* UseRegisterAtStart(HValue* value);
+
+ // A value in a register that may be trashed.
+ LOperand* UseTempRegister(HValue* value);
+ LOperand* Use(HValue* value);
+ LOperand* UseAtStart(HValue* value);
+ LOperand* UseOrConstant(HValue* value);
+ LOperand* UseOrConstantAtStart(HValue* value);
+ LOperand* UseRegisterOrConstant(HValue* value);
+ LOperand* UseRegisterOrConstantAtStart(HValue* value);
+
+ // Methods for setting up define-use relationships.
+ // Return the same instruction that they are passed.
+ template<int I, int T>
+ LInstruction* Define(LTemplateInstruction<1, I, T>* instr,
+ LUnallocated* result);
+ template<int I, int T>
+ LInstruction* Define(LTemplateInstruction<1, I, T>* instr);
+ template<int I, int T>
+ LInstruction* DefineAsRegister(LTemplateInstruction<1, I, T>* instr);
+ template<int I, int T>
+ LInstruction* DefineAsSpilled(LTemplateInstruction<1, I, T>* instr,
+ int index);
+ template<int I, int T>
+ LInstruction* DefineSameAsFirst(LTemplateInstruction<1, I, T>* instr);
+ template<int I, int T>
+ LInstruction* DefineFixed(LTemplateInstruction<1, I, T>* instr,
+ Register reg);
+ template<int I, int T>
+ LInstruction* DefineFixedDouble(LTemplateInstruction<1, I, T>* instr,
+ XMMRegister reg);
+ LInstruction* AssignEnvironment(LInstruction* instr);
+ LInstruction* AssignPointerMap(LInstruction* instr);
+
+ enum CanDeoptimize { CAN_DEOPTIMIZE_EAGERLY, CANNOT_DEOPTIMIZE_EAGERLY };
+
+ // By default we assume that instruction sequences generated for calls
+ // cannot deoptimize eagerly and we do not attach environment to this
+ // instruction.
+ LInstruction* MarkAsCall(
+ LInstruction* instr,
+ HInstruction* hinstr,
+ CanDeoptimize can_deoptimize = CANNOT_DEOPTIMIZE_EAGERLY);
+ LInstruction* MarkAsSaveDoubles(LInstruction* instr);
+
+ LInstruction* SetInstructionPendingDeoptimizationEnvironment(
+ LInstruction* instr, int ast_id);
+ void ClearInstructionPendingDeoptimizationEnvironment();
+
+ LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env);
+
+ // Temporary operand that must be in a register.
+ LUnallocated* TempRegister();
+ LOperand* FixedTemp(Register reg);
+ LOperand* FixedTemp(XMMRegister reg);
+
+ void VisitInstruction(HInstruction* current);
+
+ void DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block);
+ LInstruction* DoBit(Token::Value op, HBitwiseBinaryOperation* instr);
+ LInstruction* DoShift(Token::Value op, HBitwiseBinaryOperation* instr);
+ LInstruction* DoArithmeticD(Token::Value op,
+ HArithmeticBinaryOperation* instr);
+ LInstruction* DoArithmeticT(Token::Value op,
+ HArithmeticBinaryOperation* instr);
+
+ LChunk* chunk_;
+ HGraph* const graph_;
+ Status status_;
+ HInstruction* current_instruction_;
+ HBasicBlock* current_block_;
+ HBasicBlock* next_block_;
+ int argument_count_;
+ LAllocator* allocator_;
+ int position_;
+ LInstruction* instructions_pending_deoptimization_environment_;
+ int pending_deoptimization_ast_id_;
+
DISALLOW_COPY_AND_ASSIGN(LChunkBuilder);
};
+#undef DECLARE_HYDROGEN_ACCESSOR
+#undef DECLARE_INSTRUCTION
+#undef DECLARE_CONCRETE_INSTRUCTION
} } // namespace v8::internal
diff --git a/deps/v8/src/x64/macro-assembler-x64.cc b/deps/v8/src/x64/macro-assembler-x64.cc
index 1df9b4750c..f95755db2a 100644
--- a/deps/v8/src/x64/macro-assembler-x64.cc
+++ b/deps/v8/src/x64/macro-assembler-x64.cc
@@ -288,7 +288,7 @@ void MacroAssembler::Abort(const char* msg) {
}
#endif
// Disable stub call restrictions to always allow calls to abort.
- set_allow_stub_calls(true);
+ AllowStubCallsScope allow_scope(this, true);
push(rax);
movq(kScratchRegister, p0, RelocInfo::NONE);
@@ -1110,7 +1110,7 @@ void MacroAssembler::SmiAnd(Register dst, Register src1, Register src2) {
void MacroAssembler::SmiAndConstant(Register dst, Register src, Smi* constant) {
if (constant->value() == 0) {
- xor_(dst, dst);
+ Set(dst, 0);
} else if (dst.is(src)) {
ASSERT(!dst.is(kScratchRegister));
Register constant_reg = GetSmiConstant(constant);
@@ -1274,8 +1274,6 @@ void MacroAssembler::Move(Register dst, Register src) {
}
-
-
void MacroAssembler::Move(Register dst, Handle<Object> source) {
ASSERT(!source->IsFailure());
if (source->IsSmi()) {
@@ -1605,7 +1603,7 @@ void MacroAssembler::DecrementCounter(StatsCounter* counter, int value) {
#ifdef ENABLE_DEBUGGER_SUPPORT
void MacroAssembler::DebugBreak() {
ASSERT(allow_stub_calls());
- xor_(rax, rax); // no arguments
+ Set(rax, 0); // No arguments.
movq(rbx, ExternalReference(Runtime::kDebugBreak));
CEntryStub ces(1);
Call(ces.GetCode(), RelocInfo::DEBUG_BREAK);
diff --git a/deps/v8/src/x64/macro-assembler-x64.h b/deps/v8/src/x64/macro-assembler-x64.h
index d8f2fba42a..30b9ba5152 100644
--- a/deps/v8/src/x64/macro-assembler-x64.h
+++ b/deps/v8/src/x64/macro-assembler-x64.h
@@ -1176,7 +1176,7 @@ void MacroAssembler::SmiMul(Register dst,
jmp(on_not_smi_result);
bind(&zero_correct_result);
- xor_(dst, dst);
+ Set(dst, 0);
bind(&correct_result);
} else {
diff --git a/deps/v8/src/x64/regexp-macro-assembler-x64.cc b/deps/v8/src/x64/regexp-macro-assembler-x64.cc
index 2cf85f1165..27f3482a9a 100644
--- a/deps/v8/src/x64/regexp-macro-assembler-x64.cc
+++ b/deps/v8/src/x64/regexp-macro-assembler-x64.cc
@@ -223,9 +223,7 @@ void RegExpMacroAssemblerX64::CheckCharacters(Vector<const uc16> str,
// If input is ASCII, don't even bother calling here if the string to
// match contains a non-ascii character.
if (mode_ == ASCII) {
- for (int i = 0; i < str.length(); i++) {
- ASSERT(str[i] <= String::kMaxAsciiCharCodeU);
- }
+ ASSERT(String::IsAscii(str.start(), str.length()));
}
#endif
int byte_length = str.length() * char_size();
@@ -690,7 +688,7 @@ bool RegExpMacroAssemblerX64::CheckSpecialCharacterClass(uc16 type,
void RegExpMacroAssemblerX64::Fail() {
ASSERT(FAILURE == 0); // Return value for failure is zero.
- __ xor_(rax, rax); // zero rax.
+ __ Set(rax, 0);
__ jmp(&exit_label_);
}
diff --git a/deps/v8/src/x64/stub-cache-x64.cc b/deps/v8/src/x64/stub-cache-x64.cc
index 63e9769591..57cba14212 100644
--- a/deps/v8/src/x64/stub-cache-x64.cc
+++ b/deps/v8/src/x64/stub-cache-x64.cc
@@ -25,23 +25,17 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
#include "v8.h"
#if defined(V8_TARGET_ARCH_X64)
#include "ic-inl.h"
-#include "code-stubs.h"
#include "codegen-inl.h"
#include "stub-cache.h"
-#include "macro-assembler.h"
namespace v8 {
namespace internal {
-//-----------------------------------------------------------------------------
-// StubCompiler static helper functions
-
#define __ ACCESS_MASM(masm)
@@ -182,92 +176,6 @@ static void GenerateDictionaryNegativeLookup(MacroAssembler* masm,
}
-void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) {
- ASSERT(kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC);
- Code* code = NULL;
- if (kind == Code::LOAD_IC) {
- code = Builtins::builtin(Builtins::LoadIC_Miss);
- } else {
- code = Builtins::builtin(Builtins::KeyedLoadIC_Miss);
- }
-
- Handle<Code> ic(code);
- __ Jump(ic, RelocInfo::CODE_TARGET);
-}
-
-
-void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm,
- int index,
- Register prototype) {
- // Load the global or builtins object from the current context.
- __ movq(prototype,
- Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
- // Load the global context from the global or builtins object.
- __ movq(prototype,
- FieldOperand(prototype, GlobalObject::kGlobalContextOffset));
- // Load the function from the global context.
- __ movq(prototype, Operand(prototype, Context::SlotOffset(index)));
- // Load the initial map. The global functions all have initial maps.
- __ movq(prototype,
- FieldOperand(prototype, JSFunction::kPrototypeOrInitialMapOffset));
- // Load the prototype from the initial map.
- __ movq(prototype, FieldOperand(prototype, Map::kPrototypeOffset));
-}
-
-
-void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype(
- MacroAssembler* masm, int index, Register prototype, Label* miss) {
- // Check we're still in the same context.
- __ Move(prototype, Top::global());
- __ cmpq(Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)),
- prototype);
- __ j(not_equal, miss);
- // Get the global function with the given index.
- JSFunction* function = JSFunction::cast(Top::global_context()->get(index));
- // Load its initial map. The global functions all have initial maps.
- __ Move(prototype, Handle<Map>(function->initial_map()));
- // Load the prototype from the initial map.
- __ movq(prototype, FieldOperand(prototype, Map::kPrototypeOffset));
-}
-
-
-// Load a fast property out of a holder object (src). In-object properties
-// are loaded directly otherwise the property is loaded from the properties
-// fixed array.
-void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm,
- Register dst, Register src,
- JSObject* holder, int index) {
- // Adjust for the number of properties stored in the holder.
- index -= holder->map()->inobject_properties();
- if (index < 0) {
- // Get the property straight out of the holder.
- int offset = holder->map()->instance_size() + (index * kPointerSize);
- __ movq(dst, FieldOperand(src, offset));
- } else {
- // Calculate the offset into the properties array.
- int offset = index * kPointerSize + FixedArray::kHeaderSize;
- __ movq(dst, FieldOperand(src, JSObject::kPropertiesOffset));
- __ movq(dst, FieldOperand(dst, offset));
- }
-}
-
-
-static void PushInterceptorArguments(MacroAssembler* masm,
- Register receiver,
- Register holder,
- Register name,
- JSObject* holder_obj) {
- __ push(name);
- InterceptorInfo* interceptor = holder_obj->GetNamedInterceptor();
- ASSERT(!Heap::InNewSpace(interceptor));
- __ Move(kScratchRegister, Handle<Object>(interceptor));
- __ push(kScratchRegister);
- __ push(receiver);
- __ push(holder);
- __ push(FieldOperand(kScratchRegister, InterceptorInfo::kDataOffset));
-}
-
-
void StubCache::GenerateProbe(MacroAssembler* masm,
Code::Flags flags,
Register receiver,
@@ -324,83 +232,38 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
}
-// Both name_reg and receiver_reg are preserved on jumps to miss_label,
-// but may be destroyed if store is successful.
-void StubCompiler::GenerateStoreField(MacroAssembler* masm,
- JSObject* object,
- int index,
- Map* transition,
- Register receiver_reg,
- Register name_reg,
- Register scratch,
- Label* miss_label) {
- // Check that the object isn't a smi.
- __ JumpIfSmi(receiver_reg, miss_label);
-
- // Check that the map of the object hasn't changed.
- __ Cmp(FieldOperand(receiver_reg, HeapObject::kMapOffset),
- Handle<Map>(object->map()));
- __ j(not_equal, miss_label);
-
- // Perform global security token check if needed.
- if (object->IsJSGlobalProxy()) {
- __ CheckAccessGlobalProxy(receiver_reg, scratch, miss_label);
- }
-
- // Stub never generated for non-global objects that require access
- // checks.
- ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
-
- // Perform map transition for the receiver if necessary.
- if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) {
- // The properties must be extended before we can store the value.
- // We jump to a runtime call that extends the properties array.
- __ pop(scratch); // Return address.
- __ push(receiver_reg);
- __ Push(Handle<Map>(transition));
- __ push(rax);
- __ push(scratch);
- __ TailCallExternalReference(
- ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage)), 3, 1);
- return;
- }
-
- if (transition != NULL) {
- // Update the map of the object; no write barrier updating is
- // needed because the map is never in new space.
- __ Move(FieldOperand(receiver_reg, HeapObject::kMapOffset),
- Handle<Map>(transition));
- }
-
- // Adjust for the number of properties stored in the object. Even in the
- // face of a transition we can use the old map here because the size of the
- // object and the number of in-object properties is not going to change.
- index -= object->map()->inobject_properties();
-
- if (index < 0) {
- // Set the property straight into the object.
- int offset = object->map()->instance_size() + (index * kPointerSize);
- __ movq(FieldOperand(receiver_reg, offset), rax);
-
- // Update the write barrier for the array address.
- // Pass the value being stored in the now unused name_reg.
- __ movq(name_reg, rax);
- __ RecordWrite(receiver_reg, offset, name_reg, scratch);
- } else {
- // Write to the properties array.
- int offset = index * kPointerSize + FixedArray::kHeaderSize;
- // Get the properties array (optimistically).
- __ movq(scratch, FieldOperand(receiver_reg, JSObject::kPropertiesOffset));
- __ movq(FieldOperand(scratch, offset), rax);
+void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm,
+ int index,
+ Register prototype) {
+ // Load the global or builtins object from the current context.
+ __ movq(prototype,
+ Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ // Load the global context from the global or builtins object.
+ __ movq(prototype,
+ FieldOperand(prototype, GlobalObject::kGlobalContextOffset));
+ // Load the function from the global context.
+ __ movq(prototype, Operand(prototype, Context::SlotOffset(index)));
+ // Load the initial map. The global functions all have initial maps.
+ __ movq(prototype,
+ FieldOperand(prototype, JSFunction::kPrototypeOrInitialMapOffset));
+ // Load the prototype from the initial map.
+ __ movq(prototype, FieldOperand(prototype, Map::kPrototypeOffset));
+}
- // Update the write barrier for the array address.
- // Pass the value being stored in the now unused name_reg.
- __ movq(name_reg, rax);
- __ RecordWrite(scratch, offset, name_reg, receiver_reg);
- }
- // Return the value (register rax).
- __ ret(0);
+void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype(
+ MacroAssembler* masm, int index, Register prototype, Label* miss) {
+ // Check we're still in the same context.
+ __ Move(prototype, Top::global());
+ __ cmpq(Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)),
+ prototype);
+ __ j(not_equal, miss);
+ // Get the global function with the given index.
+ JSFunction* function = JSFunction::cast(Top::global_context()->get(index));
+ // Load its initial map. The global functions all have initial maps.
+ __ Move(prototype, Handle<Map>(function->initial_map()));
+ // Load the prototype from the initial map.
+ __ movq(prototype, FieldOperand(prototype, Map::kPrototypeOffset));
}
@@ -469,6 +332,54 @@ void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm,
}
+void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm,
+ Register receiver,
+ Register result,
+ Register scratch,
+ Label* miss_label) {
+ __ TryGetFunctionPrototype(receiver, result, miss_label);
+ if (!result.is(rax)) __ movq(rax, result);
+ __ ret(0);
+}
+
+
+// Load a fast property out of a holder object (src). In-object properties
+// are loaded directly otherwise the property is loaded from the properties
+// fixed array.
+void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm,
+ Register dst, Register src,
+ JSObject* holder, int index) {
+ // Adjust for the number of properties stored in the holder.
+ index -= holder->map()->inobject_properties();
+ if (index < 0) {
+ // Get the property straight out of the holder.
+ int offset = holder->map()->instance_size() + (index * kPointerSize);
+ __ movq(dst, FieldOperand(src, offset));
+ } else {
+ // Calculate the offset into the properties array.
+ int offset = index * kPointerSize + FixedArray::kHeaderSize;
+ __ movq(dst, FieldOperand(src, JSObject::kPropertiesOffset));
+ __ movq(dst, FieldOperand(dst, offset));
+ }
+}
+
+
+static void PushInterceptorArguments(MacroAssembler* masm,
+ Register receiver,
+ Register holder,
+ Register name,
+ JSObject* holder_obj) {
+ __ push(name);
+ InterceptorInfo* interceptor = holder_obj->GetNamedInterceptor();
+ ASSERT(!Heap::InNewSpace(interceptor));
+ __ Move(kScratchRegister, Handle<Object>(interceptor));
+ __ push(kScratchRegister);
+ __ push(receiver);
+ __ push(holder);
+ __ push(FieldOperand(kScratchRegister, InterceptorInfo::kDataOffset));
+}
+
+
static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm,
Register receiver,
Register holder,
@@ -486,20 +397,10 @@ static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm,
}
-
-void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm,
- Register receiver,
- Register result,
- Register scratch,
- Label* miss_label) {
- __ TryGetFunctionPrototype(receiver, result, miss_label);
- if (!result.is(rax)) __ movq(rax, result);
- __ ret(0);
-}
-
// Number of pointers to be reserved on stack for fast API call.
static const int kFastApiCallArguments = 3;
+
// Reserves space for the extra arguments to API function in the
// caller's frame.
//
@@ -553,7 +454,6 @@ static bool GenerateFastApiCall(MacroAssembler* masm,
// -- rsp[(argc + 3) * 8] : first argument
// -- rsp[(argc + 4) * 8] : receiver
// -----------------------------------
-
// Get the function and setup the context.
JSFunction* function = optimization.constant_function();
__ Move(rdi, Handle<JSFunction>(function));
@@ -833,6 +733,100 @@ class CallInterceptorCompiler BASE_EMBEDDED {
};
+void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) {
+ ASSERT(kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC);
+ Code* code = NULL;
+ if (kind == Code::LOAD_IC) {
+ code = Builtins::builtin(Builtins::LoadIC_Miss);
+ } else {
+ code = Builtins::builtin(Builtins::KeyedLoadIC_Miss);
+ }
+
+ Handle<Code> ic(code);
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+}
+
+
+// Both name_reg and receiver_reg are preserved on jumps to miss_label,
+// but may be destroyed if store is successful.
+void StubCompiler::GenerateStoreField(MacroAssembler* masm,
+ JSObject* object,
+ int index,
+ Map* transition,
+ Register receiver_reg,
+ Register name_reg,
+ Register scratch,
+ Label* miss_label) {
+ // Check that the object isn't a smi.
+ __ JumpIfSmi(receiver_reg, miss_label);
+
+ // Check that the map of the object hasn't changed.
+ __ Cmp(FieldOperand(receiver_reg, HeapObject::kMapOffset),
+ Handle<Map>(object->map()));
+ __ j(not_equal, miss_label);
+
+ // Perform global security token check if needed.
+ if (object->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(receiver_reg, scratch, miss_label);
+ }
+
+ // Stub never generated for non-global objects that require access
+ // checks.
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
+
+ // Perform map transition for the receiver if necessary.
+ if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) {
+ // The properties must be extended before we can store the value.
+ // We jump to a runtime call that extends the properties array.
+ __ pop(scratch); // Return address.
+ __ push(receiver_reg);
+ __ Push(Handle<Map>(transition));
+ __ push(rax);
+ __ push(scratch);
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage)), 3, 1);
+ return;
+ }
+
+ if (transition != NULL) {
+ // Update the map of the object; no write barrier updating is
+ // needed because the map is never in new space.
+ __ Move(FieldOperand(receiver_reg, HeapObject::kMapOffset),
+ Handle<Map>(transition));
+ }
+
+ // Adjust for the number of properties stored in the object. Even in the
+ // face of a transition we can use the old map here because the size of the
+ // object and the number of in-object properties is not going to change.
+ index -= object->map()->inobject_properties();
+
+ if (index < 0) {
+ // Set the property straight into the object.
+ int offset = object->map()->instance_size() + (index * kPointerSize);
+ __ movq(FieldOperand(receiver_reg, offset), rax);
+
+ // Update the write barrier for the array address.
+ // Pass the value being stored in the now unused name_reg.
+ __ movq(name_reg, rax);
+ __ RecordWrite(receiver_reg, offset, name_reg, scratch);
+ } else {
+ // Write to the properties array.
+ int offset = index * kPointerSize + FixedArray::kHeaderSize;
+ // Get the properties array (optimistically).
+ __ movq(scratch, FieldOperand(receiver_reg, JSObject::kPropertiesOffset));
+ __ movq(FieldOperand(scratch, offset), rax);
+
+ // Update the write barrier for the array address.
+ // Pass the value being stored in the now unused name_reg.
+ __ movq(name_reg, rax);
+ __ RecordWrite(scratch, offset, name_reg, receiver_reg);
+ }
+
+ // Return the value (register rax).
+ __ ret(0);
+}
+
+
// Generate code to check that a global property cell is empty. Create
// the property cell at compilation time if no cell exists for the
// property.
@@ -857,10 +851,420 @@ MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCell(
#undef __
-
#define __ ACCESS_MASM((masm()))
+Register StubCompiler::CheckPrototypes(JSObject* object,
+ Register object_reg,
+ JSObject* holder,
+ Register holder_reg,
+ Register scratch1,
+ Register scratch2,
+ String* name,
+ int save_at_depth,
+ Label* miss) {
+ // Make sure there's no overlap between holder and object registers.
+ ASSERT(!scratch1.is(object_reg) && !scratch1.is(holder_reg));
+ ASSERT(!scratch2.is(object_reg) && !scratch2.is(holder_reg)
+ && !scratch2.is(scratch1));
+
+ // Keep track of the current object in register reg. On the first
+ // iteration, reg is an alias for object_reg, on later iterations,
+ // it is an alias for holder_reg.
+ Register reg = object_reg;
+ int depth = 0;
+
+ if (save_at_depth == depth) {
+ __ movq(Operand(rsp, kPointerSize), object_reg);
+ }
+
+ // Check the maps in the prototype chain.
+ // Traverse the prototype chain from the object and do map checks.
+ JSObject* current = object;
+ while (current != holder) {
+ depth++;
+
+ // Only global objects and objects that do not require access
+ // checks are allowed in stubs.
+ ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded());
+
+ JSObject* prototype = JSObject::cast(current->GetPrototype());
+ if (!current->HasFastProperties() &&
+ !current->IsJSGlobalObject() &&
+ !current->IsJSGlobalProxy()) {
+ if (!name->IsSymbol()) {
+ MaybeObject* lookup_result = Heap::LookupSymbol(name);
+ if (lookup_result->IsFailure()) {
+ set_failure(Failure::cast(lookup_result));
+ return reg;
+ } else {
+ name = String::cast(lookup_result->ToObjectUnchecked());
+ }
+ }
+ ASSERT(current->property_dictionary()->FindEntry(name) ==
+ StringDictionary::kNotFound);
+
+ GenerateDictionaryNegativeLookup(masm(),
+ miss,
+ reg,
+ name,
+ scratch1,
+ scratch2);
+ __ movq(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
+ reg = holder_reg; // from now the object is in holder_reg
+ __ movq(reg, FieldOperand(scratch1, Map::kPrototypeOffset));
+ } else if (Heap::InNewSpace(prototype)) {
+ // Get the map of the current object.
+ __ movq(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
+ __ Cmp(scratch1, Handle<Map>(current->map()));
+ // Branch on the result of the map check.
+ __ j(not_equal, miss);
+ // Check access rights to the global object. This has to happen
+ // after the map check so that we know that the object is
+ // actually a global object.
+ if (current->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(reg, scratch1, miss);
+
+ // Restore scratch register to be the map of the object.
+ // We load the prototype from the map in the scratch register.
+ __ movq(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
+ }
+ // The prototype is in new space; we cannot store a reference
+ // to it in the code. Load it from the map.
+ reg = holder_reg; // from now the object is in holder_reg
+ __ movq(reg, FieldOperand(scratch1, Map::kPrototypeOffset));
+
+ } else {
+ // Check the map of the current object.
+ __ Cmp(FieldOperand(reg, HeapObject::kMapOffset),
+ Handle<Map>(current->map()));
+ // Branch on the result of the map check.
+ __ j(not_equal, miss);
+ // Check access rights to the global object. This has to happen
+ // after the map check so that we know that the object is
+ // actually a global object.
+ if (current->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(reg, scratch1, miss);
+ }
+ // The prototype is in old space; load it directly.
+ reg = holder_reg; // from now the object is in holder_reg
+ __ Move(reg, Handle<JSObject>(prototype));
+ }
+
+ if (save_at_depth == depth) {
+ __ movq(Operand(rsp, kPointerSize), reg);
+ }
+
+ // Go to the next object in the prototype chain.
+ current = prototype;
+ }
+
+ // Check the holder map.
+ __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), Handle<Map>(holder->map()));
+ __ j(not_equal, miss);
+
+ // Log the check depth.
+ LOG(IntEvent("check-maps-depth", depth + 1));
+
+ // Perform security check for access to the global object and return
+ // the holder register.
+ ASSERT(current == holder);
+ ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded());
+ if (current->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(reg, scratch1, miss);
+ }
+
+ // If we've skipped any global objects, it's not enough to verify
+ // that their maps haven't changed. We also need to check that the
+ // property cell for the property is still empty.
+ current = object;
+ while (current != holder) {
+ if (current->IsGlobalObject()) {
+ MaybeObject* cell = GenerateCheckPropertyCell(masm(),
+ GlobalObject::cast(current),
+ name,
+ scratch1,
+ miss);
+ if (cell->IsFailure()) {
+ set_failure(Failure::cast(cell));
+ return reg;
+ }
+ }
+ current = JSObject::cast(current->GetPrototype());
+ }
+
+ // Return the register containing the holder.
+ return reg;
+}
+
+
+void StubCompiler::GenerateLoadField(JSObject* object,
+ JSObject* holder,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ int index,
+ String* name,
+ Label* miss) {
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, miss);
+
+ // Check the prototype chain.
+ Register reg =
+ CheckPrototypes(object, receiver, holder,
+ scratch1, scratch2, scratch3, name, miss);
+
+ // Get the value from the properties.
+ GenerateFastPropertyLoad(masm(), rax, reg, holder, index);
+ __ ret(0);
+}
+
+
+bool StubCompiler::GenerateLoadCallback(JSObject* object,
+ JSObject* holder,
+ Register receiver,
+ Register name_reg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ AccessorInfo* callback,
+ String* name,
+ Label* miss,
+ Failure** failure) {
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, miss);
+
+ // Check that the maps haven't changed.
+ Register reg =
+ CheckPrototypes(object, receiver, holder, scratch1,
+ scratch2, scratch3, name, miss);
+
+ Handle<AccessorInfo> callback_handle(callback);
+
+ // Insert additional parameters into the stack frame above return address.
+ ASSERT(!scratch2.is(reg));
+ __ pop(scratch2); // Get return address to place it below.
+
+ __ push(receiver); // receiver
+ __ push(reg); // holder
+ if (Heap::InNewSpace(callback_handle->data())) {
+ __ Move(scratch1, callback_handle);
+ __ push(FieldOperand(scratch1, AccessorInfo::kDataOffset)); // data
+ } else {
+ __ Push(Handle<Object>(callback_handle->data()));
+ }
+ __ push(name_reg); // name
+ // Save a pointer to where we pushed the arguments pointer.
+ // This will be passed as the const AccessorInfo& to the C++ callback.
+
+#ifdef _WIN64
+ // Win64 uses first register--rcx--for returned value.
+ Register accessor_info_arg = r8;
+ Register name_arg = rdx;
+#else
+ Register accessor_info_arg = rsi;
+ Register name_arg = rdi;
+#endif
+
+ ASSERT(!name_arg.is(scratch2));
+ __ movq(name_arg, rsp);
+ __ push(scratch2); // Restore return address.
+
+ // Do call through the api.
+ Address getter_address = v8::ToCData<Address>(callback->getter());
+ ApiFunction fun(getter_address);
+
+ // 3 elements array for v8::Agruments::values_ and handler for name.
+ const int kStackSpace = 4;
+
+ // Allocate v8::AccessorInfo in non-GCed stack space.
+ const int kArgStackSpace = 1;
+
+ __ PrepareCallApiFunction(kArgStackSpace);
+ __ lea(rax, Operand(name_arg, 3 * kPointerSize));
+
+ // v8::AccessorInfo::args_.
+ __ movq(StackSpaceOperand(0), rax);
+
+ // The context register (rsi) has been saved in PrepareCallApiFunction and
+ // could be used to pass arguments.
+ __ lea(accessor_info_arg, StackSpaceOperand(0));
+
+ // Emitting a stub call may try to allocate (if the code is not
+ // already generated). Do not allow the assembler to perform a
+ // garbage collection but instead return the allocation failure
+ // object.
+ MaybeObject* result = masm()->TryCallApiFunctionAndReturn(&fun, kStackSpace);
+ if (result->IsFailure()) {
+ *failure = Failure::cast(result);
+ return false;
+ }
+ return true;
+}
+
+
+void StubCompiler::GenerateLoadConstant(JSObject* object,
+ JSObject* holder,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Object* value,
+ String* name,
+ Label* miss) {
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, miss);
+
+ // Check that the maps haven't changed.
+ Register reg =
+ CheckPrototypes(object, receiver, holder,
+ scratch1, scratch2, scratch3, name, miss);
+
+ // Return the constant value.
+ __ Move(rax, Handle<Object>(value));
+ __ ret(0);
+}
+
+
+void StubCompiler::GenerateLoadInterceptor(JSObject* object,
+ JSObject* interceptor_holder,
+ LookupResult* lookup,
+ Register receiver,
+ Register name_reg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ String* name,
+ Label* miss) {
+ ASSERT(interceptor_holder->HasNamedInterceptor());
+ ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined());
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, miss);
+
+ // So far the most popular follow ups for interceptor loads are FIELD
+ // and CALLBACKS, so inline only them, other cases may be added
+ // later.
+ bool compile_followup_inline = false;
+ if (lookup->IsProperty() && lookup->IsCacheable()) {
+ if (lookup->type() == FIELD) {
+ compile_followup_inline = true;
+ } else if (lookup->type() == CALLBACKS &&
+ lookup->GetCallbackObject()->IsAccessorInfo() &&
+ AccessorInfo::cast(lookup->GetCallbackObject())->getter() != NULL) {
+ compile_followup_inline = true;
+ }
+ }
+
+ if (compile_followup_inline) {
+ // Compile the interceptor call, followed by inline code to load the
+ // property from further up the prototype chain if the call fails.
+ // Check that the maps haven't changed.
+ Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
+ scratch1, scratch2, scratch3,
+ name, miss);
+ ASSERT(holder_reg.is(receiver) || holder_reg.is(scratch1));
+
+ // Save necessary data before invoking an interceptor.
+ // Requires a frame to make GC aware of pushed pointers.
+ __ EnterInternalFrame();
+
+ if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) {
+ // CALLBACKS case needs a receiver to be passed into C++ callback.
+ __ push(receiver);
+ }
+ __ push(holder_reg);
+ __ push(name_reg);
+
+ // Invoke an interceptor. Note: map checks from receiver to
+ // interceptor's holder has been compiled before (see a caller
+ // of this method.)
+ CompileCallLoadPropertyWithInterceptor(masm(),
+ receiver,
+ holder_reg,
+ name_reg,
+ interceptor_holder);
+
+ // Check if interceptor provided a value for property. If it's
+ // the case, return immediately.
+ Label interceptor_failed;
+ __ CompareRoot(rax, Heap::kNoInterceptorResultSentinelRootIndex);
+ __ j(equal, &interceptor_failed);
+ __ LeaveInternalFrame();
+ __ ret(0);
+
+ __ bind(&interceptor_failed);
+ __ pop(name_reg);
+ __ pop(holder_reg);
+ if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) {
+ __ pop(receiver);
+ }
+
+ __ LeaveInternalFrame();
+
+ // Check that the maps from interceptor's holder to lookup's holder
+ // haven't changed. And load lookup's holder into |holder| register.
+ if (interceptor_holder != lookup->holder()) {
+ holder_reg = CheckPrototypes(interceptor_holder,
+ holder_reg,
+ lookup->holder(),
+ scratch1,
+ scratch2,
+ scratch3,
+ name,
+ miss);
+ }
+
+ if (lookup->type() == FIELD) {
+ // We found FIELD property in prototype chain of interceptor's holder.
+ // Retrieve a field from field's holder.
+ GenerateFastPropertyLoad(masm(), rax, holder_reg,
+ lookup->holder(), lookup->GetFieldIndex());
+ __ ret(0);
+ } else {
+ // We found CALLBACKS property in prototype chain of interceptor's
+ // holder.
+ ASSERT(lookup->type() == CALLBACKS);
+ ASSERT(lookup->GetCallbackObject()->IsAccessorInfo());
+ AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject());
+ ASSERT(callback != NULL);
+ ASSERT(callback->getter() != NULL);
+
+ // Tail call to runtime.
+ // Important invariant in CALLBACKS case: the code above must be
+ // structured to never clobber |receiver| register.
+ __ pop(scratch2); // return address
+ __ push(receiver);
+ __ push(holder_reg);
+ __ Move(holder_reg, Handle<AccessorInfo>(callback));
+ __ push(FieldOperand(holder_reg, AccessorInfo::kDataOffset));
+ __ push(holder_reg);
+ __ push(name_reg);
+ __ push(scratch2); // restore return address
+
+ ExternalReference ref =
+ ExternalReference(IC_Utility(IC::kLoadCallbackProperty));
+ __ TailCallExternalReference(ref, 5, 1);
+ }
+ } else { // !compile_followup_inline
+ // Call the runtime system to load the interceptor.
+ // Check that the maps haven't changed.
+ Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
+ scratch1, scratch2, scratch3,
+ name, miss);
+ __ pop(scratch2); // save old return address
+ PushInterceptorArguments(masm(), receiver, holder_reg,
+ name_reg, interceptor_holder);
+ __ push(scratch2); // restore old return address
+
+ ExternalReference ref = ExternalReference(
+ IC_Utility(IC::kLoadPropertyWithInterceptorForLoad));
+ __ TailCallExternalReference(ref, 5, 1);
+ }
+}
+
+
void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) {
if (kind_ == Code::KEYED_CALL_IC) {
__ Cmp(rcx, Handle<String>(name));
@@ -932,177 +1336,6 @@ MaybeObject* CallStubCompiler::GenerateMissBranch() {
}
-MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
- JSObject* holder,
- JSFunction* function,
- String* name,
- CheckType check) {
- // ----------- S t a t e -------------
- // rcx : function name
- // rsp[0] : return address
- // rsp[8] : argument argc
- // rsp[16] : argument argc - 1
- // ...
- // rsp[argc * 8] : argument 1
- // rsp[(argc + 1) * 8] : argument 0 = receiver
- // -----------------------------------
-
- SharedFunctionInfo* function_info = function->shared();
- if (function_info->HasBuiltinFunctionId()) {
- BuiltinFunctionId id = function_info->builtin_function_id();
- MaybeObject* maybe_result = CompileCustomCall(
- id, object, holder, NULL, function, name);
- Object* result;
- if (!maybe_result->ToObject(&result)) return maybe_result;
- // undefined means bail out to regular compiler.
- if (!result->IsUndefined()) return result;
- }
-
- Label miss_in_smi_check;
-
- GenerateNameCheck(name, &miss_in_smi_check);
-
- // Get the receiver from the stack.
- const int argc = arguments().immediate();
- __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
-
- // Check that the receiver isn't a smi.
- if (check != NUMBER_CHECK) {
- __ JumpIfSmi(rdx, &miss_in_smi_check);
- }
-
- // Make sure that it's okay not to patch the on stack receiver
- // unless we're doing a receiver map check.
- ASSERT(!object->IsGlobalObject() || check == RECEIVER_MAP_CHECK);
-
- CallOptimization optimization(function);
- int depth = kInvalidProtoDepth;
- Label miss;
-
- switch (check) {
- case RECEIVER_MAP_CHECK:
- __ IncrementCounter(&Counters::call_const, 1);
-
- if (optimization.is_simple_api_call() && !object->IsGlobalObject()) {
- depth = optimization.GetPrototypeDepthOfExpectedType(
- JSObject::cast(object), holder);
- }
-
- if (depth != kInvalidProtoDepth) {
- __ IncrementCounter(&Counters::call_const_fast_api, 1);
- // Allocate space for v8::Arguments implicit values. Must be initialized
- // before to call any runtime function.
- __ subq(rsp, Immediate(kFastApiCallArguments * kPointerSize));
- }
-
- // Check that the maps haven't changed.
- CheckPrototypes(JSObject::cast(object), rdx, holder,
- rbx, rax, rdi, name, depth, &miss);
-
- // Patch the receiver on the stack with the global proxy if
- // necessary.
- if (object->IsGlobalObject()) {
- ASSERT(depth == kInvalidProtoDepth);
- __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
- __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx);
- }
- break;
-
- case STRING_CHECK:
- if (!function->IsBuiltin()) {
- // Calling non-builtins with a value as receiver requires boxing.
- __ jmp(&miss);
- } else {
- // Check that the object is a two-byte string or a symbol.
- __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, rax);
- __ j(above_equal, &miss);
- // Check that the maps starting from the prototype haven't changed.
- GenerateDirectLoadGlobalFunctionPrototype(
- masm(), Context::STRING_FUNCTION_INDEX, rax, &miss);
- CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder,
- rbx, rdx, rdi, name, &miss);
- }
- break;
-
- case NUMBER_CHECK: {
- if (!function->IsBuiltin()) {
- // Calling non-builtins with a value as receiver requires boxing.
- __ jmp(&miss);
- } else {
- Label fast;
- // Check that the object is a smi or a heap number.
- __ JumpIfSmi(rdx, &fast);
- __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rax);
- __ j(not_equal, &miss);
- __ bind(&fast);
- // Check that the maps starting from the prototype haven't changed.
- GenerateDirectLoadGlobalFunctionPrototype(
- masm(), Context::NUMBER_FUNCTION_INDEX, rax, &miss);
- CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder,
- rbx, rdx, rdi, name, &miss);
- }
- break;
- }
-
- case BOOLEAN_CHECK: {
- if (!function->IsBuiltin()) {
- // Calling non-builtins with a value as receiver requires boxing.
- __ jmp(&miss);
- } else {
- Label fast;
- // Check that the object is a boolean.
- __ CompareRoot(rdx, Heap::kTrueValueRootIndex);
- __ j(equal, &fast);
- __ CompareRoot(rdx, Heap::kFalseValueRootIndex);
- __ j(not_equal, &miss);
- __ bind(&fast);
- // Check that the maps starting from the prototype haven't changed.
- GenerateDirectLoadGlobalFunctionPrototype(
- masm(), Context::BOOLEAN_FUNCTION_INDEX, rax, &miss);
- CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder,
- rbx, rdx, rdi, name, &miss);
- }
- break;
- }
-
- default:
- UNREACHABLE();
- }
-
- if (depth != kInvalidProtoDepth) {
- Failure* failure;
- // Move the return address on top of the stack.
- __ movq(rax, Operand(rsp, 3 * kPointerSize));
- __ movq(Operand(rsp, 0 * kPointerSize), rax);
-
- // rsp[2 * kPointerSize] is uninitialized, rsp[3 * kPointerSize] contains
- // duplicate of return address and will be overwritten.
- bool success = GenerateFastApiCall(masm(), optimization, argc, &failure);
- if (!success) {
- return failure;
- }
- } else {
- __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
- }
-
- // Handle call cache miss.
- __ bind(&miss);
- if (depth != kInvalidProtoDepth) {
- __ addq(rsp, Immediate(kFastApiCallArguments * kPointerSize));
- }
-
- // Handle call cache miss.
- __ bind(&miss_in_smi_check);
- Object* obj;
- { MaybeObject* maybe_obj = GenerateMissBranch();
- if (!maybe_obj->ToObject(&obj)) return maybe_obj;
- }
-
- // Return the generated code.
- return GetCode(function);
-}
-
-
MaybeObject* CallStubCompiler::CompileCallField(JSObject* object,
JSObject* holder,
int index,
@@ -1248,8 +1481,7 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object,
__ InNewSpace(rbx, rcx, equal, &exit);
- RecordWriteStub stub(rbx, rdx, rcx);
- __ CallStub(&stub);
+ __ RecordWriteHelper(rbx, rdx, rcx);
__ ret((argc + 1) * kPointerSize);
@@ -1408,7 +1640,7 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object,
}
-MaybeObject* CallStubCompiler::CompileStringCharAtCall(
+MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
Object* object,
JSObject* holder,
JSGlobalPropertyCell* cell,
@@ -1441,10 +1673,9 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall(
CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder,
rbx, rdx, rdi, name, &miss);
- Register receiver = rax;
+ Register receiver = rbx;
Register index = rdi;
- Register scratch1 = rbx;
- Register scratch2 = rdx;
+ Register scratch = rdx;
Register result = rax;
__ movq(receiver, Operand(rsp, (argc + 1) * kPointerSize));
if (argc > 0) {
@@ -1453,23 +1684,22 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall(
__ LoadRoot(index, Heap::kUndefinedValueRootIndex);
}
- StringCharAtGenerator char_at_generator(receiver,
- index,
- scratch1,
- scratch2,
- result,
- &miss, // When not a string.
- &miss, // When not a number.
- &index_out_of_range,
- STRING_INDEX_IS_NUMBER);
- char_at_generator.GenerateFast(masm());
+ StringCharCodeAtGenerator char_code_at_generator(receiver,
+ index,
+ scratch,
+ result,
+ &miss, // When not a string.
+ &miss, // When not a number.
+ &index_out_of_range,
+ STRING_INDEX_IS_NUMBER);
+ char_code_at_generator.GenerateFast(masm());
__ ret((argc + 1) * kPointerSize);
StubRuntimeCallHelper call_helper;
- char_at_generator.GenerateSlow(masm(), call_helper);
+ char_code_at_generator.GenerateSlow(masm(), call_helper);
__ bind(&index_out_of_range);
- __ LoadRoot(rax, Heap::kEmptyStringRootIndex);
+ __ LoadRoot(rax, Heap::kNanValueRootIndex);
__ ret((argc + 1) * kPointerSize);
__ bind(&miss);
@@ -1483,7 +1713,7 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall(
}
-MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
+MaybeObject* CallStubCompiler::CompileStringCharAtCall(
Object* object,
JSObject* holder,
JSGlobalPropertyCell* cell,
@@ -1504,6 +1734,7 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
Label miss;
Label index_out_of_range;
+
GenerateNameCheck(name, &miss);
// Check that the maps starting from the prototype haven't changed.
@@ -1515,9 +1746,10 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder,
rbx, rdx, rdi, name, &miss);
- Register receiver = rbx;
+ Register receiver = rax;
Register index = rdi;
- Register scratch = rdx;
+ Register scratch1 = rbx;
+ Register scratch2 = rdx;
Register result = rax;
__ movq(receiver, Operand(rsp, (argc + 1) * kPointerSize));
if (argc > 0) {
@@ -1526,22 +1758,23 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
__ LoadRoot(index, Heap::kUndefinedValueRootIndex);
}
- StringCharCodeAtGenerator char_code_at_generator(receiver,
- index,
- scratch,
- result,
- &miss, // When not a string.
- &miss, // When not a number.
- &index_out_of_range,
- STRING_INDEX_IS_NUMBER);
- char_code_at_generator.GenerateFast(masm());
+ StringCharAtGenerator char_at_generator(receiver,
+ index,
+ scratch1,
+ scratch2,
+ result,
+ &miss, // When not a string.
+ &miss, // When not a number.
+ &index_out_of_range,
+ STRING_INDEX_IS_NUMBER);
+ char_at_generator.GenerateFast(masm());
__ ret((argc + 1) * kPointerSize);
StubRuntimeCallHelper call_helper;
- char_code_at_generator.GenerateSlow(masm(), call_helper);
+ char_at_generator.GenerateSlow(masm(), call_helper);
__ bind(&index_out_of_range);
- __ LoadRoot(rax, Heap::kNanValueRootIndex);
+ __ LoadRoot(rax, Heap::kEmptyStringRootIndex);
__ ret((argc + 1) * kPointerSize);
__ bind(&miss);
@@ -1741,6 +1974,178 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object,
}
+MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
+ JSObject* holder,
+ JSFunction* function,
+ String* name,
+ CheckType check) {
+ // ----------- S t a t e -------------
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
+ // ...
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // -----------------------------------
+
+ SharedFunctionInfo* function_info = function->shared();
+ if (function_info->HasBuiltinFunctionId()) {
+ BuiltinFunctionId id = function_info->builtin_function_id();
+ MaybeObject* maybe_result = CompileCustomCall(
+ id, object, holder, NULL, function, name);
+ Object* result;
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ // undefined means bail out to regular compiler.
+ if (!result->IsUndefined()) return result;
+ }
+
+ Label miss_in_smi_check;
+
+ GenerateNameCheck(name, &miss_in_smi_check);
+
+ // Get the receiver from the stack.
+ const int argc = arguments().immediate();
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ if (check != NUMBER_CHECK) {
+ __ JumpIfSmi(rdx, &miss_in_smi_check);
+ }
+
+ // Make sure that it's okay not to patch the on stack receiver
+ // unless we're doing a receiver map check.
+ ASSERT(!object->IsGlobalObject() || check == RECEIVER_MAP_CHECK);
+
+ CallOptimization optimization(function);
+ int depth = kInvalidProtoDepth;
+ Label miss;
+
+ switch (check) {
+ case RECEIVER_MAP_CHECK:
+ __ IncrementCounter(&Counters::call_const, 1);
+
+ if (optimization.is_simple_api_call() && !object->IsGlobalObject()) {
+ depth = optimization.GetPrototypeDepthOfExpectedType(
+ JSObject::cast(object), holder);
+ }
+
+ if (depth != kInvalidProtoDepth) {
+ __ IncrementCounter(&Counters::call_const_fast_api, 1);
+
+ // Allocate space for v8::Arguments implicit values. Must be initialized
+ // before to call any runtime function.
+ __ subq(rsp, Immediate(kFastApiCallArguments * kPointerSize));
+ }
+
+ // Check that the maps haven't changed.
+ CheckPrototypes(JSObject::cast(object), rdx, holder,
+ rbx, rax, rdi, name, depth, &miss);
+
+ // Patch the receiver on the stack with the global proxy if
+ // necessary.
+ if (object->IsGlobalObject()) {
+ ASSERT(depth == kInvalidProtoDepth);
+ __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
+ __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx);
+ }
+ break;
+
+ case STRING_CHECK:
+ if (!function->IsBuiltin()) {
+ // Calling non-builtins with a value as receiver requires boxing.
+ __ jmp(&miss);
+ } else {
+ // Check that the object is a two-byte string or a symbol.
+ __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, rax);
+ __ j(above_equal, &miss);
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateDirectLoadGlobalFunctionPrototype(
+ masm(), Context::STRING_FUNCTION_INDEX, rax, &miss);
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder,
+ rbx, rdx, rdi, name, &miss);
+ }
+ break;
+
+ case NUMBER_CHECK: {
+ if (!function->IsBuiltin()) {
+ // Calling non-builtins with a value as receiver requires boxing.
+ __ jmp(&miss);
+ } else {
+ Label fast;
+ // Check that the object is a smi or a heap number.
+ __ JumpIfSmi(rdx, &fast);
+ __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rax);
+ __ j(not_equal, &miss);
+ __ bind(&fast);
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateDirectLoadGlobalFunctionPrototype(
+ masm(), Context::NUMBER_FUNCTION_INDEX, rax, &miss);
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder,
+ rbx, rdx, rdi, name, &miss);
+ }
+ break;
+ }
+
+ case BOOLEAN_CHECK: {
+ if (!function->IsBuiltin()) {
+ // Calling non-builtins with a value as receiver requires boxing.
+ __ jmp(&miss);
+ } else {
+ Label fast;
+ // Check that the object is a boolean.
+ __ CompareRoot(rdx, Heap::kTrueValueRootIndex);
+ __ j(equal, &fast);
+ __ CompareRoot(rdx, Heap::kFalseValueRootIndex);
+ __ j(not_equal, &miss);
+ __ bind(&fast);
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateDirectLoadGlobalFunctionPrototype(
+ masm(), Context::BOOLEAN_FUNCTION_INDEX, rax, &miss);
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder,
+ rbx, rdx, rdi, name, &miss);
+ }
+ break;
+ }
+
+ default:
+ UNREACHABLE();
+ }
+
+ if (depth != kInvalidProtoDepth) {
+ Failure* failure;
+ // Move the return address on top of the stack.
+ __ movq(rax, Operand(rsp, 3 * kPointerSize));
+ __ movq(Operand(rsp, 0 * kPointerSize), rax);
+
+ // rsp[2 * kPointerSize] is uninitialized, rsp[3 * kPointerSize] contains
+ // duplicate of return address and will be overwritten.
+ bool success = GenerateFastApiCall(masm(), optimization, argc, &failure);
+ if (!success) {
+ return failure;
+ }
+ } else {
+ __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+ }
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ if (depth != kInvalidProtoDepth) {
+ __ addq(rsp, Immediate(kFastApiCallArguments * kPointerSize));
+ }
+
+ // Handle call cache miss.
+ __ bind(&miss_in_smi_check);
+ Object* obj;
+ { MaybeObject* maybe_obj = GenerateMissBranch();
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+
+ // Return the generated code.
+ return GetCode(function);
+}
+
+
MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object,
JSObject* holder,
String* name) {
@@ -1881,50 +2286,260 @@ MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object,
}
-MaybeObject* LoadStubCompiler::CompileLoadCallback(String* name,
- JSObject* object,
- JSObject* holder,
- AccessorInfo* callback) {
+MaybeObject* StoreStubCompiler::CompileStoreField(JSObject* object,
+ int index,
+ Map* transition,
+ String* name) {
// ----------- S t a t e -------------
- // -- rax : receiver
+ // -- rax : value
// -- rcx : name
+ // -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
Label miss;
- Failure* failure = Failure::InternalError();
- bool success = GenerateLoadCallback(object, holder, rax, rcx, rdx, rbx, rdi,
- callback, name, &miss, &failure);
- if (!success) {
- miss.Unuse();
- return failure;
+ // Generate store field code. Preserves receiver and name on jump to miss.
+ GenerateStoreField(masm(),
+ object,
+ index,
+ transition,
+ rdx, rcx, rbx,
+ &miss);
+
+ // Handle store cache miss.
+ __ bind(&miss);
+ Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss));
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name);
+}
+
+
+MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object,
+ AccessorInfo* callback,
+ String* name) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : name
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label miss;
+
+ // Check that the object isn't a smi.
+ __ JumpIfSmi(rdx, &miss);
+
+ // Check that the map of the object hasn't changed.
+ __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
+ Handle<Map>(object->map()));
+ __ j(not_equal, &miss);
+
+ // Perform global security token check if needed.
+ if (object->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(rdx, rbx, &miss);
}
+ // Stub never generated for non-global objects that require access
+ // checks.
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
+
+ __ pop(rbx); // remove the return address
+ __ push(rdx); // receiver
+ __ Push(Handle<AccessorInfo>(callback)); // callback info
+ __ push(rcx); // name
+ __ push(rax); // value
+ __ push(rbx); // restore return address
+
+ // Do tail-call to the runtime system.
+ ExternalReference store_callback_property =
+ ExternalReference(IC_Utility(IC::kStoreCallbackProperty));
+ __ TailCallExternalReference(store_callback_property, 4, 1);
+
+ // Handle store cache miss.
__ bind(&miss);
- GenerateLoadMiss(masm(), Code::LOAD_IC);
+ Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss));
+ __ Jump(ic, RelocInfo::CODE_TARGET);
// Return the generated code.
return GetCode(CALLBACKS, name);
}
-MaybeObject* LoadStubCompiler::CompileLoadConstant(JSObject* object,
- JSObject* holder,
- Object* value,
+MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver,
+ String* name) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : name
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label miss;
+
+ // Check that the object isn't a smi.
+ __ JumpIfSmi(rdx, &miss);
+
+ // Check that the map of the object hasn't changed.
+ __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
+ Handle<Map>(receiver->map()));
+ __ j(not_equal, &miss);
+
+ // Perform global security token check if needed.
+ if (receiver->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(rdx, rbx, &miss);
+ }
+
+ // Stub never generated for non-global objects that require access
+ // checks.
+ ASSERT(receiver->IsJSGlobalProxy() || !receiver->IsAccessCheckNeeded());
+
+ __ pop(rbx); // remove the return address
+ __ push(rdx); // receiver
+ __ push(rcx); // name
+ __ push(rax); // value
+ __ push(rbx); // restore return address
+
+ // Do tail-call to the runtime system.
+ ExternalReference store_ic_property =
+ ExternalReference(IC_Utility(IC::kStoreInterceptorProperty));
+ __ TailCallExternalReference(store_ic_property, 3, 1);
+
+ // Handle store cache miss.
+ __ bind(&miss);
+ Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss));
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode(INTERCEPTOR, name);
+}
+
+
+MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object,
+ JSGlobalPropertyCell* cell,
String* name) {
// ----------- S t a t e -------------
- // -- rax : receiver
+ // -- rax : value
// -- rcx : name
+ // -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
Label miss;
- GenerateLoadConstant(object, holder, rax, rbx, rdx, rdi, value, name, &miss);
+ // Check that the map of the global has not changed.
+ __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
+ Handle<Map>(object->map()));
+ __ j(not_equal, &miss);
+
+ // Store the value in the cell.
+ __ Move(rcx, Handle<JSGlobalPropertyCell>(cell));
+ __ movq(FieldOperand(rcx, JSGlobalPropertyCell::kValueOffset), rax);
+
+ // Return the value (register rax).
+ __ IncrementCounter(&Counters::named_store_global_inline, 1);
+ __ ret(0);
+
+ // Handle store cache miss.
__ bind(&miss);
- GenerateLoadMiss(masm(), Code::LOAD_IC);
+ __ IncrementCounter(&Counters::named_store_global_inline_miss, 1);
+ Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss));
+ __ Jump(ic, RelocInfo::CODE_TARGET);
// Return the generated code.
- return GetCode(CONSTANT_FUNCTION, name);
+ return GetCode(NORMAL, name);
+}
+
+
+MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object,
+ int index,
+ Map* transition,
+ String* name) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label miss;
+
+ __ IncrementCounter(&Counters::keyed_store_field, 1);
+
+ // Check that the name has not changed.
+ __ Cmp(rcx, Handle<String>(name));
+ __ j(not_equal, &miss);
+
+ // Generate store field code. Preserves receiver and name on jump to miss.
+ GenerateStoreField(masm(),
+ object,
+ index,
+ transition,
+ rdx, rcx, rbx,
+ &miss);
+
+ // Handle store cache miss.
+ __ bind(&miss);
+ __ DecrementCounter(&Counters::keyed_store_field, 1);
+ Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Miss));
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name);
+}
+
+
+MaybeObject* KeyedStoreStubCompiler::CompileStoreSpecialized(
+ JSObject* receiver) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label miss;
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(rdx, &miss);
+
+ // Check that the map matches.
+ __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
+ Handle<Map>(receiver->map()));
+ __ j(not_equal, &miss);
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(rcx, &miss);
+
+ // Get the elements array and make sure it is a fast element array, not 'cow'.
+ __ movq(rdi, FieldOperand(rdx, JSObject::kElementsOffset));
+ __ Cmp(FieldOperand(rdi, HeapObject::kMapOffset),
+ Factory::fixed_array_map());
+ __ j(not_equal, &miss);
+
+ // Check that the key is within bounds.
+ if (receiver->IsJSArray()) {
+ __ SmiCompare(rcx, FieldOperand(rdx, JSArray::kLengthOffset));
+ __ j(above_equal, &miss);
+ } else {
+ __ SmiCompare(rcx, FieldOperand(rdi, FixedArray::kLengthOffset));
+ __ j(above_equal, &miss);
+ }
+
+ // Do the store and update the write barrier. Make sure to preserve
+ // the value in register eax.
+ __ movq(rdx, rax);
+ __ SmiToInteger32(rcx, rcx);
+ __ movq(FieldOperand(rdi, rcx, times_pointer_size, FixedArray::kHeaderSize),
+ rax);
+ __ RecordWrite(rdi, 0, rdx, rcx);
+
+ // Done.
+ __ ret(0);
+
+ // Handle store cache miss.
+ __ bind(&miss);
+ Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Miss));
+ __ jmp(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode(NORMAL, NULL);
}
@@ -1993,6 +2608,53 @@ MaybeObject* LoadStubCompiler::CompileLoadField(JSObject* object,
}
+MaybeObject* LoadStubCompiler::CompileLoadCallback(String* name,
+ JSObject* object,
+ JSObject* holder,
+ AccessorInfo* callback) {
+ // ----------- S t a t e -------------
+ // -- rax : receiver
+ // -- rcx : name
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label miss;
+
+ Failure* failure = Failure::InternalError();
+ bool success = GenerateLoadCallback(object, holder, rax, rcx, rdx, rbx, rdi,
+ callback, name, &miss, &failure);
+ if (!success) {
+ miss.Unuse();
+ return failure;
+ }
+
+ __ bind(&miss);
+ GenerateLoadMiss(masm(), Code::LOAD_IC);
+
+ // Return the generated code.
+ return GetCode(CALLBACKS, name);
+}
+
+
+MaybeObject* LoadStubCompiler::CompileLoadConstant(JSObject* object,
+ JSObject* holder,
+ Object* value,
+ String* name) {
+ // ----------- S t a t e -------------
+ // -- rax : receiver
+ // -- rcx : name
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label miss;
+
+ GenerateLoadConstant(object, holder, rax, rbx, rdx, rdi, value, name, &miss);
+ __ bind(&miss);
+ GenerateLoadMiss(masm(), Code::LOAD_IC);
+
+ // Return the generated code.
+ return GetCode(CONSTANT_FUNCTION, name);
+}
+
+
MaybeObject* LoadStubCompiler::CompileLoadInterceptor(JSObject* receiver,
JSObject* holder,
String* name) {
@@ -2075,11 +2737,10 @@ MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object,
}
-MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback(
- String* name,
- JSObject* receiver,
- JSObject* holder,
- AccessorInfo* callback) {
+MaybeObject* KeyedLoadStubCompiler::CompileLoadField(String* name,
+ JSObject* receiver,
+ JSObject* holder,
+ int index) {
// ----------- S t a t e -------------
// -- rax : key
// -- rdx : receiver
@@ -2087,46 +2748,52 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback(
// -----------------------------------
Label miss;
- __ IncrementCounter(&Counters::keyed_load_callback, 1);
+ __ IncrementCounter(&Counters::keyed_load_field, 1);
// Check that the name has not changed.
__ Cmp(rax, Handle<String>(name));
__ j(not_equal, &miss);
- Failure* failure = Failure::InternalError();
- bool success = GenerateLoadCallback(receiver, holder, rdx, rax, rbx, rcx, rdi,
- callback, name, &miss, &failure);
- if (!success) {
- miss.Unuse();
- return failure;
- }
+ GenerateLoadField(receiver, holder, rdx, rbx, rcx, rdi, index, name, &miss);
__ bind(&miss);
- __ DecrementCounter(&Counters::keyed_load_callback, 1);
+ __ DecrementCounter(&Counters::keyed_load_field, 1);
GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
// Return the generated code.
- return GetCode(CALLBACKS, name);
+ return GetCode(FIELD, name);
}
-MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) {
+MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback(
+ String* name,
+ JSObject* receiver,
+ JSObject* holder,
+ AccessorInfo* callback) {
// ----------- S t a t e -------------
- // -- rax : key
- // -- rdx : receiver
+ // -- rax : key
+ // -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
Label miss;
- __ IncrementCounter(&Counters::keyed_load_array_length, 1);
+ __ IncrementCounter(&Counters::keyed_load_callback, 1);
// Check that the name has not changed.
__ Cmp(rax, Handle<String>(name));
__ j(not_equal, &miss);
- GenerateLoadArrayLength(masm(), rdx, rcx, &miss);
+ Failure* failure = Failure::InternalError();
+ bool success = GenerateLoadCallback(receiver, holder, rdx, rax, rbx, rcx, rdi,
+ callback, name, &miss, &failure);
+ if (!success) {
+ miss.Unuse();
+ return failure;
+ }
+
__ bind(&miss);
- __ DecrementCounter(&Counters::keyed_load_array_length, 1);
+
+ __ DecrementCounter(&Counters::keyed_load_callback, 1);
GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
// Return the generated code.
@@ -2162,30 +2829,6 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadConstant(String* name,
}
-MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) {
- // ----------- S t a t e -------------
- // -- rax : key
- // -- rdx : receiver
- // -- rsp[0] : return address
- // -----------------------------------
- Label miss;
-
- __ IncrementCounter(&Counters::keyed_load_function_prototype, 1);
-
- // Check that the name has not changed.
- __ Cmp(rax, Handle<String>(name));
- __ j(not_equal, &miss);
-
- GenerateLoadFunctionPrototype(masm(), rdx, rcx, rbx, &miss);
- __ bind(&miss);
- __ DecrementCounter(&Counters::keyed_load_function_prototype, 1);
- GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
-
- // Return the generated code.
- return GetCode(CALLBACKS, name);
-}
-
-
MaybeObject* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver,
JSObject* holder,
String* name) {
@@ -2223,23 +2866,23 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver,
}
-MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) {
+MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) {
// ----------- S t a t e -------------
// -- rax : key
// -- rdx : receiver
- // -- rsp[0] : return address
+ // -- rsp[0] : return address
// -----------------------------------
Label miss;
- __ IncrementCounter(&Counters::keyed_load_string_length, 1);
+ __ IncrementCounter(&Counters::keyed_load_array_length, 1);
// Check that the name has not changed.
__ Cmp(rax, Handle<String>(name));
__ j(not_equal, &miss);
- GenerateLoadStringLength(masm(), rdx, rcx, rbx, &miss);
+ GenerateLoadArrayLength(masm(), rdx, rcx, &miss);
__ bind(&miss);
- __ DecrementCounter(&Counters::keyed_load_string_length, 1);
+ __ DecrementCounter(&Counters::keyed_load_array_length, 1);
GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
// Return the generated code.
@@ -2247,287 +2890,59 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) {
}
-MaybeObject* KeyedLoadStubCompiler::CompileLoadSpecialized(JSObject* receiver) {
+MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) {
// ----------- S t a t e -------------
// -- rax : key
// -- rdx : receiver
- // -- esp[0] : return address
- // -----------------------------------
- Label miss;
-
- // Check that the receiver isn't a smi.
- __ JumpIfSmi(rdx, &miss);
-
- // Check that the map matches.
- __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
- Handle<Map>(receiver->map()));
- __ j(not_equal, &miss);
-
- // Check that the key is a smi.
- __ JumpIfNotSmi(rax, &miss);
-
- // Get the elements array.
- __ movq(rcx, FieldOperand(rdx, JSObject::kElementsOffset));
- __ AssertFastElements(rcx);
-
- // Check that the key is within bounds.
- __ SmiCompare(rax, FieldOperand(rcx, FixedArray::kLengthOffset));
- __ j(above_equal, &miss);
-
- // Load the result and make sure it's not the hole.
- SmiIndex index = masm()->SmiToIndex(rbx, rax, kPointerSizeLog2);
- __ movq(rbx, FieldOperand(rcx,
- index.reg,
- index.scale,
- FixedArray::kHeaderSize));
- __ CompareRoot(rbx, Heap::kTheHoleValueRootIndex);
- __ j(equal, &miss);
- __ movq(rax, rbx);
- __ ret(0);
-
- __ bind(&miss);
- GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
-
- // Return the generated code.
- return GetCode(NORMAL, NULL);
-}
-
-
-MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object,
- AccessorInfo* callback,
- String* name) {
- // ----------- S t a t e -------------
- // -- rax : value
- // -- rcx : name
- // -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
Label miss;
- // Check that the object isn't a smi.
- __ JumpIfSmi(rdx, &miss);
+ __ IncrementCounter(&Counters::keyed_load_string_length, 1);
- // Check that the map of the object hasn't changed.
- __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
- Handle<Map>(object->map()));
+ // Check that the name has not changed.
+ __ Cmp(rax, Handle<String>(name));
__ j(not_equal, &miss);
- // Perform global security token check if needed.
- if (object->IsJSGlobalProxy()) {
- __ CheckAccessGlobalProxy(rdx, rbx, &miss);
- }
-
- // Stub never generated for non-global objects that require access
- // checks.
- ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
-
- __ pop(rbx); // remove the return address
- __ push(rdx); // receiver
- __ Push(Handle<AccessorInfo>(callback)); // callback info
- __ push(rcx); // name
- __ push(rax); // value
- __ push(rbx); // restore return address
-
- // Do tail-call to the runtime system.
- ExternalReference store_callback_property =
- ExternalReference(IC_Utility(IC::kStoreCallbackProperty));
- __ TailCallExternalReference(store_callback_property, 4, 1);
-
- // Handle store cache miss.
+ GenerateLoadStringLength(masm(), rdx, rcx, rbx, &miss);
__ bind(&miss);
- Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss));
- __ Jump(ic, RelocInfo::CODE_TARGET);
+ __ DecrementCounter(&Counters::keyed_load_string_length, 1);
+ GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
// Return the generated code.
return GetCode(CALLBACKS, name);
}
-MaybeObject* StoreStubCompiler::CompileStoreField(JSObject* object,
- int index,
- Map* transition,
- String* name) {
- // ----------- S t a t e -------------
- // -- rax : value
- // -- rcx : name
- // -- rdx : receiver
- // -- rsp[0] : return address
- // -----------------------------------
- Label miss;
-
- // Generate store field code. Preserves receiver and name on jump to miss.
- GenerateStoreField(masm(),
- object,
- index,
- transition,
- rdx, rcx, rbx,
- &miss);
-
- // Handle store cache miss.
- __ bind(&miss);
- Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss));
- __ Jump(ic, RelocInfo::CODE_TARGET);
-
- // Return the generated code.
- return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name);
-}
-
-
-MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver,
- String* name) {
- // ----------- S t a t e -------------
- // -- rax : value
- // -- rcx : name
- // -- rdx : receiver
- // -- rsp[0] : return address
- // -----------------------------------
- Label miss;
-
- // Check that the object isn't a smi.
- __ JumpIfSmi(rdx, &miss);
-
- // Check that the map of the object hasn't changed.
- __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
- Handle<Map>(receiver->map()));
- __ j(not_equal, &miss);
-
- // Perform global security token check if needed.
- if (receiver->IsJSGlobalProxy()) {
- __ CheckAccessGlobalProxy(rdx, rbx, &miss);
- }
-
- // Stub never generated for non-global objects that require access
- // checks.
- ASSERT(receiver->IsJSGlobalProxy() || !receiver->IsAccessCheckNeeded());
-
- __ pop(rbx); // remove the return address
- __ push(rdx); // receiver
- __ push(rcx); // name
- __ push(rax); // value
- __ push(rbx); // restore return address
-
- // Do tail-call to the runtime system.
- ExternalReference store_ic_property =
- ExternalReference(IC_Utility(IC::kStoreInterceptorProperty));
- __ TailCallExternalReference(store_ic_property, 3, 1);
-
- // Handle store cache miss.
- __ bind(&miss);
- Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss));
- __ Jump(ic, RelocInfo::CODE_TARGET);
-
- // Return the generated code.
- return GetCode(INTERCEPTOR, name);
-}
-
-
-MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object,
- JSGlobalPropertyCell* cell,
- String* name) {
+MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) {
// ----------- S t a t e -------------
- // -- rax : value
- // -- rcx : name
+ // -- rax : key
// -- rdx : receiver
- // -- rsp[0] : return address
- // -----------------------------------
- Label miss;
-
- // Check that the map of the global has not changed.
- __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
- Handle<Map>(object->map()));
- __ j(not_equal, &miss);
-
- // Store the value in the cell.
- __ Move(rcx, Handle<JSGlobalPropertyCell>(cell));
- __ movq(FieldOperand(rcx, JSGlobalPropertyCell::kValueOffset), rax);
-
- // Return the value (register rax).
- __ IncrementCounter(&Counters::named_store_global_inline, 1);
- __ ret(0);
-
- // Handle store cache miss.
- __ bind(&miss);
- __ IncrementCounter(&Counters::named_store_global_inline_miss, 1);
- Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss));
- __ Jump(ic, RelocInfo::CODE_TARGET);
-
- // Return the generated code.
- return GetCode(NORMAL, name);
-}
-
-
-MaybeObject* KeyedLoadStubCompiler::CompileLoadField(String* name,
- JSObject* receiver,
- JSObject* holder,
- int index) {
- // ----------- S t a t e -------------
- // -- rax : key
- // -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
Label miss;
- __ IncrementCounter(&Counters::keyed_load_field, 1);
+ __ IncrementCounter(&Counters::keyed_load_function_prototype, 1);
// Check that the name has not changed.
__ Cmp(rax, Handle<String>(name));
__ j(not_equal, &miss);
- GenerateLoadField(receiver, holder, rdx, rbx, rcx, rdi, index, name, &miss);
-
+ GenerateLoadFunctionPrototype(masm(), rdx, rcx, rbx, &miss);
__ bind(&miss);
- __ DecrementCounter(&Counters::keyed_load_field, 1);
+ __ DecrementCounter(&Counters::keyed_load_function_prototype, 1);
GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
// Return the generated code.
- return GetCode(FIELD, name);
-}
-
-
-MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object,
- int index,
- Map* transition,
- String* name) {
- // ----------- S t a t e -------------
- // -- rax : value
- // -- rcx : key
- // -- rdx : receiver
- // -- rsp[0] : return address
- // -----------------------------------
- Label miss;
-
- __ IncrementCounter(&Counters::keyed_store_field, 1);
-
- // Check that the name has not changed.
- __ Cmp(rcx, Handle<String>(name));
- __ j(not_equal, &miss);
-
- // Generate store field code. Preserves receiver and name on jump to miss.
- GenerateStoreField(masm(),
- object,
- index,
- transition,
- rdx, rcx, rbx,
- &miss);
-
- // Handle store cache miss.
- __ bind(&miss);
- __ DecrementCounter(&Counters::keyed_store_field, 1);
- Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Miss));
- __ Jump(ic, RelocInfo::CODE_TARGET);
-
- // Return the generated code.
- return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name);
+ return GetCode(CALLBACKS, name);
}
-MaybeObject* KeyedStoreStubCompiler::CompileStoreSpecialized(
- JSObject* receiver) {
+MaybeObject* KeyedLoadStubCompiler::CompileLoadSpecialized(JSObject* receiver) {
// ----------- S t a t e -------------
- // -- rax : value
- // -- rcx : key
+ // -- rax : key
// -- rdx : receiver
- // -- rsp[0] : return address
+ // -- esp[0] : return address
// -----------------------------------
Label miss;
@@ -2540,455 +2955,35 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreSpecialized(
__ j(not_equal, &miss);
// Check that the key is a smi.
- __ JumpIfNotSmi(rcx, &miss);
+ __ JumpIfNotSmi(rax, &miss);
- // Get the elements array and make sure it is a fast element array, not 'cow'.
- __ movq(rdi, FieldOperand(rdx, JSObject::kElementsOffset));
- __ Cmp(FieldOperand(rdi, HeapObject::kMapOffset),
- Factory::fixed_array_map());
- __ j(not_equal, &miss);
+ // Get the elements array.
+ __ movq(rcx, FieldOperand(rdx, JSObject::kElementsOffset));
+ __ AssertFastElements(rcx);
// Check that the key is within bounds.
- if (receiver->IsJSArray()) {
- __ SmiCompare(rcx, FieldOperand(rdx, JSArray::kLengthOffset));
- __ j(above_equal, &miss);
- } else {
- __ SmiCompare(rcx, FieldOperand(rdi, FixedArray::kLengthOffset));
- __ j(above_equal, &miss);
- }
-
- // Do the store and update the write barrier. Make sure to preserve
- // the value in register eax.
- __ movq(rdx, rax);
- __ SmiToInteger32(rcx, rcx);
- __ movq(FieldOperand(rdi, rcx, times_pointer_size, FixedArray::kHeaderSize),
- rax);
- __ RecordWrite(rdi, 0, rdx, rcx);
+ __ SmiCompare(rax, FieldOperand(rcx, FixedArray::kLengthOffset));
+ __ j(above_equal, &miss);
- // Done.
+ // Load the result and make sure it's not the hole.
+ SmiIndex index = masm()->SmiToIndex(rbx, rax, kPointerSizeLog2);
+ __ movq(rbx, FieldOperand(rcx,
+ index.reg,
+ index.scale,
+ FixedArray::kHeaderSize));
+ __ CompareRoot(rbx, Heap::kTheHoleValueRootIndex);
+ __ j(equal, &miss);
+ __ movq(rax, rbx);
__ ret(0);
- // Handle store cache miss.
__ bind(&miss);
- Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Miss));
- __ jmp(ic, RelocInfo::CODE_TARGET);
+ GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
// Return the generated code.
return GetCode(NORMAL, NULL);
}
-void StubCompiler::GenerateLoadInterceptor(JSObject* object,
- JSObject* interceptor_holder,
- LookupResult* lookup,
- Register receiver,
- Register name_reg,
- Register scratch1,
- Register scratch2,
- Register scratch3,
- String* name,
- Label* miss) {
- ASSERT(interceptor_holder->HasNamedInterceptor());
- ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined());
-
- // Check that the receiver isn't a smi.
- __ JumpIfSmi(receiver, miss);
-
- // So far the most popular follow ups for interceptor loads are FIELD
- // and CALLBACKS, so inline only them, other cases may be added
- // later.
- bool compile_followup_inline = false;
- if (lookup->IsProperty() && lookup->IsCacheable()) {
- if (lookup->type() == FIELD) {
- compile_followup_inline = true;
- } else if (lookup->type() == CALLBACKS &&
- lookup->GetCallbackObject()->IsAccessorInfo() &&
- AccessorInfo::cast(lookup->GetCallbackObject())->getter() != NULL) {
- compile_followup_inline = true;
- }
- }
-
- if (compile_followup_inline) {
- // Compile the interceptor call, followed by inline code to load the
- // property from further up the prototype chain if the call fails.
- // Check that the maps haven't changed.
- Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
- scratch1, scratch2, scratch3,
- name, miss);
- ASSERT(holder_reg.is(receiver) || holder_reg.is(scratch1));
-
- // Save necessary data before invoking an interceptor.
- // Requires a frame to make GC aware of pushed pointers.
- __ EnterInternalFrame();
-
- if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) {
- // CALLBACKS case needs a receiver to be passed into C++ callback.
- __ push(receiver);
- }
- __ push(holder_reg);
- __ push(name_reg);
-
- // Invoke an interceptor. Note: map checks from receiver to
- // interceptor's holder has been compiled before (see a caller
- // of this method.)
- CompileCallLoadPropertyWithInterceptor(masm(),
- receiver,
- holder_reg,
- name_reg,
- interceptor_holder);
-
- // Check if interceptor provided a value for property. If it's
- // the case, return immediately.
- Label interceptor_failed;
- __ CompareRoot(rax, Heap::kNoInterceptorResultSentinelRootIndex);
- __ j(equal, &interceptor_failed);
- __ LeaveInternalFrame();
- __ ret(0);
-
- __ bind(&interceptor_failed);
- __ pop(name_reg);
- __ pop(holder_reg);
- if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) {
- __ pop(receiver);
- }
-
- __ LeaveInternalFrame();
-
- // Check that the maps from interceptor's holder to lookup's holder
- // haven't changed. And load lookup's holder into |holder| register.
- if (interceptor_holder != lookup->holder()) {
- holder_reg = CheckPrototypes(interceptor_holder,
- holder_reg,
- lookup->holder(),
- scratch1,
- scratch2,
- scratch3,
- name,
- miss);
- }
-
- if (lookup->type() == FIELD) {
- // We found FIELD property in prototype chain of interceptor's holder.
- // Retrieve a field from field's holder.
- GenerateFastPropertyLoad(masm(), rax, holder_reg,
- lookup->holder(), lookup->GetFieldIndex());
- __ ret(0);
- } else {
- // We found CALLBACKS property in prototype chain of interceptor's
- // holder.
- ASSERT(lookup->type() == CALLBACKS);
- ASSERT(lookup->GetCallbackObject()->IsAccessorInfo());
- AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject());
- ASSERT(callback != NULL);
- ASSERT(callback->getter() != NULL);
-
- // Tail call to runtime.
- // Important invariant in CALLBACKS case: the code above must be
- // structured to never clobber |receiver| register.
- __ pop(scratch2); // return address
- __ push(receiver);
- __ push(holder_reg);
- __ Move(holder_reg, Handle<AccessorInfo>(callback));
- __ push(FieldOperand(holder_reg, AccessorInfo::kDataOffset));
- __ push(holder_reg);
- __ push(name_reg);
- __ push(scratch2); // restore return address
-
- ExternalReference ref =
- ExternalReference(IC_Utility(IC::kLoadCallbackProperty));
- __ TailCallExternalReference(ref, 5, 1);
- }
- } else { // !compile_followup_inline
- // Call the runtime system to load the interceptor.
- // Check that the maps haven't changed.
- Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
- scratch1, scratch2, scratch3,
- name, miss);
- __ pop(scratch2); // save old return address
- PushInterceptorArguments(masm(), receiver, holder_reg,
- name_reg, interceptor_holder);
- __ push(scratch2); // restore old return address
-
- ExternalReference ref = ExternalReference(
- IC_Utility(IC::kLoadPropertyWithInterceptorForLoad));
- __ TailCallExternalReference(ref, 5, 1);
- }
-}
-
-
-bool StubCompiler::GenerateLoadCallback(JSObject* object,
- JSObject* holder,
- Register receiver,
- Register name_reg,
- Register scratch1,
- Register scratch2,
- Register scratch3,
- AccessorInfo* callback,
- String* name,
- Label* miss,
- Failure** failure) {
- // Check that the receiver isn't a smi.
- __ JumpIfSmi(receiver, miss);
-
- // Check that the maps haven't changed.
- Register reg =
- CheckPrototypes(object, receiver, holder, scratch1,
- scratch2, scratch3, name, miss);
-
- Handle<AccessorInfo> callback_handle(callback);
-
- // Insert additional parameters into the stack frame above return address.
- ASSERT(!scratch2.is(reg));
- __ pop(scratch2); // Get return address to place it below.
-
- __ push(receiver); // receiver
- __ push(reg); // holder
- if (Heap::InNewSpace(callback_handle->data())) {
- __ Move(scratch1, callback_handle);
- __ push(FieldOperand(scratch1, AccessorInfo::kDataOffset)); // data
- } else {
- __ Push(Handle<Object>(callback_handle->data()));
- }
- __ push(name_reg); // name
- // Save a pointer to where we pushed the arguments pointer.
- // This will be passed as the const AccessorInfo& to the C++ callback.
-
-#ifdef _WIN64
- // Win64 uses first register--rcx--for returned value.
- Register accessor_info_arg = r8;
- Register name_arg = rdx;
-#else
- Register accessor_info_arg = rsi;
- Register name_arg = rdi;
-#endif
-
- ASSERT(!name_arg.is(scratch2));
- __ movq(name_arg, rsp);
- __ push(scratch2); // Restore return address.
-
- // Do call through the api.
- Address getter_address = v8::ToCData<Address>(callback->getter());
- ApiFunction fun(getter_address);
-
- // 3 elements array for v8::Agruments::values_ and handler for name.
- const int kStackSpace = 4;
-
- // Allocate v8::AccessorInfo in non-GCed stack space.
- const int kArgStackSpace = 1;
-
- __ PrepareCallApiFunction(kArgStackSpace);
- __ lea(rax, Operand(name_arg, 3 * kPointerSize));
-
- // v8::AccessorInfo::args_.
- __ movq(StackSpaceOperand(0), rax);
-
- // The context register (rsi) has been saved in PrepareCallApiFunction and
- // could be used to pass arguments.
- __ lea(accessor_info_arg, StackSpaceOperand(0));
-
- // Emitting a stub call may try to allocate (if the code is not
- // already generated). Do not allow the assembler to perform a
- // garbage collection but instead return the allocation failure
- // object.
- MaybeObject* result = masm()->TryCallApiFunctionAndReturn(&fun, kStackSpace);
- if (result->IsFailure()) {
- *failure = Failure::cast(result);
- return false;
- }
- return true;
-}
-
-
-Register StubCompiler::CheckPrototypes(JSObject* object,
- Register object_reg,
- JSObject* holder,
- Register holder_reg,
- Register scratch1,
- Register scratch2,
- String* name,
- int save_at_depth,
- Label* miss) {
- // Make sure there's no overlap between holder and object registers.
- ASSERT(!scratch1.is(object_reg) && !scratch1.is(holder_reg));
- ASSERT(!scratch2.is(object_reg) && !scratch2.is(holder_reg)
- && !scratch2.is(scratch1));
-
- // Keep track of the current object in register reg. On the first
- // iteration, reg is an alias for object_reg, on later iterations,
- // it is an alias for holder_reg.
- Register reg = object_reg;
- int depth = 0;
-
- if (save_at_depth == depth) {
- __ movq(Operand(rsp, kPointerSize), object_reg);
- }
-
- // Check the maps in the prototype chain.
- // Traverse the prototype chain from the object and do map checks.
- JSObject* current = object;
- while (current != holder) {
- depth++;
-
- // Only global objects and objects that do not require access
- // checks are allowed in stubs.
- ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded());
-
- JSObject* prototype = JSObject::cast(current->GetPrototype());
- if (!current->HasFastProperties() &&
- !current->IsJSGlobalObject() &&
- !current->IsJSGlobalProxy()) {
- if (!name->IsSymbol()) {
- MaybeObject* lookup_result = Heap::LookupSymbol(name);
- if (lookup_result->IsFailure()) {
- set_failure(Failure::cast(lookup_result));
- return reg;
- } else {
- name = String::cast(lookup_result->ToObjectUnchecked());
- }
- }
- ASSERT(current->property_dictionary()->FindEntry(name) ==
- StringDictionary::kNotFound);
-
- GenerateDictionaryNegativeLookup(masm(),
- miss,
- reg,
- name,
- scratch1,
- scratch2);
- __ movq(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
- reg = holder_reg; // from now the object is in holder_reg
- __ movq(reg, FieldOperand(scratch1, Map::kPrototypeOffset));
- } else if (Heap::InNewSpace(prototype)) {
- // Get the map of the current object.
- __ movq(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
- __ Cmp(scratch1, Handle<Map>(current->map()));
- // Branch on the result of the map check.
- __ j(not_equal, miss);
- // Check access rights to the global object. This has to happen
- // after the map check so that we know that the object is
- // actually a global object.
- if (current->IsJSGlobalProxy()) {
- __ CheckAccessGlobalProxy(reg, scratch1, miss);
-
- // Restore scratch register to be the map of the object.
- // We load the prototype from the map in the scratch register.
- __ movq(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
- }
- // The prototype is in new space; we cannot store a reference
- // to it in the code. Load it from the map.
- reg = holder_reg; // from now the object is in holder_reg
- __ movq(reg, FieldOperand(scratch1, Map::kPrototypeOffset));
-
- } else {
- // Check the map of the current object.
- __ Cmp(FieldOperand(reg, HeapObject::kMapOffset),
- Handle<Map>(current->map()));
- // Branch on the result of the map check.
- __ j(not_equal, miss);
- // Check access rights to the global object. This has to happen
- // after the map check so that we know that the object is
- // actually a global object.
- if (current->IsJSGlobalProxy()) {
- __ CheckAccessGlobalProxy(reg, scratch1, miss);
- }
- // The prototype is in old space; load it directly.
- reg = holder_reg; // from now the object is in holder_reg
- __ Move(reg, Handle<JSObject>(prototype));
- }
-
- if (save_at_depth == depth) {
- __ movq(Operand(rsp, kPointerSize), reg);
- }
-
- // Go to the next object in the prototype chain.
- current = prototype;
- }
-
- // Check the holder map.
- __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), Handle<Map>(holder->map()));
- __ j(not_equal, miss);
-
- // Log the check depth.
- LOG(IntEvent("check-maps-depth", depth + 1));
-
- // Perform security check for access to the global object and return
- // the holder register.
- ASSERT(current == holder);
- ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded());
- if (current->IsJSGlobalProxy()) {
- __ CheckAccessGlobalProxy(reg, scratch1, miss);
- }
-
- // If we've skipped any global objects, it's not enough to verify
- // that their maps haven't changed. We also need to check that the
- // property cell for the property is still empty.
- current = object;
- while (current != holder) {
- if (current->IsGlobalObject()) {
- MaybeObject* cell = GenerateCheckPropertyCell(masm(),
- GlobalObject::cast(current),
- name,
- scratch1,
- miss);
- if (cell->IsFailure()) {
- set_failure(Failure::cast(cell));
- return reg;
- }
- }
- current = JSObject::cast(current->GetPrototype());
- }
-
- // Return the register containing the holder.
- return reg;
-}
-
-
-void StubCompiler::GenerateLoadField(JSObject* object,
- JSObject* holder,
- Register receiver,
- Register scratch1,
- Register scratch2,
- Register scratch3,
- int index,
- String* name,
- Label* miss) {
- // Check that the receiver isn't a smi.
- __ JumpIfSmi(receiver, miss);
-
- // Check the prototype chain.
- Register reg =
- CheckPrototypes(object, receiver, holder,
- scratch1, scratch2, scratch3, name, miss);
-
- // Get the value from the properties.
- GenerateFastPropertyLoad(masm(), rax, reg, holder, index);
- __ ret(0);
-}
-
-
-void StubCompiler::GenerateLoadConstant(JSObject* object,
- JSObject* holder,
- Register receiver,
- Register scratch1,
- Register scratch2,
- Register scratch3,
- Object* value,
- String* name,
- Label* miss) {
- // Check that the receiver isn't a smi.
- __ JumpIfSmi(receiver, miss);
-
- // Check that the maps haven't changed.
- Register reg =
- CheckPrototypes(object, receiver, holder,
- scratch1, scratch2, scratch3, name, miss);
-
- // Return the constant value.
- __ Move(rax, Handle<Object>(value));
- __ ret(0);
-}
-
-
// Specialized stub for constructing objects from functions which only have only
// simple assignments of the form this.x = ...; in their body.
MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) {
diff --git a/deps/v8/src/zone-inl.h b/deps/v8/src/zone-inl.h
index 5893a2f80e..467296039c 100644
--- a/deps/v8/src/zone-inl.h
+++ b/deps/v8/src/zone-inl.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -47,6 +47,7 @@ inline void* Zone::New(int size) {
// Check that the result has the proper alignment and return it.
ASSERT(IsAddressAligned(result, kAlignment, 0));
+ allocation_size_ += size;
return reinterpret_cast<void*>(result);
}
diff --git a/deps/v8/src/zone.cc b/deps/v8/src/zone.cc
index 01df4504fe..f8dbaabc78 100644
--- a/deps/v8/src/zone.cc
+++ b/deps/v8/src/zone.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -38,6 +38,7 @@ Address Zone::position_ = 0;
Address Zone::limit_ = 0;
int Zone::zone_excess_limit_ = 256 * MB;
int Zone::segment_bytes_allocated_ = 0;
+unsigned Zone::allocation_size_ = 0;
bool AssertNoZoneAllocation::allow_allocation_ = true;
diff --git a/deps/v8/src/zone.h b/deps/v8/src/zone.h
index dde722f675..e299f158a8 100644
--- a/deps/v8/src/zone.h
+++ b/deps/v8/src/zone.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -71,6 +71,8 @@ class Zone {
static inline void adjust_segment_bytes_allocated(int delta);
+ static unsigned allocation_size_;
+
private:
// All pointers returned from New() have this alignment.