diff options
Diffstat (limited to 'deps/v8/src')
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, ¬_js_object); + __ IsObjectJSObjectType(object, map, scratch, ¬_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(¬_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, ¬_js_object, not_taken); __ IsObjectJSObjectType(object, map, scratch, ¬_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(¬_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, ¬_size_one_array); + __ mov(scratch, FieldOperand(elements, FixedArray::kHeaderSize)); + __ mov(result_operand, scratch); + __ jmp(&done); + + __ bind(¬_size_one_array); + + // End of array_length live range. + result_pos = array_length; + array_length = no_reg; + + // Live registers: + // string_length: Sum of string lengths, as a smi. + // elements: FixedArray of strings. + + // Check that the separator is a flat ASCII string. + __ mov(string, separator_operand); + __ test(string, Immediate(kSmiTagMask)); __ j(zero, &bailout); - __ mov(scratch, FieldOperand(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, ©_loop_1_done); - __ bind(©_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, ©_loop_1); - __ bind(©_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, ®op, &rm); + AppendToBuffer("psllq %s,%s", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; } else if (*data == 0x73) { data++; int mod, regop, rm; get_modrm(*data, &mod, ®op, &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, ®op, &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, ®op, &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, ¬_size_one_array); + __ mov(scratch, FieldOperand(elements, FixedArray::kHeaderSize)); + __ mov(result_operand, scratch); + __ jmp(&done); + + __ bind(¬_size_one_array); + + // End of array_length live range. + result_pos = array_length; + array_length = no_reg; + + // Live registers: + // string_length: Sum of string lengths, as a smi. + // elements: FixedArray of strings. + + // Check that the separator is a flat ASCII string. + __ mov(string, separator_operand); + __ test(string, Immediate(kSmiTagMask)); __ j(zero, &bailout); - __ mov(scratch, FieldOperand(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, ©_loop_1_done); - __ bind(©_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, ©_loop_1); - __ bind(©_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(©_loop_entry); - bind(©_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(©_loop_entry); - sub(Operand(current_string_length), Immediate(1)); - j(greater_equal, ©_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(¤t_time, NULL) == -1) { - return false; - } - - // Calculate time for end of timeout. - struct timeval end_time; - timeradd(¤t_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. |