diff options
Diffstat (limited to 'src/3rdparty/v8/src/x64')
22 files changed, 3316 insertions, 1768 deletions
diff --git a/src/3rdparty/v8/src/x64/assembler-x64-inl.h b/src/3rdparty/v8/src/x64/assembler-x64-inl.h index a9cc2ef..f864174 100644 --- a/src/3rdparty/v8/src/x64/assembler-x64-inl.h +++ b/src/3rdparty/v8/src/x64/assembler-x64-inl.h @@ -42,6 +42,9 @@ namespace internal { // Implementation of Assembler +static const byte kCallOpcode = 0xE8; + + void Assembler::emitl(uint32_t x) { Memory::uint32_at(pc_) = x; pc_ += sizeof(uint32_t); @@ -65,10 +68,10 @@ void Assembler::emitw(uint16_t x) { void Assembler::emit_code_target(Handle<Code> target, RelocInfo::Mode rmode, - unsigned ast_id) { + TypeFeedbackId ast_id) { ASSERT(RelocInfo::IsCodeTarget(rmode)); - if (rmode == RelocInfo::CODE_TARGET && ast_id != kNoASTId) { - RecordRelocInfo(RelocInfo::CODE_TARGET_WITH_ID, ast_id); + if (rmode == RelocInfo::CODE_TARGET && !ast_id.IsNone()) { + RecordRelocInfo(RelocInfo::CODE_TARGET_WITH_ID, ast_id.ToInt()); } else { RecordRelocInfo(rmode); } @@ -195,6 +198,12 @@ void Assembler::set_target_address_at(Address pc, Address target) { CPU::FlushICache(pc, sizeof(int32_t)); } + +Address Assembler::target_address_from_return_address(Address pc) { + return pc - kCallTargetAddressOffset; +} + + Handle<Object> Assembler::code_target_object_handle_at(Address pc) { return code_targets_[Memory::int32_at(pc)]; } @@ -211,6 +220,12 @@ void RelocInfo::apply(intptr_t delta) { } else if (IsCodeTarget(rmode_)) { Memory::int32_at(pc_) -= static_cast<int32_t>(delta); CPU::FlushICache(pc_, sizeof(int32_t)); + } else if (rmode_ == CODE_AGE_SEQUENCE) { + if (*pc_ == kCallOpcode) { + int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1); + *p -= static_cast<int32_t>(delta); // Relocate entry. + CPU::FlushICache(p, sizeof(uint32_t)); + } } } @@ -309,10 +324,7 @@ Handle<JSGlobalPropertyCell> RelocInfo::target_cell_handle() { JSGlobalPropertyCell* RelocInfo::target_cell() { ASSERT(rmode_ == RelocInfo::GLOBAL_PROPERTY_CELL); - Address address = Memory::Address_at(pc_); - Object* object = HeapObject::FromAddress( - address - JSGlobalPropertyCell::kValueOffset); - return reinterpret_cast<JSGlobalPropertyCell*>(object); + return JSGlobalPropertyCell::FromValueAddress(Memory::Address_at(pc_)); } @@ -352,6 +364,21 @@ bool RelocInfo::IsPatchedDebugBreakSlotSequence() { } +Code* RelocInfo::code_age_stub() { + ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); + ASSERT(*pc_ == kCallOpcode); + return Code::GetCodeFromTargetAddress( + Assembler::target_address_at(pc_ + 1)); +} + + +void RelocInfo::set_code_age_stub(Code* stub) { + ASSERT(*pc_ == kCallOpcode); + ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); + Assembler::set_target_address_at(pc_ + 1, stub->instruction_start()); +} + + Address RelocInfo::call_address() { ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) || (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence())); @@ -405,6 +432,8 @@ void RelocInfo::Visit(ObjectVisitor* visitor) { } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { visitor->VisitExternalReference(this); CPU::FlushICache(pc_, sizeof(Address)); + } else if (RelocInfo::IsCodeAgeSequence(mode)) { + visitor->VisitCodeAgeSequence(this); #ifdef ENABLE_DEBUGGER_SUPPORT // TODO(isolates): Get a cached isolate below. } else if (((RelocInfo::IsJSReturn(mode) && @@ -433,6 +462,8 @@ void RelocInfo::Visit(Heap* heap) { } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { StaticVisitor::VisitExternalReference(this); CPU::FlushICache(pc_, sizeof(Address)); + } else if (RelocInfo::IsCodeAgeSequence(mode)) { + StaticVisitor::VisitCodeAgeSequence(heap, this); #ifdef ENABLE_DEBUGGER_SUPPORT } else if (heap->isolate()->debug()->has_break_points() && ((RelocInfo::IsJSReturn(mode) && diff --git a/src/3rdparty/v8/src/x64/assembler-x64.cc b/src/3rdparty/v8/src/x64/assembler-x64.cc index 2f0c542..1f5bea9 100644 --- a/src/3rdparty/v8/src/x64/assembler-x64.cc +++ b/src/3rdparty/v8/src/x64/assembler-x64.cc @@ -75,6 +75,7 @@ void CpuFeatures::Probe() { // Save old rsp, since we are going to modify the stack. __ push(rbp); __ pushfq(); + __ push(rdi); __ push(rcx); __ push(rbx); __ movq(rbp, rsp); @@ -128,6 +129,7 @@ void CpuFeatures::Probe() { __ movq(rsp, rbp); __ pop(rbx); __ pop(rcx); + __ pop(rdi); __ popfq(); __ pop(rbp); __ ret(0); @@ -347,8 +349,7 @@ static void InitCoverageLog(); Assembler::Assembler(Isolate* arg_isolate, void* buffer, int buffer_size) : AssemblerBase(arg_isolate), code_targets_(100), - positions_recorder_(this), - emit_debug_code_(FLAG_debug_code) { + positions_recorder_(this) { if (buffer == NULL) { // Do our own buffer management. if (buffer_size <= kMinimalBufferSize) { @@ -467,7 +468,7 @@ void Assembler::bind_to(Label* L, int pos) { static_cast<int>(*reinterpret_cast<int8_t*>(addr_at(fixup_pos))); ASSERT(offset_to_next <= 0); int disp = pos - (fixup_pos + sizeof(int8_t)); - ASSERT(is_int8(disp)); + CHECK(is_int8(disp)); set_byte_at(fixup_pos, disp); if (offset_to_next < 0) { L->link_to(fixup_pos + offset_to_next, Label::kNear); @@ -875,7 +876,7 @@ void Assembler::call(Label* L) { void Assembler::call(Handle<Code> target, RelocInfo::Mode rmode, - unsigned ast_id) { + TypeFeedbackId ast_id) { positions_recorder()->WriteRecordedPositions(); EnsureSpace ensure_space(this); // 1110 1000 #32-bit disp. @@ -1232,7 +1233,16 @@ void Assembler::j(Condition cc, Label* L, Label::Distance distance) { const int long_size = 6; int offs = L->pos() - pc_offset(); ASSERT(offs <= 0); - if (is_int8(offs - short_size)) { + // Determine whether we can use 1-byte offsets for backwards branches, + // which have a max range of 128 bytes. + + // We also need to check predictable_code_size() flag here, because on x64, + // when the full code generator recompiles code for debugging, some places + // need to be padded out to a certain size. The debugger is keeping track of + // how often it did this so that it can adjust return addresses on the + // stack, but if the size of jump instructions can also change, that's not + // enough and the calculated offsets would be incorrect. + if (is_int8(offs - short_size) && !predictable_code_size()) { // 0111 tttn #8-bit disp. emit(0x70 | cc); emit((offs - short_size) & 0xFF); @@ -1289,7 +1299,7 @@ void Assembler::jmp(Label* L, Label::Distance distance) { if (L->is_bound()) { int offs = L->pos() - pc_offset() - 1; ASSERT(offs <= 0); - if (is_int8(offs - short_size)) { + if (is_int8(offs - short_size) && !predictable_code_size()) { // 1110 1011 #8-bit disp. emit(0xEB); emit((offs - short_size) & 0xFF); @@ -3035,7 +3045,8 @@ void Assembler::RecordComment(const char* msg, bool force) { const int RelocInfo::kApplyMask = RelocInfo::kCodeTargetMask | - 1 << RelocInfo::INTERNAL_REFERENCE; + 1 << RelocInfo::INTERNAL_REFERENCE | + 1 << RelocInfo::CODE_AGE_SEQUENCE; bool RelocInfo::IsCodedSpecially() { diff --git a/src/3rdparty/v8/src/x64/assembler-x64.h b/src/3rdparty/v8/src/x64/assembler-x64.h index 9f5f850..5f9e147 100644 --- a/src/3rdparty/v8/src/x64/assembler-x64.h +++ b/src/3rdparty/v8/src/x64/assembler-x64.h @@ -455,6 +455,7 @@ class CpuFeatures : public AllStatic { ASSERT(initialized_); if (f == SSE2 && !FLAG_enable_sse2) return false; if (f == SSE3 && !FLAG_enable_sse3) return false; + if (f == SSE4_1 && !FLAG_enable_sse4_1) return false; if (f == CMOV && !FLAG_enable_cmov) return false; if (f == RDTSC && !FLAG_enable_rdtsc) return false; if (f == SAHF && !FLAG_enable_sahf) return false; @@ -557,9 +558,6 @@ class Assembler : public AssemblerBase { Assembler(Isolate* isolate, void* buffer, int buffer_size); ~Assembler(); - // Overrides the default provided by FLAG_debug_code. - void set_emit_debug_code(bool value) { emit_debug_code_ = value; } - // GetCode emits any pending (non-emitted) code and fills the descriptor // desc. GetCode() is idempotent; it returns the same result if no other // Assembler functions are invoked in between GetCode() calls. @@ -575,6 +573,10 @@ class Assembler : public AssemblerBase { static inline Address target_address_at(Address pc); static inline void set_target_address_at(Address pc, Address target); + // Return the code target address at a call site from the return address + // of that call in the instruction stream. + static inline Address target_address_from_return_address(Address pc); + // This sets the branch destination (which is in the instruction on x64). // This is for calls and branches within generated code. inline static void deserialization_set_special_target_at( @@ -614,6 +616,7 @@ class Assembler : public AssemblerBase { static const int kCallInstructionLength = 13; static const int kJSReturnSequenceLength = 13; static const int kShortCallInstructionLength = 5; + static const int kPatchDebugBreakSlotReturnOffset = 4; // The debug break slot must be able to contain a call instruction. static const int kDebugBreakSlotLength = kCallInstructionLength; @@ -1010,6 +1013,14 @@ class Assembler : public AssemblerBase { shift(dst, imm8, 0x1); } + void rorl(Register dst, Immediate imm8) { + shift_32(dst, imm8, 0x1); + } + + void rorl_cl(Register dst) { + shift_32(dst, 0x1); + } + // Shifts dst:src left by cl bits, affecting only dst. void shld(Register dst, Register src); @@ -1201,7 +1212,7 @@ class Assembler : public AssemblerBase { void call(Label* L); void call(Handle<Code> target, RelocInfo::Mode rmode = RelocInfo::CODE_TARGET, - unsigned ast_id = kNoASTId); + TypeFeedbackId ast_id = TypeFeedbackId::None()); // Calls directly to the given address using a relative offset. // Should only ever be used in Code objects for calls within the @@ -1430,9 +1441,6 @@ class Assembler : public AssemblerBase { byte byte_at(int pos) { return buffer_[pos]; } void set_byte_at(int pos, byte value) { buffer_[pos] = value; } - protected: - bool emit_debug_code() const { return emit_debug_code_; } - private: byte* addr_at(int pos) { return buffer_ + pos; } uint32_t long_at(int pos) { @@ -1451,7 +1459,7 @@ class Assembler : public AssemblerBase { inline void emitw(uint16_t x); inline void emit_code_target(Handle<Code> target, RelocInfo::Mode rmode, - unsigned ast_id = kNoASTId); + TypeFeedbackId ast_id = TypeFeedbackId::None()); void emit(Immediate x) { emitl(x.value_); } // Emits a REX prefix that encodes a 64-bit operand size and @@ -1634,9 +1642,6 @@ class Assembler : public AssemblerBase { List< Handle<Code> > code_targets_; PositionsRecorder positions_recorder_; - - bool emit_debug_code_; - friend class PositionsRecorder; }; diff --git a/src/3rdparty/v8/src/x64/builtins-x64.cc b/src/3rdparty/v8/src/x64/builtins-x64.cc index 4e037ff..ed0ec68 100644 --- a/src/3rdparty/v8/src/x64/builtins-x64.cc +++ b/src/3rdparty/v8/src/x64/builtins-x64.cc @@ -73,6 +73,45 @@ void Builtins::Generate_Adaptor(MacroAssembler* masm, } +static void GenerateTailCallToSharedCode(MacroAssembler* masm) { + __ movq(kScratchRegister, + FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); + __ movq(kScratchRegister, + FieldOperand(kScratchRegister, SharedFunctionInfo::kCodeOffset)); + __ lea(kScratchRegister, FieldOperand(kScratchRegister, Code::kHeaderSize)); + __ jmp(kScratchRegister); +} + + +void Builtins::Generate_InRecompileQueue(MacroAssembler* masm) { + GenerateTailCallToSharedCode(masm); +} + + +void Builtins::Generate_ParallelRecompile(MacroAssembler* masm) { + { + FrameScope scope(masm, StackFrame::INTERNAL); + + // Push a copy of the function onto the stack. + __ push(rdi); + // Push call kind information. + __ push(rcx); + + __ push(rdi); // Function is also the parameter to the runtime call. + __ CallRuntime(Runtime::kParallelRecompile, 1); + + // Restore call kind information. + __ pop(rcx); + // Restore receiver. + __ pop(rdi); + + // Tear down internal frame. + } + + GenerateTailCallToSharedCode(masm); +} + + static void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function, bool count_constructions) { @@ -567,6 +606,46 @@ void Builtins::Generate_LazyRecompile(MacroAssembler* masm) { } +static void GenerateMakeCodeYoungAgainCommon(MacroAssembler* masm) { + // For now, we are relying on the fact that make_code_young doesn't do any + // garbage collection which allows us to save/restore the registers without + // worrying about which of them contain pointers. We also don't build an + // internal frame to make the code faster, since we shouldn't have to do stack + // crawls in MakeCodeYoung. This seems a bit fragile. + + // Re-execute the code that was patched back to the young age when + // the stub returns. + __ subq(Operand(rsp, 0), Immediate(5)); + __ Pushad(); +#ifdef _WIN64 + __ movq(rcx, Operand(rsp, kNumSafepointRegisters * kPointerSize)); +#else + __ movq(rdi, Operand(rsp, kNumSafepointRegisters * kPointerSize)); +#endif + { // NOLINT + FrameScope scope(masm, StackFrame::MANUAL); + __ PrepareCallCFunction(1); + __ CallCFunction( + ExternalReference::get_make_code_young_function(masm->isolate()), 1); + } + __ Popad(); + __ ret(0); +} + + +#define DEFINE_CODE_AGE_BUILTIN_GENERATOR(C) \ +void Builtins::Generate_Make##C##CodeYoungAgainEvenMarking( \ + MacroAssembler* masm) { \ + GenerateMakeCodeYoungAgainCommon(masm); \ +} \ +void Builtins::Generate_Make##C##CodeYoungAgainOddMarking( \ + MacroAssembler* masm) { \ + GenerateMakeCodeYoungAgainCommon(masm); \ +} +CODE_AGE_LIST(DEFINE_CODE_AGE_BUILTIN_GENERATOR) +#undef DEFINE_CODE_AGE_BUILTIN_GENERATOR + + static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm, Deoptimizer::BailoutType type) { // Enter an internal frame. @@ -711,9 +790,9 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { // receiver. __ bind(&use_global_receiver); const int kGlobalIndex = - Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; + Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize; __ movq(rbx, FieldOperand(rsi, kGlobalIndex)); - __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalContextOffset)); + __ movq(rbx, FieldOperand(rbx, GlobalObject::kNativeContextOffset)); __ movq(rbx, FieldOperand(rbx, kGlobalIndex)); __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); @@ -896,9 +975,9 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { // Use the current global receiver object as the receiver. __ bind(&use_global_receiver); const int kGlobalOffset = - Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; + Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize; __ movq(rbx, FieldOperand(rsi, kGlobalOffset)); - __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalContextOffset)); + __ movq(rbx, FieldOperand(rbx, GlobalObject::kNativeContextOffset)); __ movq(rbx, FieldOperand(rbx, kGlobalOffset)); __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); @@ -977,7 +1056,7 @@ static void AllocateEmptyJSArray(MacroAssembler* masm, const int initial_capacity = JSArray::kPreallocatedArrayElements; STATIC_ASSERT(initial_capacity >= 0); - __ LoadInitialArrayMap(array_function, scratch2, scratch1); + __ LoadInitialArrayMap(array_function, scratch2, scratch1, false); // Allocate the JSArray object together with space for a fixed array with the // requested elements. @@ -1076,7 +1155,8 @@ static void AllocateJSArray(MacroAssembler* masm, Register scratch, bool fill_with_hole, Label* gc_required) { - __ LoadInitialArrayMap(array_function, scratch, elements_array); + __ LoadInitialArrayMap(array_function, scratch, + elements_array, fill_with_hole); if (FLAG_debug_code) { // Assert that array size is not zero. __ testq(array_size, array_size); @@ -1303,10 +1383,10 @@ static void ArrayNativeCode(MacroAssembler* masm, __ jmp(call_generic_code); __ bind(¬_double); - // Transition FAST_SMI_ONLY_ELEMENTS to FAST_ELEMENTS. + // Transition FAST_SMI_ELEMENTS to FAST_ELEMENTS. // rbx: JSArray __ movq(r11, FieldOperand(rbx, HeapObject::kMapOffset)); - __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS, r11, kScratchRegister, diff --git a/src/3rdparty/v8/src/x64/code-stubs-x64.cc b/src/3rdparty/v8/src/x64/code-stubs-x64.cc index 7069829..06ce52a 100644 --- a/src/3rdparty/v8/src/x64/code-stubs-x64.cc +++ b/src/3rdparty/v8/src/x64/code-stubs-x64.cc @@ -62,9 +62,13 @@ void ToNumberStub::Generate(MacroAssembler* masm) { void FastNewClosureStub::Generate(MacroAssembler* masm) { // Create a new closure from the given function info in new // space. Set the context to the current context in rsi. + Counters* counters = masm->isolate()->counters(); + Label gc; __ AllocateInNewSpace(JSFunction::kSize, rax, rbx, rcx, &gc, TAG_OBJECT); + __ IncrementCounter(counters->fast_new_closure_total(), 1); + // Get the function info from the stack. __ movq(rdx, Operand(rsp, 1 * kPointerSize)); @@ -72,36 +76,113 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { ? Context::FUNCTION_MAP_INDEX : Context::STRICT_MODE_FUNCTION_MAP_INDEX; - // Compute the function map in the current global context and set that + // Compute the function map in the current native context and set that // as the map of the allocated object. - __ movq(rcx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); - __ movq(rcx, FieldOperand(rcx, GlobalObject::kGlobalContextOffset)); - __ movq(rcx, Operand(rcx, Context::SlotOffset(map_index))); - __ movq(FieldOperand(rax, JSObject::kMapOffset), rcx); + __ movq(rcx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX))); + __ movq(rcx, FieldOperand(rcx, GlobalObject::kNativeContextOffset)); + __ movq(rbx, Operand(rcx, Context::SlotOffset(map_index))); + __ movq(FieldOperand(rax, JSObject::kMapOffset), rbx); // Initialize the rest of the function. We don't have to update the // write barrier because the allocated object is in new space. __ LoadRoot(rbx, Heap::kEmptyFixedArrayRootIndex); - __ LoadRoot(rcx, Heap::kTheHoleValueRootIndex); + __ LoadRoot(r8, Heap::kTheHoleValueRootIndex); __ LoadRoot(rdi, Heap::kUndefinedValueRootIndex); __ movq(FieldOperand(rax, JSObject::kPropertiesOffset), rbx); __ movq(FieldOperand(rax, JSObject::kElementsOffset), rbx); - __ movq(FieldOperand(rax, JSFunction::kPrototypeOrInitialMapOffset), rcx); + __ movq(FieldOperand(rax, JSFunction::kPrototypeOrInitialMapOffset), r8); __ movq(FieldOperand(rax, JSFunction::kSharedFunctionInfoOffset), rdx); __ movq(FieldOperand(rax, JSFunction::kContextOffset), rsi); __ movq(FieldOperand(rax, JSFunction::kLiteralsOffset), rbx); - __ movq(FieldOperand(rax, JSFunction::kNextFunctionLinkOffset), rdi); // Initialize the code pointer in the function to be the one // found in the shared function info object. + // But first check if there is an optimized version for our context. + Label check_optimized; + Label install_unoptimized; + if (FLAG_cache_optimized_code) { + __ movq(rbx, + FieldOperand(rdx, SharedFunctionInfo::kOptimizedCodeMapOffset)); + __ testq(rbx, rbx); + __ j(not_zero, &check_optimized, Label::kNear); + } + __ bind(&install_unoptimized); + __ movq(FieldOperand(rax, JSFunction::kNextFunctionLinkOffset), + rdi); // Initialize with undefined. __ movq(rdx, FieldOperand(rdx, SharedFunctionInfo::kCodeOffset)); __ lea(rdx, FieldOperand(rdx, Code::kHeaderSize)); __ movq(FieldOperand(rax, JSFunction::kCodeEntryOffset), rdx); + // Return and remove the on-stack parameter. + __ ret(1 * kPointerSize); + + __ bind(&check_optimized); + + __ IncrementCounter(counters->fast_new_closure_try_optimized(), 1); + + // rcx holds native context, ebx points to fixed array of 3-element entries + // (native context, optimized code, literals). + // The optimized code map must never be empty, so check the first elements. + Label install_optimized; + // Speculatively move code object into edx. + __ movq(rdx, FieldOperand(rbx, FixedArray::kHeaderSize + kPointerSize)); + __ cmpq(rcx, FieldOperand(rbx, FixedArray::kHeaderSize)); + __ j(equal, &install_optimized); + + // Iterate through the rest of map backwards. rdx holds an index. + Label loop; + Label restore; + __ movq(rdx, FieldOperand(rbx, FixedArray::kLengthOffset)); + __ SmiToInteger32(rdx, rdx); + __ bind(&loop); + // Do not double check first entry. + __ cmpq(rdx, Immediate(SharedFunctionInfo::kEntryLength)); + __ j(equal, &restore); + __ subq(rdx, Immediate(SharedFunctionInfo::kEntryLength)); // Skip an entry. + __ cmpq(rcx, FieldOperand(rbx, + rdx, + times_pointer_size, + FixedArray::kHeaderSize)); + __ j(not_equal, &loop, Label::kNear); + // Hit: fetch the optimized code. + __ movq(rdx, FieldOperand(rbx, + rdx, + times_pointer_size, + FixedArray::kHeaderSize + 1 * kPointerSize)); + + __ bind(&install_optimized); + __ IncrementCounter(counters->fast_new_closure_install_optimized(), 1); + + // TODO(fschneider): Idea: store proper code pointers in the map and either + // unmangle them on marking or do nothing as the whole map is discarded on + // major GC anyway. + __ lea(rdx, FieldOperand(rdx, Code::kHeaderSize)); + __ movq(FieldOperand(rax, JSFunction::kCodeEntryOffset), rdx); + + // Now link a function into a list of optimized functions. + __ movq(rdx, ContextOperand(rcx, Context::OPTIMIZED_FUNCTIONS_LIST)); + + __ movq(FieldOperand(rax, JSFunction::kNextFunctionLinkOffset), rdx); + // No need for write barrier as JSFunction (rax) is in the new space. + + __ movq(ContextOperand(rcx, Context::OPTIMIZED_FUNCTIONS_LIST), rax); + // Store JSFunction (rax) into rdx before issuing write barrier as + // it clobbers all the registers passed. + __ movq(rdx, rax); + __ RecordWriteContextSlot( + rcx, + Context::SlotOffset(Context::OPTIMIZED_FUNCTIONS_LIST), + rdx, + rbx, + kDontSaveFPRegs); // Return and remove the on-stack parameter. __ ret(1 * kPointerSize); + __ bind(&restore); + __ movq(rdx, Operand(rsp, 1 * kPointerSize)); + __ jmp(&install_unoptimized); + // Create a new closure through the slower runtime call. __ bind(&gc); __ pop(rcx); // Temporarily remove return address. @@ -136,12 +217,12 @@ void FastNewContextStub::Generate(MacroAssembler* masm) { __ movq(Operand(rax, Context::SlotOffset(Context::EXTENSION_INDEX)), rbx); // Copy the global object from the previous context. - __ movq(rbx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); - __ movq(Operand(rax, Context::SlotOffset(Context::GLOBAL_INDEX)), rbx); + __ movq(rbx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX))); + __ movq(Operand(rax, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)), rbx); // Copy the qmlglobal object from the previous context. - __ movq(rbx, Operand(rsi, Context::SlotOffset(Context::QML_GLOBAL_INDEX))); - __ movq(Operand(rax, Context::SlotOffset(Context::QML_GLOBAL_INDEX)), rbx); + __ movq(rbx, Operand(rsi, Context::SlotOffset(Context::QML_GLOBAL_OBJECT_INDEX))); + __ movq(Operand(rax, Context::SlotOffset(Context::QML_GLOBAL_OBJECT_INDEX)), rbx); // Initialize the rest of the slots to undefined. __ LoadRoot(rbx, Heap::kUndefinedValueRootIndex); @@ -182,9 +263,9 @@ void FastNewBlockContextStub::Generate(MacroAssembler* masm) { __ movq(FieldOperand(rax, HeapObject::kMapOffset), kScratchRegister); __ Move(FieldOperand(rax, FixedArray::kLengthOffset), Smi::FromInt(length)); - // If this block context is nested in the global context we get a smi + // If this block context is nested in the native context we get a smi // sentinel instead of a function. The block context should get the - // canonical empty function of the global context as its closure which + // canonical empty function of the native context as its closure which // we still have to look up. Label after_sentinel; __ JumpIfNotSmi(rcx, &after_sentinel, Label::kNear); @@ -194,7 +275,7 @@ void FastNewBlockContextStub::Generate(MacroAssembler* masm) { __ Assert(equal, message); } __ movq(rcx, GlobalObjectOperand()); - __ movq(rcx, FieldOperand(rcx, GlobalObject::kGlobalContextOffset)); + __ movq(rcx, FieldOperand(rcx, GlobalObject::kNativeContextOffset)); __ movq(rcx, ContextOperand(rcx, Context::CLOSURE_INDEX)); __ bind(&after_sentinel); @@ -204,12 +285,12 @@ void FastNewBlockContextStub::Generate(MacroAssembler* masm) { __ movq(ContextOperand(rax, Context::EXTENSION_INDEX), rbx); // Copy the global object from the previous context. - __ movq(rbx, ContextOperand(rsi, Context::GLOBAL_INDEX)); - __ movq(ContextOperand(rax, Context::GLOBAL_INDEX), rbx); + __ movq(rbx, ContextOperand(rsi, Context::GLOBAL_OBJECT_INDEX)); + __ movq(ContextOperand(rax, Context::GLOBAL_OBJECT_INDEX), rbx); // Copy the qmlglobal object from the previous context. - __ movq(rbx, ContextOperand(rsi, Context::QML_GLOBAL_INDEX)); - __ movq(ContextOperand(rax, Context::QML_GLOBAL_INDEX), rbx); + __ movq(rbx, ContextOperand(rsi, Context::QML_GLOBAL_OBJECT_INDEX)); + __ movq(ContextOperand(rax, Context::QML_GLOBAL_OBJECT_INDEX), rbx); // Initialize the rest of the slots to the hole value. __ LoadRoot(rbx, Heap::kTheHoleValueRootIndex); @@ -1007,8 +1088,8 @@ void BinaryOpStub::GenerateSmiCode( SmiCodeGenerateHeapNumberResults allow_heapnumber_results) { // Arguments to BinaryOpStub are in rdx and rax. - Register left = rdx; - Register right = rax; + const Register left = rdx; + const Register right = rax; // We only generate heapnumber answers for overflowing calculations // for the four basic arithmetic operations and logical right shift by 0. @@ -1050,20 +1131,16 @@ void BinaryOpStub::GenerateSmiCode( case Token::DIV: // SmiDiv will not accept left in rdx or right in rax. - left = rcx; - right = rbx; __ movq(rbx, rax); __ movq(rcx, rdx); - __ SmiDiv(rax, left, right, &use_fp_on_smis); + __ SmiDiv(rax, rcx, rbx, &use_fp_on_smis); break; case Token::MOD: // SmiMod will not accept left in rdx or right in rax. - left = rcx; - right = rbx; __ movq(rbx, rax); __ movq(rcx, rdx); - __ SmiMod(rax, left, right, &use_fp_on_smis); + __ SmiMod(rax, rcx, rbx, &use_fp_on_smis); break; case Token::BIT_OR: { @@ -1228,11 +1305,9 @@ void BinaryOpStub::GenerateFloatingPointCode(MacroAssembler* masm, &allocation_failed, TAG_OBJECT); // Set the map. - if (FLAG_debug_code) { - __ AbortIfNotRootValue(heap_number_map, - Heap::kHeapNumberMapRootIndex, - "HeapNumberMap register clobbered."); - } + __ AssertRootValue(heap_number_map, + Heap::kHeapNumberMapRootIndex, + "HeapNumberMap register clobbered."); __ movq(FieldOperand(rax, HeapObject::kMapOffset), heap_number_map); __ cvtqsi2sd(xmm0, rbx); @@ -1980,10 +2055,7 @@ void FloatingPointHelper::NumbersToSmis(MacroAssembler* masm, __ JumpIfSmi(second, (on_success != NULL) ? on_success : &done); __ bind(&first_smi); - if (FLAG_debug_code) { - // Second should be non-smi if we get here. - __ AbortIfSmi(second); - } + __ AssertNotSmi(second); __ cmpq(FieldOperand(second, HeapObject::kMapOffset), heap_number_map); __ j(not_equal, on_not_smis); // Convert second to smi, if possible. @@ -2193,21 +2265,28 @@ void MathPowStub::Generate(MacroAssembler* masm) { __ movsd(double_scratch2, double_result); // Load double_exponent with 1. // Get absolute value of exponent. - Label no_neg, while_true, no_multiply; + Label no_neg, while_true, while_false; __ testl(scratch, scratch); __ j(positive, &no_neg, Label::kNear); __ negl(scratch); __ bind(&no_neg); - __ bind(&while_true); + __ j(zero, &while_false, Label::kNear); __ shrl(scratch, Immediate(1)); - __ j(not_carry, &no_multiply, Label::kNear); - __ mulsd(double_result, double_scratch); - __ bind(&no_multiply); + // Above condition means CF==0 && ZF==0. This means that the + // bit that has been shifted out is 0 and the result is not 0. + __ j(above, &while_true, Label::kNear); + __ movsd(double_result, double_scratch); + __ j(zero, &while_false, Label::kNear); + __ bind(&while_true); + __ shrl(scratch, Immediate(1)); __ mulsd(double_scratch, double_scratch); + __ j(above, &while_true, Label::kNear); + __ mulsd(double_result, double_scratch); __ j(not_zero, &while_true); + __ bind(&while_false); // If the exponent is negative, return 1/result. __ testl(exponent, exponent); __ j(greater, &done); @@ -2387,10 +2466,10 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { // rax = address of new object(s) (tagged) // rcx = argument count (untagged) - // Get the arguments boilerplate from the current (global) context into rdi. + // Get the arguments boilerplate from the current native context into rdi. Label has_mapped_parameters, copy; - __ movq(rdi, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); - __ movq(rdi, FieldOperand(rdi, GlobalObject::kGlobalContextOffset)); + __ movq(rdi, Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX))); + __ movq(rdi, FieldOperand(rdi, GlobalObject::kNativeContextOffset)); __ testq(rbx, rbx); __ j(not_zero, &has_mapped_parameters, Label::kNear); @@ -2533,7 +2612,7 @@ void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { __ bind(&runtime); __ Integer32ToSmi(rcx, rcx); __ movq(Operand(rsp, 1 * kPointerSize), rcx); // Patch argument count. - __ TailCallRuntime(Runtime::kNewStrictArgumentsFast, 3, 1); + __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1); } @@ -2603,9 +2682,9 @@ void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) { // Do the allocation of both objects in one go. __ AllocateInNewSpace(rcx, rax, rdx, rbx, &runtime, TAG_OBJECT); - // Get the arguments boilerplate from the current (global) context. - __ movq(rdi, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); - __ movq(rdi, FieldOperand(rdi, GlobalObject::kGlobalContextOffset)); + // Get the arguments boilerplate from the current native context. + __ movq(rdi, Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX))); + __ movq(rdi, FieldOperand(rdi, GlobalObject::kNativeContextOffset)); const int offset = Context::SlotOffset(Context::STRICT_MODE_ARGUMENTS_BOILERPLATE_INDEX); __ movq(rdi, Operand(rdi, offset)); @@ -2722,7 +2801,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // Calculate number of capture registers (number_of_captures + 1) * 2. __ leal(rdx, Operand(rdx, rdx, times_1, 2)); // Check that the static offsets vector buffer is large enough. - __ cmpl(rdx, Immediate(OffsetsVector::kStaticOffsetsVectorSize)); + __ cmpl(rdx, Immediate(Isolate::kJSRegexpStaticOffsetsVectorSize)); __ j(above, &runtime); // rax: RegExp data (FixedArray) @@ -2872,30 +2951,37 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ IncrementCounter(counters->regexp_entry_native(), 1); // Isolates: note we add an additional parameter here (isolate pointer). - static const int kRegExpExecuteArguments = 8; + static const int kRegExpExecuteArguments = 9; int argument_slots_on_stack = masm->ArgumentStackSlotsForCFunctionCall(kRegExpExecuteArguments); __ EnterApiExitFrame(argument_slots_on_stack); - // Argument 8: Pass current isolate address. + // Argument 9: Pass current isolate address. // __ movq(Operand(rsp, (argument_slots_on_stack - 1) * kPointerSize), // Immediate(ExternalReference::isolate_address())); __ LoadAddress(kScratchRegister, ExternalReference::isolate_address()); __ movq(Operand(rsp, (argument_slots_on_stack - 1) * kPointerSize), kScratchRegister); - // Argument 7: Indicate that this is a direct call from JavaScript. + // Argument 8: Indicate that this is a direct call from JavaScript. __ movq(Operand(rsp, (argument_slots_on_stack - 2) * kPointerSize), Immediate(1)); - // Argument 6: Start (high end) of backtracking stack memory area. + // Argument 7: Start (high end) of backtracking stack memory area. __ movq(kScratchRegister, address_of_regexp_stack_memory_address); __ movq(r9, Operand(kScratchRegister, 0)); __ movq(kScratchRegister, address_of_regexp_stack_memory_size); __ addq(r9, Operand(kScratchRegister, 0)); - // Argument 6 passed in r9 on Linux and on the stack on Windows. -#ifdef _WIN64 __ movq(Operand(rsp, (argument_slots_on_stack - 3) * kPointerSize), r9); + + // Argument 6: Set the number of capture registers to zero to force global + // regexps to behave as non-global. This does not affect non-global regexps. + // Argument 6 is passed in r9 on Linux and on the stack on Windows. +#ifdef _WIN64 + __ movq(Operand(rsp, (argument_slots_on_stack - 4) * kPointerSize), + Immediate(0)); +#else + __ Set(r9, 0); #endif // Argument 5: static offsets vector buffer. @@ -2903,7 +2989,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { ExternalReference::address_of_static_offsets_vector(isolate)); // Argument 5 passed in r8 on Linux and on the stack on Windows. #ifdef _WIN64 - __ movq(Operand(rsp, (argument_slots_on_stack - 4) * kPointerSize), r8); + __ movq(Operand(rsp, (argument_slots_on_stack - 5) * kPointerSize), r8); #endif // First four arguments are passed in registers on both Linux and Windows. @@ -2968,7 +3054,9 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // Check the result. Label success; Label exception; - __ cmpl(rax, Immediate(NativeRegExpMacroAssembler::SUCCESS)); + __ cmpl(rax, Immediate(1)); + // We expect exactly one result since we force the called regexp to behave + // as non-global. __ j(equal, &success, Label::kNear); __ cmpl(rax, Immediate(NativeRegExpMacroAssembler::EXCEPTION)); __ j(equal, &exception); @@ -3125,8 +3213,8 @@ void RegExpConstructResultStub::Generate(MacroAssembler* masm) { // r8: Number of array elements as smi. // Set JSArray map to global.regexp_result_map(). - __ movq(rdx, ContextOperand(rsi, Context::GLOBAL_INDEX)); - __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalContextOffset)); + __ movq(rdx, ContextOperand(rsi, Context::GLOBAL_OBJECT_INDEX)); + __ movq(rdx, FieldOperand(rdx, GlobalObject::kNativeContextOffset)); __ movq(rdx, ContextOperand(rdx, Context::REGEXP_RESULT_MAP_INDEX)); __ movq(FieldOperand(rax, HeapObject::kMapOffset), rdx); @@ -3157,14 +3245,14 @@ void RegExpConstructResultStub::Generate(MacroAssembler* masm) { // Set length. __ Integer32ToSmi(rdx, rbx); __ movq(FieldOperand(rcx, FixedArray::kLengthOffset), rdx); - // Fill contents of fixed-array with the-hole. - __ LoadRoot(rdx, Heap::kTheHoleValueRootIndex); + // Fill contents of fixed-array with undefined. + __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex); __ lea(rcx, FieldOperand(rcx, FixedArray::kHeaderSize)); - // Fill fixed array elements with hole. + // Fill fixed array elements with undefined. // rax: JSArray. // rbx: Number of elements in array that remains to be filled, as int32. // rcx: Start of elements in FixedArray. - // rdx: the hole. + // rdx: undefined. Label loop; __ testl(rbx, rbx); __ bind(&loop); @@ -3349,13 +3437,13 @@ void CompareStub::Generate(MacroAssembler* masm) { __ jmp(¬_user_equal); __ bind(&user_equal); - + __ pop(rbx); // Return address. __ push(rax); __ push(rdx); __ push(rbx); __ TailCallRuntime(Runtime::kUserObjectEquals, 2, 1); - + __ bind(¬_user_equal); } @@ -4691,7 +4779,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { Label non_ascii, allocated, ascii_data; __ movl(rcx, r8); __ and_(rcx, r9); - STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0); STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); __ testl(rcx, Immediate(kStringEncodingMask)); __ j(zero, &non_ascii); @@ -4717,9 +4805,9 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ testb(rcx, Immediate(kAsciiDataHintMask)); __ j(not_zero, &ascii_data); __ xor_(r8, r9); - STATIC_ASSERT(kAsciiStringTag != 0 && kAsciiDataHintTag != 0); - __ andb(r8, Immediate(kAsciiStringTag | kAsciiDataHintTag)); - __ cmpb(r8, Immediate(kAsciiStringTag | kAsciiDataHintTag)); + STATIC_ASSERT(kOneByteStringTag != 0 && kAsciiDataHintTag != 0); + __ andb(r8, Immediate(kOneByteStringTag | kAsciiDataHintTag)); + __ cmpb(r8, Immediate(kOneByteStringTag | kAsciiDataHintTag)); __ j(equal, &ascii_data); // Allocate a two byte cons string. __ AllocateTwoByteConsString(rcx, rdi, no_reg, &call_runtime); @@ -5239,7 +5327,7 @@ void SubStringStub::Generate(MacroAssembler* masm) { // string's encoding is wrong because we always have to recheck encoding of // the newly created string's parent anyways due to externalized strings. Label two_byte_slice, set_slice_header; - STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0); STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); __ testb(rbx, Immediate(kStringEncodingMask)); __ j(zero, &two_byte_slice, Label::kNear); @@ -5283,7 +5371,7 @@ void SubStringStub::Generate(MacroAssembler* masm) { __ subq(rdi, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); __ bind(&sequential_string); - STATIC_ASSERT((kAsciiStringTag & kStringEncodingMask) != 0); + STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0); __ testb(rbx, Immediate(kStringEncodingMask)); __ j(zero, &two_byte_sequential); @@ -5887,8 +5975,7 @@ void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm, ASSERT(!name.is(r0)); ASSERT(!name.is(r1)); - // Assert that name contains a string. - if (FLAG_debug_code) __ AbortIfNotString(name); + __ AssertString(name); __ SmiToInteger32(r0, FieldOperand(elements, kCapacityOffset)); __ decl(r0); @@ -6044,18 +6131,20 @@ struct AheadOfTimeWriteBarrierStubList kAheadOfTime[] = { // KeyedStoreStubCompiler::GenerateStoreFastElement. { REG(rdi), REG(rbx), REG(rcx), EMIT_REMEMBERED_SET}, { REG(rdx), REG(rdi), REG(rbx), EMIT_REMEMBERED_SET}, - // ElementsTransitionGenerator::GenerateSmiOnlyToObject - // and ElementsTransitionGenerator::GenerateSmiOnlyToObject + // ElementsTransitionGenerator::GenerateMapChangeElementTransition + // and ElementsTransitionGenerator::GenerateSmiToDouble // and ElementsTransitionGenerator::GenerateDoubleToObject { REG(rdx), REG(rbx), REG(rdi), EMIT_REMEMBERED_SET}, { REG(rdx), REG(rbx), REG(rdi), OMIT_REMEMBERED_SET}, - // ElementsTransitionGenerator::GenerateSmiOnlyToDouble + // ElementsTransitionGenerator::GenerateSmiToDouble // and ElementsTransitionGenerator::GenerateDoubleToObject { REG(rdx), REG(r11), REG(r15), EMIT_REMEMBERED_SET}, // ElementsTransitionGenerator::GenerateDoubleToObject { REG(r11), REG(rax), REG(r15), EMIT_REMEMBERED_SET}, // StoreArrayLiteralElementStub::Generate { REG(rbx), REG(rax), REG(rcx), EMIT_REMEMBERED_SET}, + // FastNewClosureStub::Generate + { REG(rcx), REG(rdx), REG(rbx), EMIT_REMEMBERED_SET}, // Null termination. { REG(no_reg), REG(no_reg), REG(no_reg), EMIT_REMEMBERED_SET} }; @@ -6100,6 +6189,11 @@ void RecordWriteStub::GenerateFixedRegStubsAheadOfTime() { } +bool CodeStub::CanUseFPRegisters() { + return true; // Always have SSE2 on x64. +} + + // Takes the input in 3 registers: address_ value_ and object_. A pointer to // the value has just been written into the object, now this stub makes sure // we keep the GC informed. The word in the object where the value has been @@ -6232,6 +6326,17 @@ void RecordWriteStub::CheckNeedsToInformIncrementalMarker( Label need_incremental; Label need_incremental_pop_object; + __ movq(regs_.scratch0(), Immediate(~Page::kPageAlignmentMask)); + __ and_(regs_.scratch0(), regs_.object()); + __ movq(regs_.scratch1(), + Operand(regs_.scratch0(), + MemoryChunk::kWriteBarrierCounterOffset)); + __ subq(regs_.scratch1(), Immediate(1)); + __ movq(Operand(regs_.scratch0(), + MemoryChunk::kWriteBarrierCounterOffset), + regs_.scratch1()); + __ j(negative, &need_incremental); + // Let's look at the color of the object: If it is not black we don't have // to inform the incremental marker. __ JumpIfBlack(regs_.object(), @@ -6323,9 +6428,9 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { __ CheckFastElements(rdi, &double_elements); - // FAST_SMI_ONLY_ELEMENTS or FAST_ELEMENTS + // FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS __ JumpIfSmi(rax, &smi_element); - __ CheckFastSmiOnlyElements(rdi, &fast_elements); + __ CheckFastSmiElements(rdi, &fast_elements); // Store into the array literal requires a elements transition. Call into // the runtime. @@ -6343,7 +6448,7 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { // place. __ TailCallRuntime(Runtime::kStoreArrayLiteralElement, 5, 1); - // Array literal has ElementsKind of FAST_ELEMENTS and value is an object. + // Array literal has ElementsKind of FAST_*_ELEMENTS and value is an object. __ bind(&fast_elements); __ SmiToInteger32(kScratchRegister, rcx); __ movq(rbx, FieldOperand(rbx, JSObject::kElementsOffset)); @@ -6357,8 +6462,8 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { OMIT_SMI_CHECK); __ ret(0); - // Array literal has ElementsKind of FAST_SMI_ONLY_ELEMENTS or - // FAST_ELEMENTS, and value is Smi. + // Array literal has ElementsKind of FAST_*_SMI_ELEMENTS or + // FAST_*_ELEMENTS, and value is Smi. __ bind(&smi_element); __ SmiToInteger32(kScratchRegister, rcx); __ movq(rbx, FieldOperand(rbx, JSObject::kElementsOffset)); @@ -6379,6 +6484,74 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { __ ret(0); } + +void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) { + if (entry_hook_ != NULL) { + ProfileEntryHookStub stub; + masm->CallStub(&stub); + } +} + + +void ProfileEntryHookStub::Generate(MacroAssembler* masm) { + // Save volatile registers. + // Live registers at this point are the same as at the start of any + // JS function: + // o rdi: the JS function object being called (i.e. ourselves) + // o rsi: our context + // o rbp: our caller's frame pointer + // o rsp: stack pointer (pointing to return address) + // o rcx: rcx is zero for method calls and non-zero for function calls. +#ifdef _WIN64 + const int kNumSavedRegisters = 1; + + __ push(rcx); +#else + const int kNumSavedRegisters = 3; + + __ push(rcx); + __ push(rdi); + __ push(rsi); +#endif + + // Calculate the original stack pointer and store it in the second arg. +#ifdef _WIN64 + __ lea(rdx, Operand(rsp, kNumSavedRegisters * kPointerSize)); +#else + __ lea(rsi, Operand(rsp, kNumSavedRegisters * kPointerSize)); +#endif + + // Calculate the function address to the first arg. +#ifdef _WIN64 + __ movq(rcx, Operand(rdx, 0)); + __ subq(rcx, Immediate(Assembler::kShortCallInstructionLength)); +#else + __ movq(rdi, Operand(rsi, 0)); + __ subq(rdi, Immediate(Assembler::kShortCallInstructionLength)); +#endif + + // Call the entry hook function. + __ movq(rax, &entry_hook_, RelocInfo::NONE); + __ movq(rax, Operand(rax, 0)); + + AllowExternalCallThatCantCauseGC scope(masm); + + const int kArgumentCount = 2; + __ PrepareCallCFunction(kArgumentCount); + __ CallCFunction(rax, kArgumentCount); + + // Restore volatile regs. +#ifdef _WIN64 + __ pop(rcx); +#else + __ pop(rsi); + __ pop(rdi); + __ pop(rcx); +#endif + + __ Ret(); +} + #undef __ } } // namespace v8::internal diff --git a/src/3rdparty/v8/src/x64/codegen-x64.cc b/src/3rdparty/v8/src/x64/codegen-x64.cc index a8d39b2..ffccf47 100644 --- a/src/3rdparty/v8/src/x64/codegen-x64.cc +++ b/src/3rdparty/v8/src/x64/codegen-x64.cc @@ -220,7 +220,7 @@ ModuloFunction CreateModuloFunction() { #define __ ACCESS_MASM(masm) -void ElementsTransitionGenerator::GenerateSmiOnlyToObject( +void ElementsTransitionGenerator::GenerateMapChangeElementsTransition( MacroAssembler* masm) { // ----------- S t a t e ------------- // -- rax : value @@ -241,7 +241,7 @@ void ElementsTransitionGenerator::GenerateSmiOnlyToObject( } -void ElementsTransitionGenerator::GenerateSmiOnlyToDouble( +void ElementsTransitionGenerator::GenerateSmiToDouble( MacroAssembler* masm, Label* fail) { // ----------- S t a t e ------------- // -- rax : value @@ -551,7 +551,7 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm, // Dispatch on the encoding: ASCII or two-byte. Label ascii; __ bind(&seq_string); - STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0); STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); __ testb(result, Immediate(kStringEncodingMask)); __ j(not_zero, &ascii, Label::kNear); @@ -577,6 +577,91 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm, #undef __ + +static const int kNoCodeAgeSequenceLength = 6; + +static byte* GetNoCodeAgeSequence(uint32_t* length) { + static bool initialized = false; + static byte sequence[kNoCodeAgeSequenceLength]; + *length = kNoCodeAgeSequenceLength; + if (!initialized) { + // The sequence of instructions that is patched out for aging code is the + // following boilerplate stack-building prologue that is found both in + // FUNCTION and OPTIMIZED_FUNCTION code: + CodePatcher patcher(sequence, kNoCodeAgeSequenceLength); + patcher.masm()->push(rbp); + patcher.masm()->movq(rbp, rsp); + patcher.masm()->push(rsi); + patcher.masm()->push(rdi); + initialized = true; + } + return sequence; +} + + +byte* Code::FindPlatformCodeAgeSequence() { + byte* start = instruction_start(); + uint32_t young_length; + byte* young_sequence = GetNoCodeAgeSequence(&young_length); + if (!memcmp(start, young_sequence, young_length) || + *start == kCallOpcode) { + return start; + } else { + byte* start_after_strict = NULL; + if (kind() == FUNCTION) { + start_after_strict = start + kSizeOfFullCodegenStrictModePrologue; + } else { + ASSERT(kind() == OPTIMIZED_FUNCTION); + start_after_strict = start + kSizeOfOptimizedStrictModePrologue; + } + ASSERT(!memcmp(start_after_strict, young_sequence, young_length) || + *start_after_strict == kCallOpcode); + return start_after_strict; + } +} + + +bool Code::IsYoungSequence(byte* sequence) { + uint32_t young_length; + byte* young_sequence = GetNoCodeAgeSequence(&young_length); + bool result = (!memcmp(sequence, young_sequence, young_length)); + ASSERT(result || *sequence == kCallOpcode); + return result; +} + + +void Code::GetCodeAgeAndParity(byte* sequence, Age* age, + MarkingParity* parity) { + if (IsYoungSequence(sequence)) { + *age = kNoAge; + *parity = NO_MARKING_PARITY; + } else { + sequence++; // Skip the kCallOpcode byte + Address target_address = sequence + *reinterpret_cast<int*>(sequence) + + Assembler::kCallTargetAddressOffset; + Code* stub = GetCodeFromTargetAddress(target_address); + GetCodeAgeAndParity(stub, age, parity); + } +} + + +void Code::PatchPlatformCodeAge(byte* sequence, + Code::Age age, + MarkingParity parity) { + uint32_t young_length; + byte* young_sequence = GetNoCodeAgeSequence(&young_length); + if (age == kNoAge) { + memcpy(sequence, young_sequence, young_length); + CPU::FlushICache(sequence, young_length); + } else { + Code* stub = GetCodeAgeStub(age, parity); + CodePatcher patcher(sequence, young_length); + patcher.masm()->call(stub->instruction_start()); + patcher.masm()->nop(); + } +} + + } } // namespace v8::internal #endif // V8_TARGET_ARCH_X64 diff --git a/src/3rdparty/v8/src/x64/codegen-x64.h b/src/3rdparty/v8/src/x64/codegen-x64.h index 2e80751..5d8bbff 100644 --- a/src/3rdparty/v8/src/x64/codegen-x64.h +++ b/src/3rdparty/v8/src/x64/codegen-x64.h @@ -39,6 +39,8 @@ class CompilationInfo; enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF }; +static const int kSizeOfFullCodegenStrictModePrologue = 14; +static const int kSizeOfOptimizedStrictModePrologue = 14; // ------------------------------------------------------------------------- // CodeGenerator diff --git a/src/3rdparty/v8/src/x64/deoptimizer-x64.cc b/src/3rdparty/v8/src/x64/deoptimizer-x64.cc index f3046b9..a3fe8f9 100644 --- a/src/3rdparty/v8/src/x64/deoptimizer-x64.cc +++ b/src/3rdparty/v8/src/x64/deoptimizer-x64.cc @@ -52,6 +52,10 @@ void Deoptimizer::DeoptimizeFunction(JSFunction* function) { if (!function->IsOptimized()) return; + // The optimized code is going to be patched, so we cannot use it + // any more. Play safe and reset the whole cache. + function->shared()->ClearOptimizedCodeMap(); + // Get the optimized code. Code* code = function->code(); @@ -100,8 +104,7 @@ void Deoptimizer::DeoptimizeFunction(JSFunction* function) { // ignore all slots that might have been recorded on it. isolate->heap()->mark_compact_collector()->InvalidateCode(code); - // Set the code for the function to non-optimized version. - function->ReplaceCode(function->shared()->code()); + ReplaceCodeForRelatedFunctions(function, code); if (FLAG_trace_deopt) { PrintF("[forced deoptimization: "); @@ -188,11 +191,11 @@ void Deoptimizer::RevertStackCheckCodeAt(Code* unoptimized_code, } -static int LookupBailoutId(DeoptimizationInputData* data, unsigned ast_id) { +static int LookupBailoutId(DeoptimizationInputData* data, BailoutId ast_id) { ByteArray* translations = data->TranslationByteArray(); int length = data->DeoptCount(); for (int i = 0; i < length; i++) { - if (static_cast<unsigned>(data->AstId(i)->value()) == ast_id) { + if (data->AstId(i) == ast_id) { TranslationIterator it(translations, data->TranslationIndex(i)->value()); int value = it.Next(); ASSERT(Translation::BEGIN == static_cast<Translation::Opcode>(value)); @@ -214,7 +217,7 @@ void Deoptimizer::DoComputeOsrOutputFrame() { // the ast id. Confusing. ASSERT(bailout_id_ == ast_id); - int bailout_id = LookupBailoutId(data, ast_id); + int bailout_id = LookupBailoutId(data, BailoutId(ast_id)); unsigned translation_index = data->TranslationIndex(bailout_id)->value(); ByteArray* translations = data->TranslationByteArray(); @@ -234,9 +237,9 @@ void Deoptimizer::DoComputeOsrOutputFrame() { unsigned node_id = iterator.Next(); USE(node_id); ASSERT(node_id == ast_id); - JSFunction* function = JSFunction::cast(ComputeLiteral(iterator.Next())); - USE(function); - ASSERT(function == function_); + int closure_id = iterator.Next(); + USE(closure_id); + ASSERT_EQ(Translation::kSelfLiteralId, closure_id); unsigned height = iterator.Next(); unsigned height_in_bytes = height * kPointerSize; USE(height_in_bytes); @@ -341,15 +344,15 @@ void Deoptimizer::DoComputeOsrOutputFrame() { output_[0]->SetPc(pc); } Code* continuation = - function->GetIsolate()->builtins()->builtin(Builtins::kNotifyOSR); + function_->GetIsolate()->builtins()->builtin(Builtins::kNotifyOSR); output_[0]->SetContinuation( reinterpret_cast<intptr_t>(continuation->entry())); if (FLAG_trace_osr) { PrintF("[on-stack replacement translation %s: 0x%08" V8PRIxPTR " ", ok ? "finished" : "aborted", - reinterpret_cast<intptr_t>(function)); - function->PrintName(); + reinterpret_cast<intptr_t>(function_)); + function_->PrintName(); PrintF(" => pc=0x%0" V8PRIxPTR "]\n", output_[0]->GetPc()); } } @@ -576,16 +579,143 @@ void Deoptimizer::DoComputeConstructStubFrame(TranslationIterator* iterator, } +void Deoptimizer::DoComputeAccessorStubFrame(TranslationIterator* iterator, + int frame_index, + bool is_setter_stub_frame) { + JSFunction* accessor = JSFunction::cast(ComputeLiteral(iterator->Next())); + // The receiver (and the implicit return value, if any) are expected in + // registers by the LoadIC/StoreIC, so they don't belong to the output stack + // frame. This means that we have to use a height of 0. + unsigned height = 0; + unsigned height_in_bytes = height * kPointerSize; + const char* kind = is_setter_stub_frame ? "setter" : "getter"; + if (FLAG_trace_deopt) { + PrintF(" translating %s stub => height=%u\n", kind, height_in_bytes); + } + + // We need 1 stack entry for the return address + 4 stack entries from + // StackFrame::INTERNAL (FP, context, frame type, code object, see + // MacroAssembler::EnterFrame). For a setter stub frame we need one additional + // entry for the implicit return value, see + // StoreStubCompiler::CompileStoreViaSetter. + unsigned fixed_frame_entries = 1 + 4 + (is_setter_stub_frame ? 1 : 0); + unsigned fixed_frame_size = fixed_frame_entries * kPointerSize; + unsigned output_frame_size = height_in_bytes + fixed_frame_size; + + // Allocate and store the output frame description. + FrameDescription* output_frame = + new(output_frame_size) FrameDescription(output_frame_size, accessor); + output_frame->SetFrameType(StackFrame::INTERNAL); + + // A frame for an accessor stub can not be the topmost or bottommost one. + ASSERT(frame_index > 0 && frame_index < output_count_ - 1); + ASSERT(output_[frame_index] == NULL); + output_[frame_index] = output_frame; + + // The top address of the frame is computed from the previous frame's top and + // this frame's size. + intptr_t top_address = output_[frame_index - 1]->GetTop() - output_frame_size; + output_frame->SetTop(top_address); + + unsigned output_offset = output_frame_size; + + // Read caller's PC from the previous frame. + output_offset -= kPointerSize; + intptr_t callers_pc = output_[frame_index - 1]->GetPc(); + output_frame->SetFrameSlot(output_offset, callers_pc); + if (FLAG_trace_deopt) { + PrintF(" 0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR + " ; caller's pc\n", + top_address + output_offset, output_offset, callers_pc); + } + + // Read caller's FP from the previous frame, and set this frame's FP. + output_offset -= kPointerSize; + intptr_t value = output_[frame_index - 1]->GetFp(); + output_frame->SetFrameSlot(output_offset, value); + intptr_t fp_value = top_address + output_offset; + output_frame->SetFp(fp_value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR + " ; caller's fp\n", + fp_value, output_offset, value); + } + + // The context can be gotten from the previous frame. + output_offset -= kPointerSize; + value = output_[frame_index - 1]->GetContext(); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR + " ; context\n", + top_address + output_offset, output_offset, value); + } + + // A marker value is used in place of the function. + output_offset -= kPointerSize; + value = reinterpret_cast<intptr_t>(Smi::FromInt(StackFrame::INTERNAL)); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR + " ; function (%s sentinel)\n", + top_address + output_offset, output_offset, value, kind); + } + + // Get Code object from accessor stub. + output_offset -= kPointerSize; + Builtins::Name name = is_setter_stub_frame ? + Builtins::kStoreIC_Setter_ForDeopt : + Builtins::kLoadIC_Getter_ForDeopt; + Code* accessor_stub = isolate_->builtins()->builtin(name); + value = reinterpret_cast<intptr_t>(accessor_stub); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR + " ; code object\n", + top_address + output_offset, output_offset, value); + } + + // Skip receiver. + Translation::Opcode opcode = + static_cast<Translation::Opcode>(iterator->Next()); + iterator->Skip(Translation::NumberOfOperandsFor(opcode)); + + if (is_setter_stub_frame) { + // The implicit return value was part of the artificial setter stub + // environment. + output_offset -= kPointerSize; + DoTranslateCommand(iterator, frame_index, output_offset); + } + + ASSERT(0 == output_offset); + + Smi* offset = is_setter_stub_frame ? + isolate_->heap()->setter_stub_deopt_pc_offset() : + isolate_->heap()->getter_stub_deopt_pc_offset(); + intptr_t pc = reinterpret_cast<intptr_t>( + accessor_stub->instruction_start() + offset->value()); + output_frame->SetPc(pc); +} + + void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator, int frame_index) { - int node_id = iterator->Next(); - JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next())); + BailoutId node_id = BailoutId(iterator->Next()); + JSFunction* function; + if (frame_index != 0) { + function = JSFunction::cast(ComputeLiteral(iterator->Next())); + } else { + int closure_id = iterator->Next(); + USE(closure_id); + ASSERT_EQ(Translation::kSelfLiteralId, closure_id); + function = function_; + } unsigned height = iterator->Next(); unsigned height_in_bytes = height * kPointerSize; if (FLAG_trace_deopt) { PrintF(" translating "); function->PrintName(); - PrintF(" => node=%d, height=%d\n", node_id, height_in_bytes); + PrintF(" => node=%d, height=%d\n", node_id.ToInt(), height_in_bytes); } // The 'fixed' part of the frame consists of the incoming parameters and diff --git a/src/3rdparty/v8/src/x64/disasm-x64.cc b/src/3rdparty/v8/src/x64/disasm-x64.cc index 0738153..c8606c4 100644 --- a/src/3rdparty/v8/src/x64/disasm-x64.cc +++ b/src/3rdparty/v8/src/x64/disasm-x64.cc @@ -703,6 +703,9 @@ int DisassemblerX64::F6F7Instruction(byte* data) { case 4: mnem = "mul"; break; + case 5: + mnem = "imul"; + break; case 7: mnem = "idiv"; break; diff --git a/src/3rdparty/v8/src/x64/full-codegen-x64.cc b/src/3rdparty/v8/src/x64/full-codegen-x64.cc index 22f2ebb..a71c9b1 100644 --- a/src/3rdparty/v8/src/x64/full-codegen-x64.cc +++ b/src/3rdparty/v8/src/x64/full-codegen-x64.cc @@ -123,6 +123,8 @@ void FullCodeGenerator::Generate() { SetFunctionPosition(function()); Comment cmnt(masm_, "[ function compiled by full code generator"); + ProfileEntryHookStub::MaybeCallEntryHook(masm_); + #ifdef DEBUG if (strlen(FLAG_stop_at) > 0 && info->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) { @@ -136,6 +138,8 @@ void FullCodeGenerator::Generate() { // function calls. if (!info->is_classic_mode() || info->is_native()) { Label ok; + Label begin; + __ bind(&begin); __ testq(rcx, rcx); __ j(zero, &ok, Label::kNear); // +1 for return address. @@ -143,6 +147,8 @@ void FullCodeGenerator::Generate() { __ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex); __ movq(Operand(rsp, receiver_offset), kScratchRegister); __ bind(&ok); + ASSERT(!FLAG_age_code || + (kSizeOfFullCodegenStrictModePrologue == ok.pos() - begin.pos())); } // Open a frame scope to indicate that there is a frame on the stack. The @@ -173,11 +179,14 @@ void FullCodeGenerator::Generate() { int heap_slots = info->scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; if (heap_slots > 0 || (scope()->is_qml_mode() && scope()->is_global_scope())) { - Comment cmnt(masm_, "[ Allocate local context"); + Comment cmnt(masm_, "[ Allocate context"); // Argument to NewContext is the function, which is still in rdi. __ push(rdi); - if (heap_slots <= FastNewContextStub::kMaximumSlots) { - FastNewContextStub stub((heap_slots < 0)?0:heap_slots); + if (FLAG_harmony_scoping && info->scope()->is_global_scope()) { + __ Push(info->scope()->GetScopeInfo()); + __ CallRuntime(Runtime::kNewGlobalContext, 2); + } else if (heap_slots <= FastNewContextStub::kMaximumSlots) { + FastNewContextStub stub((heap_slots < 0) ? 0 : heap_slots); __ CallStub(&stub); } else { __ CallRuntime(Runtime::kNewFunctionContext, 1); @@ -253,7 +262,7 @@ void FullCodeGenerator::Generate() { scope()->VisitIllegalRedeclaration(this); } else { - PrepareForBailoutForId(AstNode::kFunctionEntryId, NO_REGISTERS); + PrepareForBailoutForId(BailoutId::FunctionEntry(), NO_REGISTERS); { Comment cmnt(masm_, "[ Declarations"); // For named function expressions, declare the function name as a // constant. @@ -268,7 +277,7 @@ void FullCodeGenerator::Generate() { } { Comment cmnt(masm_, "[ Stack check"); - PrepareForBailoutForId(AstNode::kDeclarationsId, NO_REGISTERS); + PrepareForBailoutForId(BailoutId::Declarations(), NO_REGISTERS); Label ok; __ CompareRoot(rsp, Heap::kStackLimitRootIndex); __ j(above_equal, &ok, Label::kNear); @@ -311,10 +320,6 @@ void FullCodeGenerator::EmitProfilingCounterReset() { // Self-optimization is a one-off thing; if it fails, don't try again. reset_value = Smi::kMaxValue; } - if (isolate()->IsDebuggerActive()) { - // Detect debug break requests as soon as possible. - reset_value = 10; - } __ movq(rbx, profiling_counter_, RelocInfo::EMBEDDED_OBJECT); __ movq(kScratchRegister, reinterpret_cast<uint64_t>(Smi::FromInt(reset_value)), @@ -324,10 +329,6 @@ void FullCodeGenerator::EmitProfilingCounterReset() { } -static const int kMaxBackEdgeWeight = 127; -static const int kBackEdgeDistanceDivisor = 162; - - void FullCodeGenerator::EmitStackCheck(IterationStatement* stmt, Label* back_edge_target) { Comment cmnt(masm_, "[ Stack check"); @@ -339,7 +340,7 @@ void FullCodeGenerator::EmitStackCheck(IterationStatement* stmt, ASSERT(back_edge_target->is_bound()); int distance = masm_->SizeOfCodeGeneratedSince(back_edge_target); weight = Min(kMaxBackEdgeWeight, - Max(1, distance / kBackEdgeDistanceDivisor)); + Max(1, distance / kBackEdgeDistanceUnit)); } EmitProfilingCounterDecrement(weight); __ j(positive, &ok, Label::kNear); @@ -395,7 +396,7 @@ void FullCodeGenerator::EmitReturnSequence() { } else if (FLAG_weighted_back_edges) { int distance = masm_->pc_offset(); weight = Min(kMaxBackEdgeWeight, - Max(1, distance = kBackEdgeDistanceDivisor)); + Max(1, distance / kBackEdgeDistanceUnit)); } EmitProfilingCounterDecrement(weight); Label ok; @@ -509,12 +510,20 @@ void FullCodeGenerator::EffectContext::Plug(Handle<Object> lit) const { void FullCodeGenerator::AccumulatorValueContext::Plug( Handle<Object> lit) const { - __ Move(result_register(), lit); + if (lit->IsSmi()) { + __ SafeMove(result_register(), Smi::cast(*lit)); + } else { + __ Move(result_register(), lit); + } } void FullCodeGenerator::StackValueContext::Plug(Handle<Object> lit) const { - __ Push(lit); + if (lit->IsSmi()) { + __ SafePush(Smi::cast(*lit)); + } else { + __ Push(lit); + } } @@ -660,7 +669,7 @@ void FullCodeGenerator::DoTest(Expression* condition, Label* fall_through) { ToBooleanStub stub(result_register()); __ push(result_register()); - __ CallStub(&stub); + __ CallStub(&stub, condition->test_id()); __ testq(result_register(), result_register()); // The stub returns nonzero for true. Split(not_zero, if_true, if_false, fall_through); @@ -758,7 +767,7 @@ void FullCodeGenerator::EmitDebugCheckDeclarationContext(Variable* variable) { // The variable in the declaration always resides in the current function // context. ASSERT_EQ(0, scope()->ContextChainLength(variable->scope())); - if (FLAG_debug_code) { + if (generate_debug_code_) { // Check that we're not inside a with or catch context. __ movq(rbx, FieldOperand(rsi, HeapObject::kMapOffset)); __ CompareRoot(rbx, Heap::kWithContextMapRootIndex); @@ -780,11 +789,12 @@ void FullCodeGenerator::VisitVariableDeclaration( bool hole_init = mode == CONST || mode == CONST_HARMONY || mode == LET; switch (variable->location()) { case Variable::UNALLOCATED: - globals_->Add(variable->name()); + globals_->Add(variable->name(), zone()); globals_->Add(variable->binding_needs_init() ? isolate()->factory()->the_hole_value() - : isolate()->factory()->undefined_value()); - globals_->Add(isolate()->factory()->ToBoolean(variable->is_qml_global())); + : isolate()->factory()->undefined_value(), + zone()); + globals_->Add(isolate()->factory()->ToBoolean(variable->is_qml_global()), zone()); break; case Variable::PARAMETER: @@ -812,10 +822,9 @@ void FullCodeGenerator::VisitVariableDeclaration( __ push(rsi); __ Push(variable->name()); // Declaration nodes are always introduced in one of four modes. - ASSERT(mode == VAR || mode == LET || - mode == CONST || mode == CONST_HARMONY); + ASSERT(IsDeclaredVariableMode(mode)); PropertyAttributes attr = - (mode == CONST || mode == CONST_HARMONY) ? READ_ONLY : NONE; + IsImmutableVariableMode(mode) ? READ_ONLY : NONE; __ Push(Smi::FromInt(attr)); // Push initial value, if any. // Note: For variables we must not push an initial value (such as @@ -839,13 +848,13 @@ void FullCodeGenerator::VisitFunctionDeclaration( Variable* variable = proxy->var(); switch (variable->location()) { case Variable::UNALLOCATED: { - globals_->Add(variable->name()); + globals_->Add(variable->name(), zone()); Handle<SharedFunctionInfo> function = Compiler::BuildFunctionInfo(declaration->fun(), script()); // Check for stack-overflow exception. if (function.is_null()) return SetStackOverflow(); - globals_->Add(function); - globals_->Add(isolate()->factory()->ToBoolean(variable->is_qml_global())); + globals_->Add(function, zone()); + globals_->Add(isolate()->factory()->ToBoolean(variable->is_qml_global()), zone()); break; } @@ -897,9 +906,9 @@ void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* declaration) { switch (variable->location()) { case Variable::UNALLOCATED: { Comment cmnt(masm_, "[ ModuleDeclaration"); - globals_->Add(variable->name()); - globals_->Add(instance); - globals_->Add(isolate()->factory()->ToBoolean(variable->is_qml_global())); + globals_->Add(variable->name(), zone()); + globals_->Add(instance, zone()); + globals_->Add(isolate()->factory()->ToBoolean(variable->is_qml_global()), zone()); Visit(declaration->module()); break; } @@ -1103,22 +1112,32 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { Label fixed_array; __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset), Heap::kMetaMapRootIndex); - __ j(not_equal, &fixed_array, Label::kNear); + __ j(not_equal, &fixed_array); // We got a map in register rax. Get the enumeration cache from it. __ bind(&use_cache); + + Label no_descriptors; + + __ EnumLength(rdx, rax); + __ Cmp(rdx, Smi::FromInt(0)); + __ j(equal, &no_descriptors); + __ LoadInstanceDescriptors(rax, rcx); - __ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumerationIndexOffset)); - __ movq(rdx, FieldOperand(rcx, DescriptorArray::kEnumCacheBridgeCacheOffset)); + __ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumCacheOffset)); + __ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumCacheBridgeCacheOffset)); // Set up the four remaining stack slots. __ push(rax); // Map. - __ push(rdx); // Enumeration cache. - __ movq(rax, FieldOperand(rdx, FixedArray::kLengthOffset)); - __ push(rax); // Enumeration cache length (as smi). + __ push(rcx); // Enumeration cache. + __ push(rdx); // Number of valid entries for the map in the enum cache. __ Push(Smi::FromInt(0)); // Initial index. __ jmp(&loop); + __ bind(&no_descriptors); + __ addq(rsp, Immediate(kPointerSize)); + __ jmp(&exit); + // We got a fixed array in register rax. Iterate through that. Label non_proxy; __ bind(&fixed_array); @@ -1127,7 +1146,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { isolate()->factory()->NewJSGlobalPropertyCell( Handle<Object>( Smi::FromInt(TypeFeedbackCells::kForInFastCaseMarker))); - RecordTypeFeedbackCell(stmt->PrepareId(), cell); + RecordTypeFeedbackCell(stmt->ForInFeedbackId(), cell); __ LoadHeapObject(rbx, cell); __ Move(FieldOperand(rbx, JSGlobalPropertyCell::kValueOffset), Smi::FromInt(TypeFeedbackCells::kForInSlowCaseMarker)); @@ -1286,9 +1305,9 @@ void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var, __ movq(temp, context); } // Load map for comparison into register, outside loop. - __ LoadRoot(kScratchRegister, Heap::kGlobalContextMapRootIndex); + __ LoadRoot(kScratchRegister, Heap::kNativeContextMapRootIndex); __ bind(&next); - // Terminate at global context. + // Terminate at native context. __ cmpq(kScratchRegister, FieldOperand(temp, HeapObject::kMapOffset)); __ j(equal, &fast, Label::kNear); // Check that extension is NULL. @@ -1571,9 +1590,9 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { // Mark all computed expressions that are bound to a key that // is shadowed by a later occurrence of the same key. For the // marked expressions, no store code is emitted. - expr->CalculateEmitStore(); + expr->CalculateEmitStore(zone()); - AccessorTable accessor_table(isolate()->zone()); + AccessorTable accessor_table(zone()); for (int i = 0; i < expr->properties()->length(); i++) { ObjectLiteral::Property* property = expr->properties()->at(i); if (property->IsCompileTimeValue()) continue; @@ -1599,7 +1618,7 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { Handle<Code> ic = is_classic_mode() ? isolate()->builtins()->StoreIC_Initialize() : isolate()->builtins()->StoreIC_Initialize_Strict(); - CallIC(ic, RelocInfo::CODE_TARGET, key->id()); + CallIC(ic, RelocInfo::CODE_TARGET, key->LiteralFeedbackId()); PrepareForBailoutForId(key->id(), NO_REGISTERS); } else { VisitForEffect(value); @@ -1663,7 +1682,8 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { ASSERT_EQ(2, constant_elements->length()); ElementsKind constant_elements_kind = static_cast<ElementsKind>(Smi::cast(constant_elements->get(0))->value()); - bool has_constant_fast_elements = constant_elements_kind == FAST_ELEMENTS; + bool has_constant_fast_elements = + IsFastObjectElementsKind(constant_elements_kind); Handle<FixedArrayBase> constant_elements_values( FixedArrayBase::cast(constant_elements->get(1))); @@ -1674,7 +1694,7 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { Heap* heap = isolate()->heap(); if (has_constant_fast_elements && constant_elements_values->map() == heap->fixed_cow_array_map()) { - // If the elements are already FAST_ELEMENTS, the boilerplate cannot + // If the elements are already FAST_*_ELEMENTS, the boilerplate cannot // change, so it's possible to specialize the stub in advance. __ IncrementCounter(isolate()->counters()->cow_arrays_created_stub(), 1); FastCloneShallowArrayStub stub( @@ -1686,10 +1706,9 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) { __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); } else { - ASSERT(constant_elements_kind == FAST_ELEMENTS || - constant_elements_kind == FAST_SMI_ONLY_ELEMENTS || + ASSERT(IsFastSmiOrObjectElementsKind(constant_elements_kind) || FLAG_smi_only_arrays); - // If the elements are already FAST_ELEMENTS, the boilerplate cannot + // If the elements are already FAST_*_ELEMENTS, the boilerplate cannot // change, so it's possible to specialize the stub in advance. FastCloneShallowArrayStub::Mode mode = has_constant_fast_elements ? FastCloneShallowArrayStub::CLONE_ELEMENTS @@ -1717,9 +1736,9 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { } VisitForAccumulatorValue(subexpr); - if (constant_elements_kind == FAST_ELEMENTS) { - // Fast-case array literal with ElementsKind of FAST_ELEMENTS, they cannot - // transition and don't need to call the runtime stub. + if (IsFastObjectElementsKind(constant_elements_kind)) { + // Fast-case array literal with ElementsKind of FAST_*_ELEMENTS, they + // cannot transition and don't need to call the runtime stub. int offset = FixedArray::kHeaderSize + (i * kPointerSize); __ movq(rbx, Operand(rsp, 0)); // Copy of array literal. __ movq(rbx, FieldOperand(rbx, JSObject::kElementsOffset)); @@ -1810,11 +1829,11 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { break; case NAMED_PROPERTY: EmitNamedPropertyLoad(property); - PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG); + PrepareForBailoutForId(property->LoadId(), TOS_REG); break; case KEYED_PROPERTY: EmitKeyedPropertyLoad(property); - PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG); + PrepareForBailoutForId(property->LoadId(), TOS_REG); break; } } @@ -1869,14 +1888,14 @@ void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) { Literal* key = prop->key()->AsLiteral(); __ Move(rcx, key->handle()); Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); - CallIC(ic, RelocInfo::CODE_TARGET, prop->id()); + CallIC(ic, RelocInfo::CODE_TARGET, prop->PropertyFeedbackId()); } void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { SetSourcePosition(prop->position()); Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize(); - CallIC(ic, RelocInfo::CODE_TARGET, prop->id()); + CallIC(ic, RelocInfo::CODE_TARGET, prop->PropertyFeedbackId()); } @@ -1898,7 +1917,8 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr, __ bind(&stub_call); __ movq(rax, rcx); BinaryOpStub stub(op, mode); - CallIC(stub.GetCode(), RelocInfo::CODE_TARGET, expr->id()); + CallIC(stub.GetCode(), RelocInfo::CODE_TARGET, + expr->BinaryOperationFeedbackId()); patch_site.EmitPatchInfo(); __ jmp(&done, Label::kNear); @@ -1947,7 +1967,8 @@ void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr, __ pop(rdx); BinaryOpStub stub(op, mode); JumpPatchSite patch_site(masm_); // unbound, signals no inlined smi code. - CallIC(stub.GetCode(), RelocInfo::CODE_TARGET, expr->id()); + CallIC(stub.GetCode(), RelocInfo::CODE_TARGET, + expr->BinaryOperationFeedbackId()); patch_site.EmitPatchInfo(); context()->Plug(rax); } @@ -2073,7 +2094,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, // in harmony mode. if (var->IsStackAllocated() || var->IsContextSlot()) { MemOperand location = VarOperand(var, rcx); - if (FLAG_debug_code && op == Token::INIT_LET) { + if (generate_debug_code_ && op == Token::INIT_LET) { // Check for an uninitialized let binding. __ movq(rdx, location); __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); @@ -2105,37 +2126,15 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { ASSERT(prop != NULL); ASSERT(prop->key()->AsLiteral() != NULL); - // If the assignment starts a block of assignments to the same object, - // change to slow case to avoid the quadratic behavior of repeatedly - // adding fast properties. - if (expr->starts_initialization_block()) { - __ push(result_register()); - __ push(Operand(rsp, kPointerSize)); // Receiver is now under value. - __ CallRuntime(Runtime::kToSlowProperties, 1); - __ pop(result_register()); - } - // Record source code position before IC call. SetSourcePosition(expr->position()); __ Move(rcx, prop->key()->AsLiteral()->handle()); - if (expr->ends_initialization_block()) { - __ movq(rdx, Operand(rsp, 0)); - } else { - __ pop(rdx); - } + __ pop(rdx); Handle<Code> ic = is_classic_mode() ? isolate()->builtins()->StoreIC_Initialize() : isolate()->builtins()->StoreIC_Initialize_Strict(); - CallIC(ic, RelocInfo::CODE_TARGET, expr->id()); + CallIC(ic, RelocInfo::CODE_TARGET, expr->AssignmentFeedbackId()); - // If the assignment ends an initialization block, revert to fast case. - if (expr->ends_initialization_block()) { - __ push(rax); // Result of assignment, saved even if not needed. - __ push(Operand(rsp, kPointerSize)); // Receiver is under value. - __ CallRuntime(Runtime::kToFastProperties, 1); - __ pop(rax); - __ Drop(1); - } PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); context()->Plug(rax); } @@ -2144,38 +2143,14 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { // Assignment to a property, using a keyed store IC. - // If the assignment starts a block of assignments to the same object, - // change to slow case to avoid the quadratic behavior of repeatedly - // adding fast properties. - if (expr->starts_initialization_block()) { - __ push(result_register()); - // Receiver is now under the key and value. - __ push(Operand(rsp, 2 * kPointerSize)); - __ CallRuntime(Runtime::kToSlowProperties, 1); - __ pop(result_register()); - } - __ pop(rcx); - if (expr->ends_initialization_block()) { - __ movq(rdx, Operand(rsp, 0)); // Leave receiver on the stack for later. - } else { - __ pop(rdx); - } + __ pop(rdx); // Record source code position before IC call. SetSourcePosition(expr->position()); Handle<Code> ic = is_classic_mode() ? isolate()->builtins()->KeyedStoreIC_Initialize() : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); - CallIC(ic, RelocInfo::CODE_TARGET, expr->id()); - - // If the assignment ends an initialization block, revert to fast case. - if (expr->ends_initialization_block()) { - __ pop(rdx); - __ push(rax); // Result of assignment, saved even if not needed. - __ push(rdx); - __ CallRuntime(Runtime::kToFastProperties, 1); - __ pop(rax); - } + CallIC(ic, RelocInfo::CODE_TARGET, expr->AssignmentFeedbackId()); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); context()->Plug(rax); @@ -2189,6 +2164,7 @@ void FullCodeGenerator::VisitProperty(Property* expr) { if (key->IsPropertyName()) { VisitForAccumulatorValue(expr->obj()); EmitNamedPropertyLoad(expr); + PrepareForBailoutForId(expr->LoadId(), TOS_REG); context()->Plug(rax); } else { VisitForStackValue(expr->obj()); @@ -2202,7 +2178,7 @@ void FullCodeGenerator::VisitProperty(Property* expr) { void FullCodeGenerator::CallIC(Handle<Code> code, RelocInfo::Mode rmode, - unsigned ast_id) { + TypeFeedbackId ast_id) { ic_total_count_++; __ call(code, rmode, ast_id); } @@ -2225,7 +2201,7 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, // Call the IC initialization code. Handle<Code> ic = isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode); - CallIC(ic, mode, expr->id()); + CallIC(ic, mode, expr->CallFeedbackId()); RecordJSReturnSite(expr); // Restore context register. __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); @@ -2258,7 +2234,7 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, Handle<Code> ic = isolate()->stub_cache()->ComputeKeyedCallInitialize(arg_count); __ movq(rcx, Operand(rsp, (arg_count + 1) * kPointerSize)); // Key. - CallIC(ic, RelocInfo::CODE_TARGET, expr->id()); + CallIC(ic, RelocInfo::CODE_TARGET, expr->CallFeedbackId()); RecordJSReturnSite(expr); // Restore context register. __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); @@ -2278,20 +2254,18 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) { // Record source position for debugger. SetSourcePosition(expr->position()); - // Record call targets in unoptimized code, but not in the snapshot. - if (!Serializer::enabled()) { - flags = static_cast<CallFunctionFlags>(flags | RECORD_CALL_TARGET); - Handle<Object> uninitialized = - TypeFeedbackCells::UninitializedSentinel(isolate()); - Handle<JSGlobalPropertyCell> cell = - isolate()->factory()->NewJSGlobalPropertyCell(uninitialized); - RecordTypeFeedbackCell(expr->id(), cell); - __ Move(rbx, cell); - } + // Record call targets in unoptimized code. + flags = static_cast<CallFunctionFlags>(flags | RECORD_CALL_TARGET); + Handle<Object> uninitialized = + TypeFeedbackCells::UninitializedSentinel(isolate()); + Handle<JSGlobalPropertyCell> cell = + isolate()->factory()->NewJSGlobalPropertyCell(uninitialized); + RecordTypeFeedbackCell(expr->CallFeedbackId(), cell); + __ Move(rbx, cell); CallFunctionStub stub(arg_count, flags); __ movq(rdi, Operand(rsp, (arg_count + 1) * kPointerSize)); - __ CallStub(&stub); + __ CallStub(&stub, expr->CallFeedbackId()); RecordJSReturnSite(expr); // Restore context register. __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); @@ -2469,20 +2443,14 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) { __ movq(rdi, Operand(rsp, arg_count * kPointerSize)); // Record call targets in unoptimized code, but not in the snapshot. - CallFunctionFlags flags; - if (!Serializer::enabled()) { - flags = RECORD_CALL_TARGET; - Handle<Object> uninitialized = - TypeFeedbackCells::UninitializedSentinel(isolate()); - Handle<JSGlobalPropertyCell> cell = - isolate()->factory()->NewJSGlobalPropertyCell(uninitialized); - RecordTypeFeedbackCell(expr->id(), cell); - __ Move(rbx, cell); - } else { - flags = NO_CALL_FUNCTION_FLAGS; - } + Handle<Object> uninitialized = + TypeFeedbackCells::UninitializedSentinel(isolate()); + Handle<JSGlobalPropertyCell> cell = + isolate()->factory()->NewJSGlobalPropertyCell(uninitialized); + RecordTypeFeedbackCell(expr->CallNewFeedbackId(), cell); + __ Move(rbx, cell); - CallConstructStub stub(flags); + CallConstructStub stub(RECORD_CALL_TARGET); __ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL); PrepareForBailoutForId(expr->ReturnId(), TOS_REG); context()->Plug(rax); @@ -2623,7 +2591,7 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - if (FLAG_debug_code) __ AbortIfSmi(rax); + __ AssertNotSmi(rax); // Check whether this map has already been checked to be safe for default // valueOf. @@ -2639,45 +2607,50 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( __ j(equal, if_false); // Look for valueOf symbol in the descriptor array, and indicate false if - // found. The type is not checked, so if it is a transition it is a false - // negative. + // found. Since we omit an enumeration index check, if it is added via a + // transition that shares its descriptor array, this is a false positive. + Label entry, loop, done; + + // Skip loop if no descriptors are valid. + __ NumberOfOwnDescriptors(rcx, rbx); + __ cmpq(rcx, Immediate(0)); + __ j(equal, &done); + __ LoadInstanceDescriptors(rbx, rbx); - __ movq(rcx, FieldOperand(rbx, FixedArray::kLengthOffset)); - // rbx: descriptor array - // rcx: length of descriptor array + // rbx: descriptor array. + // rcx: valid entries in the descriptor array. // Calculate the end of the descriptor array. + __ imul(rcx, rcx, Immediate(DescriptorArray::kDescriptorSize)); SmiIndex index = masm_->SmiToIndex(rdx, rcx, kPointerSizeLog2); __ lea(rcx, Operand( - rbx, index.reg, index.scale, FixedArray::kHeaderSize)); + rbx, index.reg, index.scale, DescriptorArray::kFirstOffset)); // Calculate location of the first key name. - __ addq(rbx, - Immediate(FixedArray::kHeaderSize + - DescriptorArray::kFirstIndex * kPointerSize)); + __ addq(rbx, Immediate(DescriptorArray::kFirstOffset)); // Loop through all the keys in the descriptor array. If one of these is the // symbol valueOf the result is false. - Label entry, loop; __ jmp(&entry); __ bind(&loop); __ movq(rdx, FieldOperand(rbx, 0)); __ Cmp(rdx, FACTORY->value_of_symbol()); __ j(equal, if_false); - __ addq(rbx, Immediate(kPointerSize)); + __ addq(rbx, Immediate(DescriptorArray::kDescriptorSize * kPointerSize)); __ bind(&entry); __ cmpq(rbx, rcx); __ j(not_equal, &loop); + __ bind(&done); // Reload map as register rbx was used as temporary above. __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset)); - // If a valueOf property is not found on the object check that it's + // If a valueOf property is not found on the object check that its // prototype is the un-modified String prototype. If not result is false. __ movq(rcx, FieldOperand(rbx, Map::kPrototypeOffset)); __ testq(rcx, Immediate(kSmiTagMask)); __ j(zero, if_false); __ movq(rcx, FieldOperand(rcx, HeapObject::kMapOffset)); - __ movq(rdx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); - __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalContextOffset)); + __ movq(rdx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX))); + __ movq(rdx, FieldOperand(rdx, GlobalObject::kNativeContextOffset)); __ cmpq(rcx, ContextOperand(rdx, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX)); __ j(not_equal, if_false); @@ -2847,7 +2820,7 @@ void FullCodeGenerator::EmitArgumentsLength(CallRuntime* expr) { __ movq(rax, Operand(rbx, ArgumentsAdaptorFrameConstants::kLengthOffset)); __ bind(&exit); - if (FLAG_debug_code) __ AbortIfNotSmi(rax); + __ AssertSmi(rax); context()->Plug(rax); } @@ -2954,12 +2927,14 @@ void FullCodeGenerator::EmitRandomHeapNumber(CallRuntime* expr) { // The fresh HeapNumber is in rbx, which is callee-save on both x64 ABIs. __ PrepareCallCFunction(1); #ifdef _WIN64 - __ movq(rcx, ContextOperand(context_register(), Context::GLOBAL_INDEX)); - __ movq(rcx, FieldOperand(rcx, GlobalObject::kGlobalContextOffset)); + __ movq(rcx, + ContextOperand(context_register(), Context::GLOBAL_OBJECT_INDEX)); + __ movq(rcx, FieldOperand(rcx, GlobalObject::kNativeContextOffset)); #else - __ movq(rdi, ContextOperand(context_register(), Context::GLOBAL_INDEX)); - __ movq(rdi, FieldOperand(rdi, GlobalObject::kGlobalContextOffset)); + __ movq(rdi, + ContextOperand(context_register(), Context::GLOBAL_OBJECT_INDEX)); + __ movq(rdi, FieldOperand(rdi, GlobalObject::kNativeContextOffset)); #endif __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1); @@ -3033,19 +3008,18 @@ void FullCodeGenerator::EmitDateField(CallRuntime* expr) { VisitForAccumulatorValue(args->at(0)); // Load the object. - Label runtime, done; + Label runtime, done, not_date_object; Register object = rax; Register result = rax; Register scratch = rcx; -#ifdef DEBUG - __ AbortIfSmi(object); + __ JumpIfSmi(object, ¬_date_object); __ CmpObjectType(object, JS_DATE_TYPE, scratch); - __ Assert(equal, "Trying to get date field from non-date."); -#endif + __ j(not_equal, ¬_date_object); if (index->value() == 0) { __ movq(result, FieldOperand(object, JSDate::kValueOffset)); + __ jmp(&done); } else { if (index->value() < JSDate::kFirstUncachedField) { ExternalReference stamp = ExternalReference::date_cache_stamp(isolate()); @@ -3067,8 +3041,12 @@ void FullCodeGenerator::EmitDateField(CallRuntime* expr) { #endif __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2); __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); - __ bind(&done); + __ jmp(&done); } + + __ bind(¬_date_object); + __ CallRuntime(Runtime::kThrowNotDateError, 0); + __ bind(&done); context()->Plug(rax); } @@ -3333,10 +3311,11 @@ void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) { } VisitForAccumulatorValue(args->last()); // Function. - // Check for proxy. - Label proxy, done; - __ CmpObjectType(rax, JS_FUNCTION_PROXY_TYPE, rbx); - __ j(equal, &proxy); + Label runtime, done; + // Check for non-function argument (including proxy). + __ JumpIfSmi(rax, &runtime); + __ CmpObjectType(rax, JS_FUNCTION_TYPE, rbx); + __ j(not_equal, &runtime); // InvokeFunction requires the function in rdi. Move it in there. __ movq(rdi, result_register()); @@ -3346,7 +3325,7 @@ void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) { __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); __ jmp(&done); - __ bind(&proxy); + __ bind(&runtime); __ push(rax); __ CallRuntime(Runtime::kCall, args->length()); __ bind(&done); @@ -3375,7 +3354,7 @@ void FullCodeGenerator::EmitGetFromCache(CallRuntime* expr) { int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value(); Handle<FixedArray> jsfunction_result_caches( - isolate()->global_context()->jsfunction_result_caches()); + isolate()->native_context()->jsfunction_result_caches()); if (jsfunction_result_caches->length() <= cache_id) { __ Abort("Attempt to use undefined cache."); __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); @@ -3388,9 +3367,9 @@ void FullCodeGenerator::EmitGetFromCache(CallRuntime* expr) { Register key = rax; Register cache = rbx; Register tmp = rcx; - __ movq(cache, ContextOperand(rsi, Context::GLOBAL_INDEX)); + __ movq(cache, ContextOperand(rsi, Context::GLOBAL_OBJECT_INDEX)); __ movq(cache, - FieldOperand(cache, GlobalObject::kGlobalContextOffset)); + FieldOperand(cache, GlobalObject::kNativeContextOffset)); __ movq(cache, ContextOperand(cache, Context::JSFUNCTION_RESULT_CACHES_INDEX)); __ movq(cache, @@ -3491,9 +3470,7 @@ void FullCodeGenerator::EmitGetCachedArrayIndex(CallRuntime* expr) { ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); - if (FLAG_debug_code) { - __ AbortIfNotString(rax); - } + __ AssertString(rax); __ movl(rax, FieldOperand(rax, String::kHashFieldOffset)); ASSERT(String::kHashShift >= kSmiTagSize); @@ -3571,7 +3548,7 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(CallRuntime* expr) { // Loop condition: while (index < array_length). // Live loop registers: index(int32), array_length(int32), string(String*), // scratch, string_length(int32), elements(FixedArray*). - if (FLAG_debug_code) { + if (generate_debug_code_) { __ cmpq(index, array_length); __ Assert(below, "No empty arrays here in EmitFastAsciiArrayJoin"); } @@ -3585,7 +3562,7 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(CallRuntime* expr) { __ movzxbl(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); __ andb(scratch, Immediate( kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask)); - __ cmpb(scratch, Immediate(kStringTag | kAsciiStringTag | kSeqStringTag)); + __ cmpb(scratch, Immediate(kStringTag | kOneByteStringTag | kSeqStringTag)); __ j(not_equal, &bailout); __ AddSmiField(string_length, FieldOperand(string, SeqAsciiString::kLengthOffset)); @@ -3624,7 +3601,7 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(CallRuntime* expr) { __ movzxbl(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); __ andb(scratch, Immediate( kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask)); - __ cmpb(scratch, Immediate(kStringTag | kAsciiStringTag | kSeqStringTag)); + __ cmpb(scratch, Immediate(kStringTag | kOneByteStringTag | kSeqStringTag)); __ j(not_equal, &bailout); // Live registers: @@ -3817,7 +3794,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { RelocInfo::Mode mode = RelocInfo::CODE_TARGET; Handle<Code> ic = isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode); - CallIC(ic, mode, expr->id()); + CallIC(ic, mode, expr->CallRuntimeFeedbackId()); // Restore context register. __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); } else { @@ -3975,7 +3952,8 @@ void FullCodeGenerator::EmitUnaryOperation(UnaryOperation* expr, // accumulator register rax. VisitForAccumulatorValue(expr->expression()); SetSourcePosition(expr->position()); - CallIC(stub.GetCode(), RelocInfo::CODE_TARGET, expr->id()); + CallIC(stub.GetCode(), RelocInfo::CODE_TARGET, + expr->UnaryOperationFeedbackId()); context()->Plug(rax); } @@ -4031,7 +4009,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { if (assign_type == VARIABLE) { PrepareForBailout(expr->expression(), TOS_REG); } else { - PrepareForBailoutForId(expr->CountId(), TOS_REG); + PrepareForBailoutForId(prop->LoadId(), TOS_REG); } // Call ToNumber only if operand is not a smi. @@ -4096,7 +4074,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { __ movq(rdx, rax); __ Move(rax, Smi::FromInt(1)); } - CallIC(stub.GetCode(), RelocInfo::CODE_TARGET, expr->CountId()); + CallIC(stub.GetCode(), RelocInfo::CODE_TARGET, expr->CountBinOpFeedbackId()); patch_site.EmitPatchInfo(); __ bind(&done); @@ -4130,7 +4108,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { Handle<Code> ic = is_classic_mode() ? isolate()->builtins()->StoreIC_Initialize() : isolate()->builtins()->StoreIC_Initialize_Strict(); - CallIC(ic, RelocInfo::CODE_TARGET, expr->id()); + CallIC(ic, RelocInfo::CODE_TARGET, expr->CountStoreFeedbackId()); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { if (!context()->IsEffect()) { @@ -4147,7 +4125,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { Handle<Code> ic = is_classic_mode() ? isolate()->builtins()->KeyedStoreIC_Initialize() : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); - CallIC(ic, RelocInfo::CODE_TARGET, expr->id()); + CallIC(ic, RelocInfo::CODE_TARGET, expr->CountStoreFeedbackId()); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { if (!context()->IsEffect()) { @@ -4354,7 +4332,7 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { // Record position and call the compare IC. SetSourcePosition(expr->position()); Handle<Code> ic = CompareIC::GetUninitialized(op); - CallIC(ic, RelocInfo::CODE_TARGET, expr->id()); + CallIC(ic, RelocInfo::CODE_TARGET, expr->CompareOperationFeedbackId()); patch_site.EmitPatchInfo(); PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); @@ -4436,7 +4414,7 @@ void FullCodeGenerator::PushFunctionArgumentForContextAllocation() { Scope* declaration_scope = scope()->DeclarationScope(); if (declaration_scope->is_global_scope() || declaration_scope->is_module_scope()) { - // Contexts nested in the global context have a canonical empty function + // Contexts nested in the native context have a canonical empty function // as their closure, not the anonymous closure containing the global // code. Pass a smi sentinel and let the runtime look up the empty // function. @@ -4466,15 +4444,52 @@ void FullCodeGenerator::EnterFinallyBlock() { __ subq(rdx, rcx); __ Integer32ToSmi(rdx, rdx); __ push(rdx); + // Store result register while executing finally block. __ push(result_register()); + + // Store pending message while executing finally block. + ExternalReference pending_message_obj = + ExternalReference::address_of_pending_message_obj(isolate()); + __ Load(rdx, pending_message_obj); + __ push(rdx); + + ExternalReference has_pending_message = + ExternalReference::address_of_has_pending_message(isolate()); + __ Load(rdx, has_pending_message); + __ Integer32ToSmi(rdx, rdx); + __ push(rdx); + + ExternalReference pending_message_script = + ExternalReference::address_of_pending_message_script(isolate()); + __ Load(rdx, pending_message_script); + __ push(rdx); } void FullCodeGenerator::ExitFinallyBlock() { ASSERT(!result_register().is(rdx)); ASSERT(!result_register().is(rcx)); + // Restore pending message from stack. + __ pop(rdx); + ExternalReference pending_message_script = + ExternalReference::address_of_pending_message_script(isolate()); + __ Store(pending_message_script, rdx); + + __ pop(rdx); + __ SmiToInteger32(rdx, rdx); + ExternalReference has_pending_message = + ExternalReference::address_of_has_pending_message(isolate()); + __ Store(has_pending_message, rdx); + + __ pop(rdx); + ExternalReference pending_message_obj = + ExternalReference::address_of_pending_message_obj(isolate()); + __ Store(pending_message_obj, rdx); + + // Restore result register from stack. __ pop(result_register()); + // Uncook return address. __ pop(rdx); __ SmiToInteger32(rdx, rdx); diff --git a/src/3rdparty/v8/src/x64/ic-x64.cc b/src/3rdparty/v8/src/x64/ic-x64.cc index 6ba5fb6..efa07a8 100644 --- a/src/3rdparty/v8/src/x64/ic-x64.cc +++ b/src/3rdparty/v8/src/x64/ic-x64.cc @@ -135,7 +135,7 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, r0, r1); - // If probing finds an entry in the dictionary, r0 contains the + // If probing finds an entry in the dictionary, r1 contains the // index into the dictionary. Check that the value is a normal // property. __ bind(&done); @@ -178,10 +178,9 @@ static void GenerateDictionaryStore(MacroAssembler* masm, // // value - holds the value to store and is unchanged. // - // scratch0 - used for index into the property dictionary and is clobbered. + // scratch0 - used during the positive dictionary lookup and is clobbered. // - // scratch1 - used to hold the capacity of the property dictionary and is - // clobbered. + // scratch1 - used for index into the property dictionary and is clobbered. Label done; // Probe the dictionary. @@ -624,6 +623,123 @@ void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) { } +static void KeyedStoreGenerateGenericHelper( + MacroAssembler* masm, + Label* fast_object, + Label* fast_double, + Label* slow, + KeyedStoreCheckMap check_map, + KeyedStoreIncrementLength increment_length) { + Label transition_smi_elements; + Label finish_object_store, non_double_value, transition_double_elements; + Label fast_double_without_map_check; + // Fast case: Do the store, could be either Object or double. + __ bind(fast_object); + // rax: value + // rbx: receiver's elements array (a FixedArray) + // rcx: index + // rdx: receiver (a JSArray) + // r9: map of receiver + if (check_map == kCheckMap) { + __ movq(rdi, FieldOperand(rbx, HeapObject::kMapOffset)); + __ CompareRoot(rdi, Heap::kFixedArrayMapRootIndex); + __ j(not_equal, fast_double); + } + // Smi stores don't require further checks. + Label non_smi_value; + __ JumpIfNotSmi(rax, &non_smi_value); + if (increment_length == kIncrementLength) { + // Add 1 to receiver->length. + __ leal(rdi, Operand(rcx, 1)); + __ Integer32ToSmiField(FieldOperand(rdx, JSArray::kLengthOffset), rdi); + } + // It's irrelevant whether array is smi-only or not when writing a smi. + __ movq(FieldOperand(rbx, rcx, times_pointer_size, FixedArray::kHeaderSize), + rax); + __ ret(0); + + __ bind(&non_smi_value); + // Writing a non-smi, check whether array allows non-smi elements. + // r9: receiver's map + __ CheckFastObjectElements(r9, &transition_smi_elements); + + __ bind(&finish_object_store); + if (increment_length == kIncrementLength) { + // Add 1 to receiver->length. + __ leal(rdi, Operand(rcx, 1)); + __ Integer32ToSmiField(FieldOperand(rdx, JSArray::kLengthOffset), rdi); + } + __ movq(FieldOperand(rbx, rcx, times_pointer_size, FixedArray::kHeaderSize), + rax); + __ movq(rdx, rax); // Preserve the value which is returned. + __ RecordWriteArray( + rbx, rdx, rcx, kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); + __ ret(0); + + __ bind(fast_double); + if (check_map == kCheckMap) { + // Check for fast double array case. If this fails, call through to the + // runtime. + // rdi: elements array's map + __ CompareRoot(rdi, Heap::kFixedDoubleArrayMapRootIndex); + __ j(not_equal, slow); + } + __ bind(&fast_double_without_map_check); + __ StoreNumberToDoubleElements(rax, rbx, rcx, xmm0, + &transition_double_elements); + if (increment_length == kIncrementLength) { + // Add 1 to receiver->length. + __ leal(rdi, Operand(rcx, 1)); + __ Integer32ToSmiField(FieldOperand(rdx, JSArray::kLengthOffset), rdi); + } + __ ret(0); + + __ bind(&transition_smi_elements); + __ movq(rbx, FieldOperand(rdx, HeapObject::kMapOffset)); + + // Transition the array appropriately depending on the value type. + __ movq(r9, FieldOperand(rax, HeapObject::kMapOffset)); + __ CompareRoot(r9, Heap::kHeapNumberMapRootIndex); + __ j(not_equal, &non_double_value); + + // Value is a double. Transition FAST_SMI_ELEMENTS -> + // FAST_DOUBLE_ELEMENTS and complete the store. + __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, + FAST_DOUBLE_ELEMENTS, + rbx, + rdi, + slow); + ElementsTransitionGenerator::GenerateSmiToDouble(masm, slow); + __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset)); + __ jmp(&fast_double_without_map_check); + + __ bind(&non_double_value); + // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS + __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, + FAST_ELEMENTS, + rbx, + rdi, + slow); + ElementsTransitionGenerator::GenerateMapChangeElementsTransition(masm); + __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset)); + __ jmp(&finish_object_store); + + __ bind(&transition_double_elements); + // Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a + // HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and + // transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS + __ movq(rbx, FieldOperand(rdx, HeapObject::kMapOffset)); + __ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS, + FAST_ELEMENTS, + rbx, + rdi, + slow); + ElementsTransitionGenerator::GenerateDoubleToObject(masm, slow); + __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset)); + __ jmp(&finish_object_store); +} + + void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, StrictModeFlag strict_mode) { // ----------- S t a t e ------------- @@ -632,11 +748,9 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, // -- rdx : receiver // -- rsp[0] : return address // ----------------------------------- - Label slow, slow_with_tagged_index, fast, array, extra, check_extra_double; - Label fast_object_with_map_check, fast_object_without_map_check; - Label fast_double_with_map_check, fast_double_without_map_check; - Label transition_smi_elements, finish_object_store, non_double_value; - Label transition_double_elements; + Label slow, slow_with_tagged_index, fast_object, fast_object_grow; + Label fast_double, fast_double_grow; + Label array, extra, check_if_double_array; // Check that the object isn't a smi. __ JumpIfSmi(rdx, &slow_with_tagged_index); @@ -667,7 +781,7 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, // rax: value // rbx: FixedArray // rcx: index - __ j(above, &fast_object_with_map_check); + __ j(above, &fast_object); // Slow case: call runtime. __ bind(&slow); @@ -691,18 +805,14 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, // Increment index to get new length. __ movq(rdi, FieldOperand(rbx, HeapObject::kMapOffset)); __ CompareRoot(rdi, Heap::kFixedArrayMapRootIndex); - __ j(not_equal, &check_extra_double); - __ leal(rdi, Operand(rcx, 1)); - __ Integer32ToSmiField(FieldOperand(rdx, JSArray::kLengthOffset), rdi); - __ jmp(&fast_object_without_map_check); + __ j(not_equal, &check_if_double_array); + __ jmp(&fast_object_grow); - __ bind(&check_extra_double); + __ bind(&check_if_double_array); // rdi: elements array's map __ CompareRoot(rdi, Heap::kFixedDoubleArrayMapRootIndex); __ j(not_equal, &slow); - __ leal(rdi, Operand(rcx, 1)); - __ Integer32ToSmiField(FieldOperand(rdx, JSArray::kLengthOffset), rdi); - __ jmp(&fast_double_without_map_check); + __ jmp(&fast_double_grow); // Array case: Get the length and the elements array from the JS // array. Check that the array is in fast mode (and writable); if it @@ -718,92 +828,10 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, __ SmiCompareInteger32(FieldOperand(rdx, JSArray::kLengthOffset), rcx); __ j(below_equal, &extra); - // Fast case: Do the store. - __ bind(&fast_object_with_map_check); - // rax: value - // rbx: receiver's elements array (a FixedArray) - // rcx: index - // rdx: receiver (a JSArray) - __ movq(rdi, FieldOperand(rbx, HeapObject::kMapOffset)); - __ CompareRoot(rdi, Heap::kFixedArrayMapRootIndex); - __ j(not_equal, &fast_double_with_map_check); - __ bind(&fast_object_without_map_check); - // Smi stores don't require further checks. - Label non_smi_value; - __ JumpIfNotSmi(rax, &non_smi_value); - // It's irrelevant whether array is smi-only or not when writing a smi. - __ movq(FieldOperand(rbx, rcx, times_pointer_size, FixedArray::kHeaderSize), - rax); - __ ret(0); - - __ bind(&non_smi_value); - // Writing a non-smi, check whether array allows non-smi elements. - // r9: receiver's map - __ CheckFastObjectElements(r9, &transition_smi_elements); - __ bind(&finish_object_store); - __ movq(FieldOperand(rbx, rcx, times_pointer_size, FixedArray::kHeaderSize), - rax); - __ movq(rdx, rax); // Preserve the value which is returned. - __ RecordWriteArray( - rbx, rdx, rcx, kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); - __ ret(0); - - __ bind(&fast_double_with_map_check); - // Check for fast double array case. If this fails, call through to the - // runtime. - // rdi: elements array's map - __ CompareRoot(rdi, Heap::kFixedDoubleArrayMapRootIndex); - __ j(not_equal, &slow); - __ bind(&fast_double_without_map_check); - // If the value is a number, store it as a double in the FastDoubleElements - // array. - __ StoreNumberToDoubleElements(rax, rbx, rcx, xmm0, - &transition_double_elements); - __ ret(0); - - __ bind(&transition_smi_elements); - __ movq(rbx, FieldOperand(rdx, HeapObject::kMapOffset)); - - // Transition the array appropriately depending on the value type. - __ movq(r9, FieldOperand(rax, HeapObject::kMapOffset)); - __ CompareRoot(r9, Heap::kHeapNumberMapRootIndex); - __ j(not_equal, &non_double_value); - - // Value is a double. Transition FAST_SMI_ONLY_ELEMENTS -> - // FAST_DOUBLE_ELEMENTS and complete the store. - __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, - FAST_DOUBLE_ELEMENTS, - rbx, - rdi, - &slow); - ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &slow); - __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset)); - __ jmp(&fast_double_without_map_check); - - __ bind(&non_double_value); - // Value is not a double, FAST_SMI_ONLY_ELEMENTS -> FAST_ELEMENTS - __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, - FAST_ELEMENTS, - rbx, - rdi, - &slow); - ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm); - __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset)); - __ jmp(&finish_object_store); - - __ bind(&transition_double_elements); - // Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a - // HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and - // transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS - __ movq(rbx, FieldOperand(rdx, HeapObject::kMapOffset)); - __ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS, - FAST_ELEMENTS, - rbx, - rdi, - &slow); - ElementsTransitionGenerator::GenerateDoubleToObject(masm, &slow); - __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset)); - __ jmp(&finish_object_store); + KeyedStoreGenerateGenericHelper(masm, &fast_object, &fast_double, + &slow, kCheckMap, kDontIncrementLength); + KeyedStoreGenerateGenericHelper(masm, &fast_object_grow, &fast_double_grow, + &slow, kDontCheckMap, kIncrementLength); } @@ -823,7 +851,7 @@ void CallICBase::GenerateMonomorphicCacheProbe(MacroAssembler* masm, Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, - NORMAL, + Code::NORMAL, argc); Isolate::Current()->stub_cache()->GenerateProbe(masm, flags, rdx, rcx, rbx, rax); @@ -1642,7 +1670,7 @@ void KeyedStoreIC::GenerateTransitionElementsSmiToDouble(MacroAssembler* masm) { // Must return the modified receiver in eax. if (!FLAG_trace_elements_transitions) { Label fail; - ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &fail); + ElementsTransitionGenerator::GenerateSmiToDouble(masm, &fail); __ movq(rax, rdx); __ Ret(); __ bind(&fail); diff --git a/src/3rdparty/v8/src/x64/lithium-codegen-x64.cc b/src/3rdparty/v8/src/x64/lithium-codegen-x64.cc index d34a520..a948ccc 100644 --- a/src/3rdparty/v8/src/x64/lithium-codegen-x64.cc +++ b/src/3rdparty/v8/src/x64/lithium-codegen-x64.cc @@ -92,17 +92,8 @@ void LCodeGen::FinishCode(Handle<Code> code) { } -void LCodeGen::Abort(const char* format, ...) { - if (FLAG_trace_bailout) { - SmartArrayPointer<char> name( - info()->shared_info()->DebugName()->ToCString()); - PrintF("Aborting LCodeGen in @\"%s\": ", *name); - va_list arguments; - va_start(arguments, format); - OS::VPrint(format, arguments); - va_end(arguments); - PrintF("\n"); - } +void LChunkBuilder::Abort(const char* reason) { + info()->set_bailout_reason(reason); status_ = ABORTED; } @@ -128,6 +119,8 @@ void LCodeGen::Comment(const char* format, ...) { bool LCodeGen::GeneratePrologue() { ASSERT(is_generating()); + ProfileEntryHookStub::MaybeCallEntryHook(masm_); + #ifdef DEBUG if (strlen(FLAG_stop_at) > 0 && info_->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) { @@ -140,6 +133,8 @@ bool LCodeGen::GeneratePrologue() { // object). rcx is zero for method calls and non-zero for function // calls. if (!info_->is_classic_mode() || info_->is_native()) { + Label begin; + __ bind(&begin); Label ok; __ testq(rcx, rcx); __ j(zero, &ok, Label::kNear); @@ -148,6 +143,8 @@ bool LCodeGen::GeneratePrologue() { __ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex); __ movq(Operand(rsp, receiver_offset), kScratchRegister); __ bind(&ok); + ASSERT(!FLAG_age_code || + (kSizeOfOptimizedStrictModePrologue == ok.pos() - begin.pos())); } __ push(rbp); // Caller's frame pointer. @@ -321,24 +318,24 @@ bool LCodeGen::IsTaggedConstant(LConstantOperand* op) const { int LCodeGen::ToInteger32(LConstantOperand* op) const { - Handle<Object> value = chunk_->LookupLiteral(op); + HConstant* constant = chunk_->LookupConstant(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()); + ASSERT(constant->HasInteger32Value()); + return constant->Integer32Value(); } double LCodeGen::ToDouble(LConstantOperand* op) const { - Handle<Object> value = chunk_->LookupLiteral(op); - return value->Number(); + HConstant* constant = chunk_->LookupConstant(op); + ASSERT(constant->HasDoubleValue()); + return constant->DoubleValue(); } Handle<Object> LCodeGen::ToHandle(LConstantOperand* op) const { - Handle<Object> literal = chunk_->LookupLiteral(op); + HConstant* constant = chunk_->LookupConstant(op); ASSERT(chunk_->LookupLiteralRepresentation(op).IsTagged()); - return literal; + return constant->handle(); } @@ -359,7 +356,9 @@ Operand LCodeGen::ToOperand(LOperand* op) const { void LCodeGen::WriteTranslation(LEnvironment* environment, - Translation* translation) { + Translation* translation, + int* arguments_index, + int* arguments_count) { if (environment == NULL) return; // The translation includes one command per value in the environment. @@ -367,8 +366,21 @@ void LCodeGen::WriteTranslation(LEnvironment* environment, // 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()); + // Function parameters are arguments to the outermost environment. The + // arguments index points to the first element of a sequence of tagged + // values on the stack that represent the arguments. This needs to be + // kept in sync with the LArgumentsElements implementation. + *arguments_index = -environment->parameter_count(); + *arguments_count = environment->parameter_count(); + + WriteTranslation(environment->outer(), + translation, + arguments_index, + arguments_count); + int closure_id = *info()->closure() != *environment->closure() + ? DefineDeoptimizationLiteral(environment->closure()) + : Translation::kSelfLiteralId; + switch (environment->frame_type()) { case JS_FUNCTION: translation->BeginJSFrame(environment->ast_id(), closure_id, height); @@ -376,12 +388,31 @@ void LCodeGen::WriteTranslation(LEnvironment* environment, case JS_CONSTRUCT: translation->BeginConstructStubFrame(closure_id, translation_size); break; + case JS_GETTER: + ASSERT(translation_size == 1); + ASSERT(height == 0); + translation->BeginGetterStubFrame(closure_id); + break; + case JS_SETTER: + ASSERT(translation_size == 2); + ASSERT(height == 0); + translation->BeginSetterStubFrame(closure_id); + break; case ARGUMENTS_ADAPTOR: translation->BeginArgumentsAdaptorFrame(closure_id, translation_size); break; - default: - UNREACHABLE(); } + + // Inlined frames which push their arguments cause the index to be + // bumped and a new stack area to be used for materialization. + if (environment->entry() != NULL && + environment->entry()->arguments_pushed()) { + *arguments_index = *arguments_index < 0 + ? GetStackSlotCount() + : *arguments_index + *arguments_count; + *arguments_count = environment->entry()->arguments_count() + 1; + } + for (int i = 0; i < translation_size; ++i) { LOperand* value = environment->values()->at(i); // spilled_registers_ and spilled_double_registers_ are either @@ -392,7 +423,10 @@ void LCodeGen::WriteTranslation(LEnvironment* environment, translation->MarkDuplicate(); AddToTranslation(translation, environment->spilled_registers()[value->index()], - environment->HasTaggedValueAt(i)); + environment->HasTaggedValueAt(i), + environment->HasUint32ValueAt(i), + *arguments_index, + *arguments_count); } else if ( value->IsDoubleRegister() && environment->spilled_double_registers()[value->index()] != NULL) { @@ -400,26 +434,39 @@ void LCodeGen::WriteTranslation(LEnvironment* environment, AddToTranslation( translation, environment->spilled_double_registers()[value->index()], - false); + false, + false, + *arguments_index, + *arguments_count); } } - AddToTranslation(translation, value, environment->HasTaggedValueAt(i)); + AddToTranslation(translation, + value, + environment->HasTaggedValueAt(i), + environment->HasUint32ValueAt(i), + *arguments_index, + *arguments_count); } } void LCodeGen::AddToTranslation(Translation* translation, LOperand* op, - bool is_tagged) { + bool is_tagged, + bool is_uint32, + int arguments_index, + int arguments_count) { 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(); + translation->StoreArgumentsObject(arguments_index, arguments_count); } else if (op->IsStackSlot()) { if (is_tagged) { translation->StoreStackSlot(op->index()); + } else if (is_uint32) { + translation->StoreUint32StackSlot(op->index()); } else { translation->StoreInt32StackSlot(op->index()); } @@ -433,6 +480,8 @@ void LCodeGen::AddToTranslation(Translation* translation, Register reg = ToRegister(op); if (is_tagged) { translation->StoreRegister(reg); + } else if (is_uint32) { + translation->StoreUint32Register(reg); } else { translation->StoreInt32Register(reg); } @@ -440,8 +489,8 @@ void LCodeGen::AddToTranslation(Translation* translation, XMMRegister reg = ToDoubleRegister(op); translation->StoreDoubleRegister(reg); } else if (op->IsConstantOperand()) { - Handle<Object> literal = chunk()->LookupLiteral(LConstantOperand::cast(op)); - int src_index = DefineDeoptimizationLiteral(literal); + HConstant* constant = chunk()->LookupConstant(LConstantOperand::cast(op)); + int src_index = DefineDeoptimizationLiteral(constant->handle()); translation->StoreLiteral(src_index); } else { UNREACHABLE(); @@ -518,20 +567,22 @@ void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment, int frame_count = 0; int jsframe_count = 0; + int args_index = 0; + int args_count = 0; for (LEnvironment* e = environment; e != NULL; e = e->outer()) { ++frame_count; if (e->frame_type() == JS_FUNCTION) { ++jsframe_count; } } - Translation translation(&translations_, frame_count, jsframe_count); - WriteTranslation(environment, &translation); + Translation translation(&translations_, frame_count, jsframe_count, zone()); + WriteTranslation(environment, &translation, &args_index, &args_count); int deoptimization_index = deoptimizations_.length(); int pc_offset = masm()->pc_offset(); environment->Register(deoptimization_index, translation.index(), (mode == Safepoint::kLazyDeopt) ? pc_offset : -1); - deoptimizations_.Add(environment); + deoptimizations_.Add(environment, environment->zone()); } } @@ -553,7 +604,7 @@ void LCodeGen::DeoptimizeIf(Condition cc, LEnvironment* environment) { // jump entry if this is the case. if (jump_table_.is_empty() || jump_table_.last().address != entry) { - jump_table_.Add(JumpTableEntry(entry)); + jump_table_.Add(JumpTableEntry(entry), zone()); } __ j(cc, &jump_table_.last().label); } @@ -577,13 +628,13 @@ void LCodeGen::PopulateDeoptimizationData(Handle<Code> code) { } data->SetLiteralArray(*literals); - data->SetOsrAstId(Smi::FromInt(info_->osr_ast_id())); + data->SetOsrAstId(Smi::FromInt(info_->osr_ast_id().ToInt())); 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->SetAstId(i, env->ast_id()); data->SetTranslationIndex(i, Smi::FromInt(env->translation_index())); data->SetArgumentsStackHeight(i, Smi::FromInt(env->arguments_stack_height())); @@ -598,7 +649,7 @@ int LCodeGen::DefineDeoptimizationLiteral(Handle<Object> literal) { for (int i = 0; i < deoptimization_literals_.length(); ++i) { if (deoptimization_literals_[i].is_identical_to(literal)) return i; } - deoptimization_literals_.Add(literal); + deoptimization_literals_.Add(literal, zone()); return result; } @@ -645,14 +696,14 @@ void LCodeGen::RecordSafepoint( for (int i = 0; i < operands->length(); i++) { LOperand* pointer = operands->at(i); if (pointer->IsStackSlot()) { - safepoint.DefinePointerSlot(pointer->index()); + safepoint.DefinePointerSlot(pointer->index(), zone()); } else if (pointer->IsRegister() && (kind & Safepoint::kWithRegisters)) { - safepoint.DefinePointerRegister(ToRegister(pointer)); + safepoint.DefinePointerRegister(ToRegister(pointer), zone()); } } if (kind & Safepoint::kWithRegisters) { // Register rsi always contains a pointer to the context. - safepoint.DefinePointerRegister(rsi); + safepoint.DefinePointerRegister(rsi, zone()); } } @@ -664,7 +715,7 @@ void LCodeGen::RecordSafepoint(LPointerMap* pointers, void LCodeGen::RecordSafepoint(Safepoint::DeoptMode deopt_mode) { - LPointerMap empty_pointers(RelocInfo::kNoPosition); + LPointerMap empty_pointers(RelocInfo::kNoPosition, zone()); RecordSafepoint(&empty_pointers, deopt_mode); } @@ -772,7 +823,7 @@ void LCodeGen::DoUnknownOSRValue(LUnknownOSRValue* instr) { void LCodeGen::DoModI(LModI* instr) { if (instr->hydrogen()->HasPowerOf2Divisor()) { - Register dividend = ToRegister(instr->InputAt(0)); + Register dividend = ToRegister(instr->left()); int32_t divisor = HConstant::cast(instr->hydrogen()->right())->Integer32Value(); @@ -796,8 +847,8 @@ void LCodeGen::DoModI(LModI* instr) { __ bind(&done); } else { Label done, remainder_eq_dividend, slow, do_subtraction, both_positive; - Register left_reg = ToRegister(instr->InputAt(0)); - Register right_reg = ToRegister(instr->InputAt(1)); + Register left_reg = ToRegister(instr->left()); + Register right_reg = ToRegister(instr->right()); Register result_reg = ToRegister(instr->result()); ASSERT(left_reg.is(rax)); @@ -827,7 +878,7 @@ void LCodeGen::DoModI(LModI* instr) { __ j(less, &remainder_eq_dividend, Label::kNear); // Check if the divisor is a PowerOfTwo integer. - Register scratch = ToRegister(instr->TempAt(0)); + Register scratch = ToRegister(instr->temp()); __ movl(scratch, right_reg); __ subl(scratch, Immediate(1)); __ testl(scratch, right_reg); @@ -883,12 +934,95 @@ void LCodeGen::DoModI(LModI* instr) { } +void LCodeGen::DoMathFloorOfDiv(LMathFloorOfDiv* instr) { + ASSERT(instr->right()->IsConstantOperand()); + + const Register dividend = ToRegister(instr->left()); + int32_t divisor = ToInteger32(LConstantOperand::cast(instr->right())); + const Register result = ToRegister(instr->result()); + + switch (divisor) { + case 0: + DeoptimizeIf(no_condition, instr->environment()); + return; + + case 1: + if (!result.is(dividend)) { + __ movl(result, dividend); + } + return; + + case -1: + if (!result.is(dividend)) { + __ movl(result, dividend); + } + __ negl(result); + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + DeoptimizeIf(zero, instr->environment()); + } + if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) { + DeoptimizeIf(overflow, instr->environment()); + } + return; + } + + uint32_t divisor_abs = abs(divisor); + if (IsPowerOf2(divisor_abs)) { + int32_t power = WhichPowerOf2(divisor_abs); + if (divisor < 0) { + __ movsxlq(result, dividend); + __ neg(result); + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + DeoptimizeIf(zero, instr->environment()); + } + __ sar(result, Immediate(power)); + } else { + if (!result.is(dividend)) { + __ movl(result, dividend); + } + __ sarl(result, Immediate(power)); + } + } else { + Register reg1 = ToRegister(instr->temp()); + Register reg2 = ToRegister(instr->result()); + + // Find b which: 2^b < divisor_abs < 2^(b+1). + unsigned b = 31 - CompilerIntrinsics::CountLeadingZeros(divisor_abs); + unsigned shift = 32 + b; // Precision +1bit (effectively). + double multiplier_f = + static_cast<double>(static_cast<uint64_t>(1) << shift) / divisor_abs; + int64_t multiplier; + if (multiplier_f - floor(multiplier_f) < 0.5) { + multiplier = static_cast<int64_t>(floor(multiplier_f)); + } else { + multiplier = static_cast<int64_t>(floor(multiplier_f)) + 1; + } + // The multiplier is a uint32. + ASSERT(multiplier > 0 && + multiplier < (static_cast<int64_t>(1) << 32)); + // The multiply is int64, so sign-extend to r64. + __ movsxlq(reg1, dividend); + if (divisor < 0 && + instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + __ neg(reg1); + DeoptimizeIf(zero, instr->environment()); + } + __ movq(reg2, multiplier, RelocInfo::NONE); + // Result just fit in r64, because it's int32 * uint32. + __ imul(reg2, reg1); + + __ addq(reg2, Immediate(1 << 30)); + __ sar(reg2, Immediate(shift)); + } +} + + void LCodeGen::DoDivI(LDivI* instr) { - LOperand* right = instr->InputAt(1); + LOperand* right = instr->right(); ASSERT(ToRegister(instr->result()).is(rax)); - ASSERT(ToRegister(instr->InputAt(0)).is(rax)); - ASSERT(!ToRegister(instr->InputAt(1)).is(rax)); - ASSERT(!ToRegister(instr->InputAt(1)).is(rdx)); + ASSERT(ToRegister(instr->left()).is(rax)); + ASSERT(!ToRegister(instr->right()).is(rax)); + ASSERT(!ToRegister(instr->right()).is(rdx)); Register left_reg = rax; @@ -930,8 +1064,8 @@ void LCodeGen::DoDivI(LDivI* instr) { void LCodeGen::DoMulI(LMulI* instr) { - Register left = ToRegister(instr->InputAt(0)); - LOperand* right = instr->InputAt(1); + Register left = ToRegister(instr->left()); + LOperand* right = instr->right(); if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { __ movl(kScratchRegister, left); @@ -996,8 +1130,11 @@ void LCodeGen::DoMulI(LMulI* instr) { __ testl(left, left); __ j(not_zero, &done, Label::kNear); if (right->IsConstantOperand()) { - if (ToInteger32(LConstantOperand::cast(right)) <= 0) { + if (ToInteger32(LConstantOperand::cast(right)) < 0) { DeoptimizeIf(no_condition, instr->environment()); + } else if (ToInteger32(LConstantOperand::cast(right)) == 0) { + __ cmpl(kScratchRegister, Immediate(0)); + DeoptimizeIf(less, instr->environment()); } } else if (right->IsStackSlot()) { __ orl(kScratchRegister, ToOperand(right)); @@ -1013,8 +1150,8 @@ void LCodeGen::DoMulI(LMulI* instr) { void LCodeGen::DoBitI(LBitI* instr) { - LOperand* left = instr->InputAt(0); - LOperand* right = instr->InputAt(1); + LOperand* left = instr->left(); + LOperand* right = instr->right(); ASSERT(left->Equals(instr->result())); ASSERT(left->IsRegister()); @@ -1070,14 +1207,17 @@ void LCodeGen::DoBitI(LBitI* instr) { void LCodeGen::DoShiftI(LShiftI* instr) { - LOperand* left = instr->InputAt(0); - LOperand* right = instr->InputAt(1); + LOperand* left = instr->left(); + LOperand* right = instr->right(); ASSERT(left->Equals(instr->result())); ASSERT(left->IsRegister()); if (right->IsRegister()) { ASSERT(ToRegister(right).is(rcx)); switch (instr->op()) { + case Token::ROR: + __ rorl_cl(ToRegister(left)); + break; case Token::SAR: __ sarl_cl(ToRegister(left)); break; @@ -1099,6 +1239,11 @@ void LCodeGen::DoShiftI(LShiftI* instr) { int value = ToInteger32(LConstantOperand::cast(right)); uint8_t shift_count = static_cast<uint8_t>(value & 0x1F); switch (instr->op()) { + case Token::ROR: + if (shift_count != 0) { + __ rorl(ToRegister(left), Immediate(shift_count)); + } + break; case Token::SAR: if (shift_count != 0) { __ sarl(ToRegister(left), Immediate(shift_count)); @@ -1126,8 +1271,8 @@ void LCodeGen::DoShiftI(LShiftI* instr) { void LCodeGen::DoSubI(LSubI* instr) { - LOperand* left = instr->InputAt(0); - LOperand* right = instr->InputAt(1); + LOperand* left = instr->left(); + LOperand* right = instr->right(); ASSERT(left->Equals(instr->result())); if (right->IsConstantOperand()) { @@ -1161,7 +1306,7 @@ void LCodeGen::DoConstantD(LConstantD* instr) { if (int_val == 0) { __ xorps(res, res); } else { - Register tmp = ToRegister(instr->TempAt(0)); + Register tmp = ToRegister(instr->temp()); __ Set(tmp, int_val); __ movq(res, tmp); } @@ -1181,21 +1326,28 @@ void LCodeGen::DoConstantT(LConstantT* instr) { void LCodeGen::DoJSArrayLength(LJSArrayLength* instr) { Register result = ToRegister(instr->result()); - Register array = ToRegister(instr->InputAt(0)); + Register array = ToRegister(instr->value()); __ movq(result, FieldOperand(array, JSArray::kLengthOffset)); } void LCodeGen::DoFixedArrayBaseLength(LFixedArrayBaseLength* instr) { Register result = ToRegister(instr->result()); - Register array = ToRegister(instr->InputAt(0)); + Register array = ToRegister(instr->value()); __ movq(result, FieldOperand(array, FixedArrayBase::kLengthOffset)); } +void LCodeGen::DoMapEnumLength(LMapEnumLength* instr) { + Register result = ToRegister(instr->result()); + Register map = ToRegister(instr->value()); + __ EnumLength(result, map); +} + + void LCodeGen::DoElementsKind(LElementsKind* instr) { Register result = ToRegister(instr->result()); - Register input = ToRegister(instr->InputAt(0)); + Register input = ToRegister(instr->value()); // Load map into |result|. __ movq(result, FieldOperand(input, HeapObject::kMapOffset)); @@ -1208,7 +1360,7 @@ void LCodeGen::DoElementsKind(LElementsKind* instr) { void LCodeGen::DoValueOf(LValueOf* instr) { - Register input = ToRegister(instr->InputAt(0)); + Register input = ToRegister(instr->value()); Register result = ToRegister(instr->result()); ASSERT(input.is(result)); Label done; @@ -1225,18 +1377,17 @@ void LCodeGen::DoValueOf(LValueOf* instr) { void LCodeGen::DoDateField(LDateField* instr) { - Register object = ToRegister(instr->InputAt(0)); + Register object = ToRegister(instr->date()); Register result = ToRegister(instr->result()); Smi* index = instr->index(); - Label runtime, done; + Label runtime, done, not_date_object; ASSERT(object.is(result)); ASSERT(object.is(rax)); -#ifdef DEBUG - __ AbortIfSmi(object); + Condition cc = masm()->CheckSmi(object); + DeoptimizeIf(cc, instr->environment()); __ CmpObjectType(object, JS_DATE_TYPE, kScratchRegister); - __ Assert(equal, "Trying to get date field from non-date."); -#endif + DeoptimizeIf(not_equal, instr->environment()); if (index->value() == 0) { __ movq(result, FieldOperand(object, JSDate::kValueOffset)); @@ -1268,14 +1419,14 @@ void LCodeGen::DoDateField(LDateField* instr) { void LCodeGen::DoBitNotI(LBitNotI* instr) { - LOperand* input = instr->InputAt(0); + LOperand* input = instr->value(); ASSERT(input->Equals(instr->result())); __ not_(ToRegister(input)); } void LCodeGen::DoThrow(LThrow* instr) { - __ push(ToRegister(instr->InputAt(0))); + __ push(ToRegister(instr->value())); CallRuntime(Runtime::kThrow, 1, instr); if (FLAG_debug_code) { @@ -1286,8 +1437,8 @@ void LCodeGen::DoThrow(LThrow* instr) { void LCodeGen::DoAddI(LAddI* instr) { - LOperand* left = instr->InputAt(0); - LOperand* right = instr->InputAt(1); + LOperand* left = instr->left(); + LOperand* right = instr->right(); ASSERT(left->Equals(instr->result())); if (right->IsConstantOperand()) { @@ -1305,9 +1456,75 @@ void LCodeGen::DoAddI(LAddI* instr) { } +void LCodeGen::DoMathMinMax(LMathMinMax* instr) { + LOperand* left = instr->left(); + LOperand* right = instr->right(); + ASSERT(left->Equals(instr->result())); + HMathMinMax::Operation operation = instr->hydrogen()->operation(); + if (instr->hydrogen()->representation().IsInteger32()) { + Label return_left; + Condition condition = (operation == HMathMinMax::kMathMin) + ? less_equal + : greater_equal; + Register left_reg = ToRegister(left); + if (right->IsConstantOperand()) { + Immediate right_imm = + Immediate(ToInteger32(LConstantOperand::cast(right))); + __ cmpq(left_reg, right_imm); + __ j(condition, &return_left, Label::kNear); + __ movq(left_reg, right_imm); + } else if (right->IsRegister()) { + Register right_reg = ToRegister(right); + __ cmpq(left_reg, right_reg); + __ j(condition, &return_left, Label::kNear); + __ movq(left_reg, right_reg); + } else { + Operand right_op = ToOperand(right); + __ cmpq(left_reg, right_op); + __ j(condition, &return_left, Label::kNear); + __ movq(left_reg, right_op); + } + __ bind(&return_left); + } else { + ASSERT(instr->hydrogen()->representation().IsDouble()); + Label check_nan_left, check_zero, return_left, return_right; + Condition condition = (operation == HMathMinMax::kMathMin) ? below : above; + XMMRegister left_reg = ToDoubleRegister(left); + XMMRegister right_reg = ToDoubleRegister(right); + __ ucomisd(left_reg, right_reg); + __ j(parity_even, &check_nan_left, Label::kNear); // At least one NaN. + __ j(equal, &check_zero, Label::kNear); // left == right. + __ j(condition, &return_left, Label::kNear); + __ jmp(&return_right, Label::kNear); + + __ bind(&check_zero); + XMMRegister xmm_scratch = xmm0; + __ xorps(xmm_scratch, xmm_scratch); + __ ucomisd(left_reg, xmm_scratch); + __ j(not_equal, &return_left, Label::kNear); // left == right != 0. + // At this point, both left and right are either 0 or -0. + if (operation == HMathMinMax::kMathMin) { + __ orpd(left_reg, right_reg); + } else { + // Since we operate on +0 and/or -0, addsd and andsd have the same effect. + __ addsd(left_reg, right_reg); + } + __ jmp(&return_left, Label::kNear); + + __ bind(&check_nan_left); + __ ucomisd(left_reg, left_reg); // NaN check. + __ j(parity_even, &return_left, Label::kNear); + __ bind(&return_right); + __ movsd(left_reg, right_reg); + + __ bind(&return_left); + } +} + + void LCodeGen::DoArithmeticD(LArithmeticD* instr) { - XMMRegister left = ToDoubleRegister(instr->InputAt(0)); - XMMRegister right = ToDoubleRegister(instr->InputAt(1)); + XMMRegister left = ToDoubleRegister(instr->left()); + XMMRegister right = ToDoubleRegister(instr->right()); XMMRegister result = ToDoubleRegister(instr->result()); // All operations except MOD are computed in-place. ASSERT(instr->op() == Token::MOD || left.is(result)); @@ -1341,8 +1558,8 @@ void LCodeGen::DoArithmeticD(LArithmeticD* instr) { void LCodeGen::DoArithmeticT(LArithmeticT* instr) { - ASSERT(ToRegister(instr->InputAt(0)).is(rdx)); - ASSERT(ToRegister(instr->InputAt(1)).is(rax)); + ASSERT(ToRegister(instr->left()).is(rdx)); + ASSERT(ToRegister(instr->right()).is(rax)); ASSERT(ToRegister(instr->result()).is(rax)); BinaryOpStub stub(instr->op(), NO_OVERWRITE); @@ -1386,17 +1603,17 @@ void LCodeGen::DoBranch(LBranch* instr) { Representation r = instr->hydrogen()->value()->representation(); if (r.IsInteger32()) { - Register reg = ToRegister(instr->InputAt(0)); + Register reg = ToRegister(instr->value()); __ testl(reg, reg); EmitBranch(true_block, false_block, not_zero); } else if (r.IsDouble()) { - XMMRegister reg = ToDoubleRegister(instr->InputAt(0)); + XMMRegister reg = ToDoubleRegister(instr->value()); __ xorps(xmm0, xmm0); __ ucomisd(reg, xmm0); EmitBranch(true_block, false_block, not_equal); } else { ASSERT(r.IsTagged()); - Register reg = ToRegister(instr->InputAt(0)); + Register reg = ToRegister(instr->value()); HType type = instr->hydrogen()->value()->type(); if (type.IsBoolean()) { __ CompareRoot(reg, Heap::kTrueValueRootIndex); @@ -1533,8 +1750,8 @@ inline Condition LCodeGen::TokenToCondition(Token::Value op, bool is_unsigned) { void LCodeGen::DoCmpIDAndBranch(LCmpIDAndBranch* instr) { - LOperand* left = instr->InputAt(0); - LOperand* right = instr->InputAt(1); + LOperand* left = instr->left(); + LOperand* right = instr->right(); int false_block = chunk_->LookupDestination(instr->false_block_id()); int true_block = chunk_->LookupDestination(instr->true_block_id()); Condition cc = TokenToCondition(instr->op(), instr->is_double()); @@ -1581,8 +1798,8 @@ void LCodeGen::DoCmpIDAndBranch(LCmpIDAndBranch* instr) { void LCodeGen::DoCmpObjectEqAndBranch(LCmpObjectEqAndBranch* instr) { - Register left = ToRegister(instr->InputAt(0)); - Register right = ToRegister(instr->InputAt(1)); + Register left = ToRegister(instr->left()); + Register right = ToRegister(instr->right()); int false_block = chunk_->LookupDestination(instr->false_block_id()); int true_block = chunk_->LookupDestination(instr->true_block_id()); @@ -1592,7 +1809,7 @@ void LCodeGen::DoCmpObjectEqAndBranch(LCmpObjectEqAndBranch* instr) { void LCodeGen::DoCmpConstantEqAndBranch(LCmpConstantEqAndBranch* instr) { - Register left = ToRegister(instr->InputAt(0)); + Register left = ToRegister(instr->left()); int true_block = chunk_->LookupDestination(instr->true_block_id()); int false_block = chunk_->LookupDestination(instr->false_block_id()); @@ -1602,7 +1819,7 @@ void LCodeGen::DoCmpConstantEqAndBranch(LCmpConstantEqAndBranch* instr) { void LCodeGen::DoIsNilAndBranch(LIsNilAndBranch* instr) { - Register reg = ToRegister(instr->InputAt(0)); + Register reg = ToRegister(instr->value()); int false_block = chunk_->LookupDestination(instr->false_block_id()); // If the expression is known to be untagged or a smi, then it's definitely @@ -1632,7 +1849,7 @@ void LCodeGen::DoIsNilAndBranch(LIsNilAndBranch* instr) { __ JumpIfSmi(reg, 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->TempAt(0)); + Register scratch = ToRegister(instr->temp()); __ movq(scratch, FieldOperand(reg, HeapObject::kMapOffset)); __ testb(FieldOperand(scratch, Map::kBitFieldOffset), Immediate(1 << Map::kIsUndetectable)); @@ -1667,7 +1884,7 @@ Condition LCodeGen::EmitIsObject(Register input, void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) { - Register reg = ToRegister(instr->InputAt(0)); + Register reg = ToRegister(instr->value()); int true_block = chunk_->LookupDestination(instr->true_block_id()); int false_block = chunk_->LookupDestination(instr->false_block_id()); @@ -1691,8 +1908,8 @@ Condition LCodeGen::EmitIsString(Register input, void LCodeGen::DoIsStringAndBranch(LIsStringAndBranch* instr) { - Register reg = ToRegister(instr->InputAt(0)); - Register temp = ToRegister(instr->TempAt(0)); + Register reg = ToRegister(instr->value()); + Register temp = ToRegister(instr->temp()); int true_block = chunk_->LookupDestination(instr->true_block_id()); int false_block = chunk_->LookupDestination(instr->false_block_id()); @@ -1709,11 +1926,11 @@ void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) { int false_block = chunk_->LookupDestination(instr->false_block_id()); Condition is_smi; - if (instr->InputAt(0)->IsRegister()) { - Register input = ToRegister(instr->InputAt(0)); + if (instr->value()->IsRegister()) { + Register input = ToRegister(instr->value()); is_smi = masm()->CheckSmi(input); } else { - Operand input = ToOperand(instr->InputAt(0)); + Operand input = ToOperand(instr->value()); is_smi = masm()->CheckSmi(input); } EmitBranch(true_block, false_block, is_smi); @@ -1721,8 +1938,8 @@ void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) { void LCodeGen::DoIsUndetectableAndBranch(LIsUndetectableAndBranch* instr) { - Register input = ToRegister(instr->InputAt(0)); - Register temp = ToRegister(instr->TempAt(0)); + Register input = ToRegister(instr->value()); + Register temp = ToRegister(instr->temp()); int true_block = chunk_->LookupDestination(instr->true_block_id()); int false_block = chunk_->LookupDestination(instr->false_block_id()); @@ -1771,7 +1988,7 @@ static Condition BranchCondition(HHasInstanceTypeAndBranch* instr) { void LCodeGen::DoHasInstanceTypeAndBranch(LHasInstanceTypeAndBranch* instr) { - Register input = ToRegister(instr->InputAt(0)); + Register input = ToRegister(instr->value()); int true_block = chunk_->LookupDestination(instr->true_block_id()); int false_block = chunk_->LookupDestination(instr->false_block_id()); @@ -1786,12 +2003,10 @@ void LCodeGen::DoHasInstanceTypeAndBranch(LHasInstanceTypeAndBranch* instr) { void LCodeGen::DoGetCachedArrayIndex(LGetCachedArrayIndex* instr) { - Register input = ToRegister(instr->InputAt(0)); + Register input = ToRegister(instr->value()); Register result = ToRegister(instr->result()); - if (FLAG_debug_code) { - __ AbortIfNotString(input); - } + __ AssertString(input); __ movl(result, FieldOperand(input, String::kHashFieldOffset)); ASSERT(String::kHashShift >= kSmiTagSize); @@ -1801,7 +2016,7 @@ void LCodeGen::DoGetCachedArrayIndex(LGetCachedArrayIndex* instr) { void LCodeGen::DoHasCachedArrayIndexAndBranch( LHasCachedArrayIndexAndBranch* instr) { - Register input = ToRegister(instr->InputAt(0)); + Register input = ToRegister(instr->value()); int true_block = chunk_->LookupDestination(instr->true_block_id()); int false_block = chunk_->LookupDestination(instr->false_block_id()); @@ -1881,9 +2096,9 @@ void LCodeGen::EmitClassOfTest(Label* is_true, void LCodeGen::DoClassOfTestAndBranch(LClassOfTestAndBranch* instr) { - Register input = ToRegister(instr->InputAt(0)); - Register temp = ToRegister(instr->TempAt(0)); - Register temp2 = ToRegister(instr->TempAt(1)); + Register input = ToRegister(instr->value()); + Register temp = ToRegister(instr->temp()); + Register temp2 = ToRegister(instr->temp2()); Handle<String> class_name = instr->hydrogen()->class_name(); int true_block = chunk_->LookupDestination(instr->true_block_id()); @@ -1899,7 +2114,7 @@ void LCodeGen::DoClassOfTestAndBranch(LClassOfTestAndBranch* instr) { void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) { - Register reg = ToRegister(instr->InputAt(0)); + Register reg = ToRegister(instr->value()); int true_block = instr->true_block_id(); int false_block = instr->false_block_id(); @@ -1910,8 +2125,8 @@ void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) { void LCodeGen::DoInstanceOf(LInstanceOf* instr) { InstanceofStub stub(InstanceofStub::kNoFlags); - __ push(ToRegister(instr->InputAt(0))); - __ push(ToRegister(instr->InputAt(1))); + __ push(ToRegister(instr->left())); + __ push(ToRegister(instr->right())); CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); Label true_value, done; __ testq(rax, rax); @@ -1942,10 +2157,10 @@ void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) { DeferredInstanceOfKnownGlobal* deferred; - deferred = new DeferredInstanceOfKnownGlobal(this, instr); + deferred = new(zone()) DeferredInstanceOfKnownGlobal(this, instr); Label done, false_result; - Register object = ToRegister(instr->InputAt(0)); + Register object = ToRegister(instr->value()); // A Smi is not an instance of anything. __ JumpIfSmi(object, &false_result); @@ -1955,7 +2170,7 @@ void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) { // instanceof stub. Label cache_miss; // Use a temp register to avoid memory operands with variable lengths. - Register map = ToRegister(instr->TempAt(0)); + Register map = ToRegister(instr->temp()); __ movq(map, FieldOperand(object, HeapObject::kMapOffset)); __ bind(deferred->map_check()); // Label for calculating code patching. Handle<JSGlobalPropertyCell> cache_cell = @@ -1998,7 +2213,7 @@ void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, InstanceofStub::kNoFlags | InstanceofStub::kCallSiteInlineCheck); InstanceofStub stub(flags); - __ push(ToRegister(instr->InputAt(0))); + __ push(ToRegister(instr->value())); __ PushHeapObject(instr->function()); static const int kAdditionalDelta = 10; @@ -2098,7 +2313,7 @@ void LCodeGen::DoStoreGlobalCell(LStoreGlobalCell* instr) { // it as no longer deleted. We deoptimize in that case. if (instr->hydrogen()->RequiresHoleCheck()) { // We have a temp because CompareRoot might clobber kScratchRegister. - Register cell = ToRegister(instr->TempAt(0)); + Register cell = ToRegister(instr->temp()); ASSERT(!value.is(cell)); __ movq(cell, cell_handle, RelocInfo::GLOBAL_PROPERTY_CELL); __ CompareRoot(Operand(cell, 0), Heap::kTheHoleValueRootIndex); @@ -2166,7 +2381,7 @@ void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) { SmiCheck check_needed = type.IsHeapObject() ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; int offset = Context::SlotOffset(instr->slot_index()); - Register scratch = ToRegister(instr->TempAt(0)); + Register scratch = ToRegister(instr->temp()); __ RecordWriteContextSlot(context, offset, value, @@ -2181,7 +2396,7 @@ void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) { void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) { - Register object = ToRegister(instr->InputAt(0)); + Register object = ToRegister(instr->object()); Register result = ToRegister(instr->result()); if (instr->hydrogen()->is_in_object()) { __ movq(result, FieldOperand(object, instr->hydrogen()->offset())); @@ -2195,12 +2410,12 @@ void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) { void LCodeGen::EmitLoadFieldOrConstantFunction(Register result, Register object, Handle<Map> type, - Handle<String> name) { + Handle<String> name, + LEnvironment* env) { LookupResult lookup(isolate()); - type->LookupInDescriptors(NULL, *name, &lookup); - ASSERT(lookup.IsFound() && - (lookup.type() == FIELD || lookup.type() == CONSTANT_FUNCTION)); - if (lookup.type() == FIELD) { + type->LookupDescriptor(NULL, *name, &lookup); + ASSERT(lookup.IsFound() || lookup.IsCacheable()); + if (lookup.IsField()) { int index = lookup.GetLocalFieldIndexFromMap(*type); int offset = index * kPointerSize; if (index < 0) { @@ -2212,13 +2427,43 @@ void LCodeGen::EmitLoadFieldOrConstantFunction(Register result, __ movq(result, FieldOperand(object, JSObject::kPropertiesOffset)); __ movq(result, FieldOperand(result, offset + FixedArray::kHeaderSize)); } - } else { + } else if (lookup.IsConstantFunction()) { Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*type)); __ LoadHeapObject(result, function); + } else { + // Negative lookup. + // Check prototypes. + Handle<HeapObject> current(HeapObject::cast((*type)->prototype())); + Heap* heap = type->GetHeap(); + while (*current != heap->null_value()) { + __ LoadHeapObject(result, current); + __ Cmp(FieldOperand(result, HeapObject::kMapOffset), + Handle<Map>(current->map())); + DeoptimizeIf(not_equal, env); + current = + Handle<HeapObject>(HeapObject::cast(current->map()->prototype())); + } + __ LoadRoot(result, Heap::kUndefinedValueRootIndex); } } +// Check for cases where EmitLoadFieldOrConstantFunction needs to walk the +// prototype chain, which causes unbounded code generation. +static bool CompactEmit(SmallMapList* list, + Handle<String> name, + int i, + Isolate* isolate) { + Handle<Map> map = list->at(i); + // If the map has ElementsKind transitions, we will generate map checks + // for each kind in __ CompareMap(..., ALLOW_ELEMENTS_TRANSITION_MAPS). + if (map->HasElementsTransition()) return false; + LookupResult lookup(isolate); + map->LookupDescriptor(NULL, *name, &lookup); + return lookup.IsField() || lookup.IsConstantFunction(); +} + + void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) { Register object = ToRegister(instr->object()); Register result = ToRegister(instr->result()); @@ -2232,18 +2477,32 @@ void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) { } Handle<String> name = instr->hydrogen()->name(); Label done; + bool all_are_compact = true; + for (int i = 0; i < map_count; ++i) { + if (!CompactEmit(instr->hydrogen()->types(), name, i, isolate())) { + all_are_compact = false; + break; + } + } for (int i = 0; i < map_count; ++i) { bool last = (i == map_count - 1); Handle<Map> map = instr->hydrogen()->types()->at(i); - __ Cmp(FieldOperand(object, HeapObject::kMapOffset), map); + Label check_passed; + __ CompareMap(object, map, &check_passed, ALLOW_ELEMENT_TRANSITION_MAPS); if (last && !need_generic) { DeoptimizeIf(not_equal, instr->environment()); - EmitLoadFieldOrConstantFunction(result, object, map, name); + __ bind(&check_passed); + EmitLoadFieldOrConstantFunction( + result, object, map, name, instr->environment()); } else { Label next; - __ j(not_equal, &next, Label::kNear); - EmitLoadFieldOrConstantFunction(result, object, map, name); - __ jmp(&done, Label::kNear); + bool compact = all_are_compact ? true : + CompactEmit(instr->hydrogen()->types(), name, i, isolate()); + __ j(not_equal, &next, compact ? Label::kNear : Label::kFar); + __ bind(&check_passed); + EmitLoadFieldOrConstantFunction( + result, object, map, name, instr->environment()); + __ jmp(&done, all_are_compact ? Label::kNear : Label::kFar); __ bind(&next); } } @@ -2309,7 +2568,7 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) { void LCodeGen::DoLoadElements(LLoadElements* instr) { Register result = ToRegister(instr->result()); - Register input = ToRegister(instr->InputAt(0)); + Register input = ToRegister(instr->object()); __ movq(result, FieldOperand(input, JSObject::kElementsOffset)); if (FLAG_debug_code) { Label done, ok, fail; @@ -2325,8 +2584,10 @@ void LCodeGen::DoLoadElements(LLoadElements* instr) { __ movzxbq(temp, FieldOperand(temp, Map::kBitField2Offset)); __ and_(temp, Immediate(Map::kElementsKindMask)); __ shr(temp, Immediate(Map::kElementsKindShift)); - __ cmpl(temp, Immediate(FAST_ELEMENTS)); - __ j(equal, &ok, Label::kNear); + __ cmpl(temp, Immediate(GetInitialFastElementsKind())); + __ j(less, &fail, Label::kNear); + __ cmpl(temp, Immediate(TERMINAL_FAST_ELEMENTS_KIND)); + __ j(less_equal, &ok, Label::kNear); __ cmpl(temp, Immediate(FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND)); __ j(less, &fail, Label::kNear); __ cmpl(temp, Immediate(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND)); @@ -2343,7 +2604,7 @@ void LCodeGen::DoLoadElements(LLoadElements* instr) { void LCodeGen::DoLoadExternalArrayPointer( LLoadExternalArrayPointer* instr) { Register result = ToRegister(instr->result()); - Register input = ToRegister(instr->InputAt(0)); + Register input = ToRegister(instr->object()); __ movq(result, FieldOperand(input, ExternalPixelArray::kExternalPointerOffset)); } @@ -2353,118 +2614,47 @@ void LCodeGen::DoAccessArgumentsAt(LAccessArgumentsAt* instr) { Register arguments = ToRegister(instr->arguments()); Register length = ToRegister(instr->length()); Register result = ToRegister(instr->result()); - + // There are two words between the frame pointer and the last argument. + // Subtracting from length accounts for one of them add one more. if (instr->index()->IsRegister()) { __ subl(length, ToRegister(instr->index())); } else { __ subl(length, ToOperand(instr->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. __ movq(result, Operand(arguments, length, times_pointer_size, kPointerSize)); } -void LCodeGen::DoLoadKeyedFastElement(LLoadKeyedFastElement* instr) { - Register result = ToRegister(instr->result()); - - if (instr->hydrogen()->IsDehoisted() && !instr->key()->IsConstantOperand()) { - // Sign extend key because it could be a 32 bit negative value - // and the dehoisted address computation happens in 64 bits. - Register key_reg = ToRegister(instr->key()); - __ movsxlq(key_reg, key_reg); - } - - // Load the result. - __ movq(result, - BuildFastArrayOperand(instr->elements(), - instr->key(), - FAST_ELEMENTS, - FixedArray::kHeaderSize - kHeapObjectTag, - instr->additional_index())); - - // Check for the hole value. - if (instr->hydrogen()->RequiresHoleCheck()) { - __ CompareRoot(result, Heap::kTheHoleValueRootIndex); - DeoptimizeIf(equal, instr->environment()); - } -} - - -void LCodeGen::DoLoadKeyedFastDoubleElement( - LLoadKeyedFastDoubleElement* instr) { - XMMRegister result(ToDoubleRegister(instr->result())); - - if (instr->hydrogen()->IsDehoisted() && !instr->key()->IsConstantOperand()) { - // Sign extend key because it could be a 32 bit negative value - // and the dehoisted address computation happens in 64 bits - Register key_reg = ToRegister(instr->key()); - __ movsxlq(key_reg, key_reg); - } - - int offset = FixedDoubleArray::kHeaderSize - kHeapObjectTag + - sizeof(kHoleNanLower32); - Operand hole_check_operand = BuildFastArrayOperand( - instr->elements(), - instr->key(), - FAST_DOUBLE_ELEMENTS, - offset, - instr->additional_index()); - __ cmpl(hole_check_operand, Immediate(kHoleNanUpper32)); - DeoptimizeIf(equal, instr->environment()); - - Operand double_load_operand = BuildFastArrayOperand( - instr->elements(), - instr->key(), - FAST_DOUBLE_ELEMENTS, - FixedDoubleArray::kHeaderSize - kHeapObjectTag, - instr->additional_index()); - __ movsd(result, double_load_operand); -} - - -Operand LCodeGen::BuildFastArrayOperand( - LOperand* elements_pointer, - LOperand* key, - ElementsKind elements_kind, - uint32_t offset, - uint32_t additional_index) { - Register elements_pointer_reg = ToRegister(elements_pointer); - int shift_size = ElementsKindToShiftSize(elements_kind); - if (key->IsConstantOperand()) { - int constant_value = ToInteger32(LConstantOperand::cast(key)); - if (constant_value & 0xF0000000) { - Abort("array index constant value too big"); +template <class T> +inline void LCodeGen::PrepareKeyForKeyedOp(T* hydrogen_instr, LOperand* key) { + if (ArrayOpClobbersKey<T>(hydrogen_instr)) { + // Even though the HLoad/StoreKeyed (in this case) instructions force + // the input representation for the key to be an integer, the input + // gets replaced during bound check elimination with the index argument + // to the bounds check, which can be tagged, so that case must be + // handled here, too. + Register key_reg = ToRegister(key); + if (hydrogen_instr->key()->representation().IsTagged()) { + __ SmiToInteger64(key_reg, key_reg); + } else if (hydrogen_instr->IsDehoisted()) { + // Sign extend key because it could be a 32 bit negative value + // and the dehoisted address computation happens in 64 bits + __ movsxlq(key_reg, key_reg); } - return Operand(elements_pointer_reg, - ((constant_value + additional_index) << shift_size) - + offset); - } else { - ScaleFactor scale_factor = static_cast<ScaleFactor>(shift_size); - return Operand(elements_pointer_reg, - ToRegister(key), - scale_factor, - offset + (additional_index << shift_size)); } } -void LCodeGen::DoLoadKeyedSpecializedArrayElement( - LLoadKeyedSpecializedArrayElement* instr) { +void LCodeGen::DoLoadKeyedExternalArray(LLoadKeyed* instr) { ElementsKind elements_kind = instr->elements_kind(); - Operand operand(BuildFastArrayOperand(instr->external_pointer(), - instr->key(), - elements_kind, - 0, - instr->additional_index())); - if (instr->hydrogen()->IsDehoisted() && !instr->key()->IsConstantOperand()) { - // Sign extend key because it could be a 32 bit negative value - // and the dehoisted address computation happens in 64 bits - Register key_reg = ToRegister(instr->key()); - __ movsxlq(key_reg, key_reg); - } + LOperand* key = instr->key(); + PrepareKeyForKeyedOp(instr->hydrogen(), key); + Operand operand(BuildFastArrayOperand( + instr->elements(), + key, + elements_kind, + 0, + instr->additional_index())); if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { XMMRegister result(ToDoubleRegister(instr->result())); @@ -2493,17 +2683,19 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( break; case EXTERNAL_UNSIGNED_INT_ELEMENTS: __ movl(result, operand); - __ testl(result, result); - // TODO(danno): we could be more clever here, perhaps having a special - // version of the stub that detects if the overflow case actually - // happens, and generate code that returns a double rather than int. - DeoptimizeIf(negative, instr->environment()); + if (!instr->hydrogen()->CheckFlag(HInstruction::kUint32)) { + __ testl(result, result); + DeoptimizeIf(negative, instr->environment()); + } break; case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -2513,6 +2705,96 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( } +void LCodeGen::DoLoadKeyedFixedDoubleArray(LLoadKeyed* instr) { + XMMRegister result(ToDoubleRegister(instr->result())); + LOperand* key = instr->key(); + PrepareKeyForKeyedOp<HLoadKeyed>(instr->hydrogen(), key); + if (instr->hydrogen()->RequiresHoleCheck()) { + int offset = FixedDoubleArray::kHeaderSize - kHeapObjectTag + + sizeof(kHoleNanLower32); + Operand hole_check_operand = BuildFastArrayOperand( + instr->elements(), + key, + FAST_DOUBLE_ELEMENTS, + offset, + instr->additional_index()); + __ cmpl(hole_check_operand, Immediate(kHoleNanUpper32)); + DeoptimizeIf(equal, instr->environment()); + } + + Operand double_load_operand = BuildFastArrayOperand( + instr->elements(), + key, + FAST_DOUBLE_ELEMENTS, + FixedDoubleArray::kHeaderSize - kHeapObjectTag, + instr->additional_index()); + __ movsd(result, double_load_operand); +} + + +void LCodeGen::DoLoadKeyedFixedArray(LLoadKeyed* instr) { + Register result = ToRegister(instr->result()); + LOperand* key = instr->key(); + PrepareKeyForKeyedOp<HLoadKeyed>(instr->hydrogen(), key); + + // Load the result. + __ movq(result, + BuildFastArrayOperand(instr->elements(), + key, + FAST_ELEMENTS, + FixedArray::kHeaderSize - kHeapObjectTag, + instr->additional_index())); + + // Check for the hole value. + if (instr->hydrogen()->RequiresHoleCheck()) { + if (IsFastSmiElementsKind(instr->hydrogen()->elements_kind())) { + Condition smi = __ CheckSmi(result); + DeoptimizeIf(NegateCondition(smi), instr->environment()); + } else { + __ CompareRoot(result, Heap::kTheHoleValueRootIndex); + DeoptimizeIf(equal, instr->environment()); + } + } +} + + +void LCodeGen::DoLoadKeyed(LLoadKeyed* instr) { + if (instr->is_external()) { + DoLoadKeyedExternalArray(instr); + } else if (instr->hydrogen()->representation().IsDouble()) { + DoLoadKeyedFixedDoubleArray(instr); + } else { + DoLoadKeyedFixedArray(instr); + } +} + + +Operand LCodeGen::BuildFastArrayOperand( + LOperand* elements_pointer, + LOperand* key, + ElementsKind elements_kind, + uint32_t offset, + uint32_t additional_index) { + Register elements_pointer_reg = ToRegister(elements_pointer); + int shift_size = ElementsKindToShiftSize(elements_kind); + if (key->IsConstantOperand()) { + int constant_value = ToInteger32(LConstantOperand::cast(key)); + if (constant_value & 0xF0000000) { + Abort("array index constant value too big"); + } + return Operand(elements_pointer_reg, + ((constant_value + additional_index) << shift_size) + + offset); + } else { + ScaleFactor scale_factor = static_cast<ScaleFactor>(shift_size); + return Operand(elements_pointer_reg, + ToRegister(key), + scale_factor, + offset + (additional_index << shift_size)); + } +} + + void LCodeGen::DoLoadKeyedGeneric(LLoadKeyedGeneric* instr) { ASSERT(ToRegister(instr->object()).is(rdx)); ASSERT(ToRegister(instr->key()).is(rax)); @@ -2556,10 +2838,10 @@ void LCodeGen::DoArgumentsLength(LArgumentsLength* instr) { Label done; // If no arguments adaptor frame the number of arguments is fixed. - if (instr->InputAt(0)->IsRegister()) { - __ cmpq(rbp, ToRegister(instr->InputAt(0))); + if (instr->elements()->IsRegister()) { + __ cmpq(rbp, ToRegister(instr->elements())); } else { - __ cmpq(rbp, ToOperand(instr->InputAt(0))); + __ cmpq(rbp, ToOperand(instr->elements())); } __ movl(result, Immediate(scope()->num_parameters())); __ j(equal, &done, Label::kNear); @@ -2616,7 +2898,7 @@ void LCodeGen::DoWrapReceiver(LWrapReceiver* instr) { // TODO(kmillikin): We have a hydrogen value for the global object. See // if it's better to use it than to explicitly fetch it from the context // here. - __ movq(receiver, ContextOperand(rsi, Context::GLOBAL_INDEX)); + __ movq(receiver, ContextOperand(rsi, Context::GLOBAL_OBJECT_INDEX)); __ movq(receiver, FieldOperand(receiver, JSGlobalObject::kGlobalReceiverOffset)); __ bind(&receiver_ok); @@ -2667,7 +2949,7 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) { void LCodeGen::DoPushArgument(LPushArgument* instr) { - LOperand* argument = instr->InputAt(0); + LOperand* argument = instr->value(); EmitPushTaggedOperand(argument); } @@ -2679,7 +2961,7 @@ void LCodeGen::DoDrop(LDrop* instr) { void LCodeGen::DoThisFunction(LThisFunction* instr) { Register result = ToRegister(instr->result()); - __ LoadHeapObject(result, instr->hydrogen()->closure()); + __ movq(result, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); } @@ -2734,14 +3016,8 @@ void LCodeGen::CallKnownFunction(Handle<JSFunction> function, __ LoadHeapObject(rdi, function); } - // Change context if needed. - bool change_context = - (info()->closure()->context() != function->context()) || - scope()->contains_with() || - (scope()->num_heap_slots() > 0); - if (change_context) { - __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); - } + // Change context. + __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); // Set rax to arguments count if adaption is not needed. Assumes that rax // is available to write to at this point. @@ -2783,7 +3059,7 @@ void LCodeGen::DoCallConstantFunction(LCallConstantFunction* instr) { void LCodeGen::DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr) { - Register input_reg = ToRegister(instr->InputAt(0)); + Register input_reg = ToRegister(instr->value()); __ CompareRoot(FieldOperand(input_reg, HeapObject::kMapOffset), Heap::kHeapNumberMapRootIndex); DeoptimizeIf(not_equal, instr->environment()); @@ -2835,7 +3111,7 @@ void LCodeGen::DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr) { void LCodeGen::EmitIntegerMathAbs(LUnaryMathOperation* instr) { - Register input_reg = ToRegister(instr->InputAt(0)); + Register input_reg = ToRegister(instr->value()); __ testl(input_reg, input_reg); Label is_positive; __ j(not_sign, &is_positive); @@ -2860,12 +3136,12 @@ void LCodeGen::DoMathAbs(LUnaryMathOperation* instr) { LUnaryMathOperation* instr_; }; - ASSERT(instr->InputAt(0)->Equals(instr->result())); + ASSERT(instr->value()->Equals(instr->result())); Representation r = instr->hydrogen()->value()->representation(); if (r.IsDouble()) { XMMRegister scratch = xmm0; - XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0)); + XMMRegister input_reg = ToDoubleRegister(instr->value()); __ xorps(scratch, scratch); __ subsd(scratch, input_reg); __ andpd(input_reg, scratch); @@ -2873,8 +3149,8 @@ void LCodeGen::DoMathAbs(LUnaryMathOperation* instr) { EmitIntegerMathAbs(instr); } else { // Tagged case. DeferredMathAbsTaggedHeapNumber* deferred = - new DeferredMathAbsTaggedHeapNumber(this, instr); - Register input_reg = ToRegister(instr->InputAt(0)); + new(zone()) DeferredMathAbsTaggedHeapNumber(this, instr); + Register input_reg = ToRegister(instr->value()); // Smi check. __ JumpIfNotSmi(input_reg, deferred->entry()); __ SmiToInteger32(input_reg, input_reg); @@ -2888,8 +3164,7 @@ void LCodeGen::DoMathAbs(LUnaryMathOperation* instr) { void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) { XMMRegister xmm_scratch = xmm0; Register output_reg = ToRegister(instr->result()); - XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0)); - Label done; + XMMRegister input_reg = ToDoubleRegister(instr->value()); if (CpuFeatures::IsSupported(SSE4_1)) { CpuFeatures::Scope scope(SSE4_1); @@ -2904,10 +3179,13 @@ void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) { __ cmpl(output_reg, Immediate(0x80000000)); DeoptimizeIf(equal, instr->environment()); } else { + Label negative_sign, done; // Deoptimize on negative inputs. __ xorps(xmm_scratch, xmm_scratch); // Zero the register. __ ucomisd(input_reg, xmm_scratch); - DeoptimizeIf(below, instr->environment()); + DeoptimizeIf(parity_even, instr->environment()); + __ j(below, &negative_sign, Label::kNear); + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { // Check for negative zero. Label positive_sign; @@ -2922,19 +3200,30 @@ void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) { // Use truncating instruction (OK because input is positive). __ cvttsd2si(output_reg, input_reg); - // Overflow is signalled with minint. __ cmpl(output_reg, Immediate(0x80000000)); DeoptimizeIf(equal, instr->environment()); + __ jmp(&done, Label::kNear); + + // Non-zero negative reaches here. + __ bind(&negative_sign); + // Truncate, then compare and compensate. + __ cvttsd2si(output_reg, input_reg); + __ cvtlsi2sd(xmm_scratch, output_reg); + __ ucomisd(input_reg, xmm_scratch); + __ j(equal, &done, Label::kNear); + __ subl(output_reg, Immediate(1)); + DeoptimizeIf(overflow, instr->environment()); + + __ bind(&done); } - __ bind(&done); } void LCodeGen::DoMathRound(LUnaryMathOperation* instr) { const XMMRegister xmm_scratch = xmm0; Register output_reg = ToRegister(instr->result()); - XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0)); + XMMRegister input_reg = ToDoubleRegister(instr->value()); Label done; // xmm_scratch = 0.5 @@ -2979,7 +3268,7 @@ void LCodeGen::DoMathRound(LUnaryMathOperation* instr) { void LCodeGen::DoMathSqrt(LUnaryMathOperation* instr) { - XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0)); + XMMRegister input_reg = ToDoubleRegister(instr->value()); ASSERT(ToDoubleRegister(instr->result()).is(input_reg)); __ sqrtsd(input_reg, input_reg); } @@ -2987,7 +3276,7 @@ void LCodeGen::DoMathSqrt(LUnaryMathOperation* instr) { void LCodeGen::DoMathPowHalf(LUnaryMathOperation* instr) { XMMRegister xmm_scratch = xmm0; - XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0)); + XMMRegister input_reg = ToDoubleRegister(instr->value()); ASSERT(ToDoubleRegister(instr->result()).is(input_reg)); // Note that according to ECMA-262 15.8.2.13: @@ -3028,11 +3317,11 @@ void LCodeGen::DoPower(LPower* instr) { #else Register exponent = rdi; #endif - ASSERT(!instr->InputAt(1)->IsRegister() || - ToRegister(instr->InputAt(1)).is(exponent)); - ASSERT(!instr->InputAt(1)->IsDoubleRegister() || - ToDoubleRegister(instr->InputAt(1)).is(xmm1)); - ASSERT(ToDoubleRegister(instr->InputAt(0)).is(xmm2)); + ASSERT(!instr->right()->IsRegister() || + ToRegister(instr->right()).is(exponent)); + ASSERT(!instr->right()->IsDoubleRegister() || + ToDoubleRegister(instr->right()).is(xmm1)); + ASSERT(ToDoubleRegister(instr->left()).is(xmm2)); ASSERT(ToDoubleRegister(instr->result()).is(xmm3)); if (exponent_type.IsTagged()) { @@ -3065,7 +3354,7 @@ void LCodeGen::DoRandom(LRandom* instr) { LRandom* instr_; }; - DeferredDoRandom* deferred = new DeferredDoRandom(this, instr); + DeferredDoRandom* deferred = new(zone()) DeferredDoRandom(this, instr); // Having marked this instruction as a call we can use any // registers. @@ -3074,10 +3363,10 @@ void LCodeGen::DoRandom(LRandom* instr) { // Choose the right register for the first argument depending on // calling convention. #ifdef _WIN64 - ASSERT(ToRegister(instr->InputAt(0)).is(rcx)); + ASSERT(ToRegister(instr->global_object()).is(rcx)); Register global_object = rcx; #else - ASSERT(ToRegister(instr->InputAt(0)).is(rdi)); + ASSERT(ToRegister(instr->global_object()).is(rdi)); Register global_object = rdi; #endif @@ -3085,11 +3374,11 @@ void LCodeGen::DoRandom(LRandom* instr) { STATIC_ASSERT(kPointerSize == 2 * kSeedSize); __ movq(global_object, - FieldOperand(global_object, GlobalObject::kGlobalContextOffset)); + FieldOperand(global_object, GlobalObject::kNativeContextOffset)); static const int kRandomSeedOffset = FixedArray::kHeaderSize + Context::RANDOM_SEED_INDEX * kPointerSize; __ movq(rbx, FieldOperand(global_object, kRandomSeedOffset)); - // rbx: FixedArray of the global context's random seeds + // rbx: FixedArray of the native context's random seeds // Load state[0]. __ movl(rax, FieldOperand(rbx, ByteArray::kHeaderSize)); @@ -3292,7 +3581,7 @@ void LCodeGen::DoCallKnownGlobal(LCallKnownGlobal* instr) { void LCodeGen::DoCallNew(LCallNew* instr) { - ASSERT(ToRegister(instr->InputAt(0)).is(rdi)); + ASSERT(ToRegister(instr->constructor()).is(rdi)); ASSERT(ToRegister(instr->result()).is(rax)); CallConstructStub stub(NO_CALL_FUNCTION_FLAGS); @@ -3312,7 +3601,22 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { int offset = instr->offset(); if (!instr->transition().is_null()) { - __ Move(FieldOperand(object, HeapObject::kMapOffset), instr->transition()); + if (!instr->hydrogen()->NeedsWriteBarrierForMap()) { + __ Move(FieldOperand(object, HeapObject::kMapOffset), + instr->transition()); + } else { + Register temp = ToRegister(instr->temp()); + __ Move(kScratchRegister, instr->transition()); + __ movq(FieldOperand(object, HeapObject::kMapOffset), kScratchRegister); + // Update the write barrier for the map field. + __ RecordWriteField(object, + HeapObject::kMapOffset, + kScratchRegister, + temp, + kSaveFPRegs, + OMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + } } // Do the store. @@ -3322,7 +3626,7 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { if (instr->is_in_object()) { __ movq(FieldOperand(object, offset), value); if (instr->hydrogen()->NeedsWriteBarrier()) { - Register temp = ToRegister(instr->TempAt(0)); + Register temp = ToRegister(instr->temp()); // Update the write barrier for the object for in-object properties. __ RecordWriteField(object, offset, @@ -3333,7 +3637,7 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { check_needed); } } else { - Register temp = ToRegister(instr->TempAt(0)); + Register temp = ToRegister(instr->temp()); __ movq(temp, FieldOperand(object, JSObject::kPropertiesOffset)); __ movq(FieldOperand(temp, offset), value); if (instr->hydrogen()->NeedsWriteBarrier()) { @@ -3363,21 +3667,76 @@ void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) { } -void LCodeGen::DoStoreKeyedSpecializedArrayElement( - LStoreKeyedSpecializedArrayElement* instr) { - ElementsKind elements_kind = instr->elements_kind(); - Operand operand(BuildFastArrayOperand(instr->external_pointer(), - instr->key(), - elements_kind, - 0, - instr->additional_index())); +void LCodeGen::DeoptIfTaggedButNotSmi(LEnvironment* environment, + HValue* value, + LOperand* operand) { + if (value->representation().IsTagged() && !value->type().IsSmi()) { + Condition cc; + if (operand->IsRegister()) { + cc = masm()->CheckSmi(ToRegister(operand)); + } else { + cc = masm()->CheckSmi(ToOperand(operand)); + } + DeoptimizeIf(NegateCondition(cc), environment); + } +} + - if (instr->hydrogen()->IsDehoisted() && !instr->key()->IsConstantOperand()) { - // Sign extend key because it could be a 32 bit negative value - // and the dehoisted address computation happens in 64 bits - Register key_reg = ToRegister(instr->key()); - __ movsxlq(key_reg, key_reg); +void LCodeGen::DoBoundsCheck(LBoundsCheck* instr) { + DeoptIfTaggedButNotSmi(instr->environment(), + instr->hydrogen()->length(), + instr->length()); + DeoptIfTaggedButNotSmi(instr->environment(), + instr->hydrogen()->index(), + instr->index()); + if (instr->length()->IsRegister()) { + Register reg = ToRegister(instr->length()); + if (!instr->hydrogen()->length()->representation().IsTagged()) { + __ AssertZeroExtended(reg); + } + if (instr->index()->IsConstantOperand()) { + int constant_index = + ToInteger32(LConstantOperand::cast(instr->index())); + if (instr->hydrogen()->length()->representation().IsTagged()) { + __ Cmp(reg, Smi::FromInt(constant_index)); + } else { + __ cmpq(reg, Immediate(constant_index)); + } + } else { + Register reg2 = ToRegister(instr->index()); + if (!instr->hydrogen()->index()->representation().IsTagged()) { + __ AssertZeroExtended(reg2); + } + __ cmpq(reg, reg2); + } + } else { + Operand length = ToOperand(instr->length()); + if (instr->index()->IsConstantOperand()) { + int constant_index = + ToInteger32(LConstantOperand::cast(instr->index())); + if (instr->hydrogen()->length()->representation().IsTagged()) { + __ Cmp(length, Smi::FromInt(constant_index)); + } else { + __ cmpq(length, Immediate(constant_index)); + } + } else { + __ cmpq(length, ToRegister(instr->index())); + } } + DeoptimizeIf(below_equal, instr->environment()); +} + + +void LCodeGen::DoStoreKeyedExternalArray(LStoreKeyed* instr) { + ElementsKind elements_kind = instr->elements_kind(); + LOperand* key = instr->key(); + PrepareKeyForKeyedOp<HStoreKeyed>(instr->hydrogen(), key); + Operand operand(BuildFastArrayOperand( + instr->elements(), + key, + elements_kind, + 0, + instr->additional_index())); if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { XMMRegister value(ToDoubleRegister(instr->value())); @@ -3404,8 +3763,11 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3415,106 +3777,79 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( } -void LCodeGen::DoBoundsCheck(LBoundsCheck* instr) { - if (instr->length()->IsRegister()) { - Register reg = ToRegister(instr->length()); - if (FLAG_debug_code) { - __ AbortIfNotZeroExtended(reg); - } - if (instr->index()->IsConstantOperand()) { - __ cmpq(reg, - Immediate(ToInteger32(LConstantOperand::cast(instr->index())))); - } else { - Register reg2 = ToRegister(instr->index()); - if (FLAG_debug_code) { - __ AbortIfNotZeroExtended(reg2); - } - __ cmpq(reg, reg2); - } - } else { - if (instr->index()->IsConstantOperand()) { - __ cmpq(ToOperand(instr->length()), - Immediate(ToInteger32(LConstantOperand::cast(instr->index())))); - } else { - __ cmpq(ToOperand(instr->length()), ToRegister(instr->index())); - } +void LCodeGen::DoStoreKeyedFixedDoubleArray(LStoreKeyed* instr) { + XMMRegister value = ToDoubleRegister(instr->value()); + LOperand* key = instr->key(); + PrepareKeyForKeyedOp<HStoreKeyed>(instr->hydrogen(), key); + if (instr->NeedsCanonicalization()) { + Label have_value; + + __ ucomisd(value, value); + __ j(parity_odd, &have_value); // NaN. + + __ Set(kScratchRegister, BitCast<uint64_t>( + FixedDoubleArray::canonical_not_the_hole_nan_as_double())); + __ movq(value, kScratchRegister); + + __ bind(&have_value); } - DeoptimizeIf(below_equal, instr->environment()); + + Operand double_store_operand = BuildFastArrayOperand( + instr->elements(), + key, + FAST_DOUBLE_ELEMENTS, + FixedDoubleArray::kHeaderSize - kHeapObjectTag, + instr->additional_index()); + + __ movsd(double_store_operand, value); } -void LCodeGen::DoStoreKeyedFastElement(LStoreKeyedFastElement* instr) { +void LCodeGen::DoStoreKeyedFixedArray(LStoreKeyed* instr) { Register value = ToRegister(instr->value()); - Register elements = ToRegister(instr->object()); - Register key = instr->key()->IsRegister() ? ToRegister(instr->key()) : no_reg; + Register elements = ToRegister(instr->elements()); + LOperand* key = instr->key(); + PrepareKeyForKeyedOp<HStoreKeyed>(instr->hydrogen(), key); Operand operand = - BuildFastArrayOperand(instr->object(), - instr->key(), + BuildFastArrayOperand(instr->elements(), + key, FAST_ELEMENTS, FixedArray::kHeaderSize - kHeapObjectTag, instr->additional_index()); - if (instr->hydrogen()->IsDehoisted() && !instr->key()->IsConstantOperand()) { - // Sign extend key because it could be a 32 bit negative value - // and the dehoisted address computation happens in 64 bits - Register key_reg = ToRegister(instr->key()); - __ movsxlq(key_reg, key_reg); - } - - __ movq(operand, value); - if (instr->hydrogen()->NeedsWriteBarrier()) { ASSERT(!instr->key()->IsConstantOperand()); HType type = instr->hydrogen()->value()->type(); SmiCheck check_needed = type.IsHeapObject() ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; // Compute address of modified element and store it into key register. - __ lea(key, operand); + Register key_reg(ToRegister(key)); + __ lea(key_reg, operand); + __ movq(Operand(key_reg, 0), value); __ RecordWrite(elements, - key, + key_reg, value, kSaveFPRegs, EMIT_REMEMBERED_SET, check_needed); + } else { + __ movq(operand, value); } } -void LCodeGen::DoStoreKeyedFastDoubleElement( - LStoreKeyedFastDoubleElement* instr) { - XMMRegister value = ToDoubleRegister(instr->value()); - - if (instr->NeedsCanonicalization()) { - Label have_value; - - __ ucomisd(value, value); - __ j(parity_odd, &have_value); // NaN. - - __ Set(kScratchRegister, BitCast<uint64_t>( - FixedDoubleArray::canonical_not_the_hole_nan_as_double())); - __ movq(value, kScratchRegister); - - __ bind(&have_value); - } - - Operand double_store_operand = BuildFastArrayOperand( - instr->elements(), - instr->key(), - FAST_DOUBLE_ELEMENTS, - FixedDoubleArray::kHeaderSize - kHeapObjectTag, - instr->additional_index()); - - if (instr->hydrogen()->IsDehoisted() && !instr->key()->IsConstantOperand()) { - // Sign extend key because it could be a 32 bit negative value - // and the dehoisted address computation happens in 64 bits - Register key_reg = ToRegister(instr->key()); - __ movsxlq(key_reg, key_reg); +void LCodeGen::DoStoreKeyed(LStoreKeyed* instr) { + if (instr->is_external()) { + DoStoreKeyedExternalArray(instr); + } else if (instr->hydrogen()->value()->representation().IsDouble()) { + DoStoreKeyedFixedDoubleArray(instr); + } else { + DoStoreKeyedFixedArray(instr); } - - __ movsd(double_store_operand, value); } + void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) { ASSERT(ToRegister(instr->object()).is(rdx)); ASSERT(ToRegister(instr->key()).is(rcx)); @@ -3529,7 +3864,7 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) { void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) { Register object_reg = ToRegister(instr->object()); - Register new_map_reg = ToRegister(instr->new_map_reg()); + Register new_map_reg = ToRegister(instr->new_map_temp()); Handle<Map> from_map = instr->original_map(); Handle<Map> to_map = instr->transitioned_map(); @@ -3540,22 +3875,23 @@ void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) { __ Cmp(FieldOperand(object_reg, HeapObject::kMapOffset), from_map); __ j(not_equal, ¬_applicable); __ movq(new_map_reg, to_map, RelocInfo::EMBEDDED_OBJECT); - if (from_kind == FAST_SMI_ONLY_ELEMENTS && to_kind == FAST_ELEMENTS) { + if (IsSimpleMapChangeTransition(from_kind, to_kind)) { __ movq(FieldOperand(object_reg, HeapObject::kMapOffset), new_map_reg); // Write barrier. - ASSERT_NE(instr->temp_reg(), NULL); + ASSERT_NE(instr->temp(), NULL); __ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg, - ToRegister(instr->temp_reg()), kDontSaveFPRegs); - } else if (from_kind == FAST_SMI_ONLY_ELEMENTS && - to_kind == FAST_DOUBLE_ELEMENTS) { - Register fixed_object_reg = ToRegister(instr->temp_reg()); + ToRegister(instr->temp()), kDontSaveFPRegs); + } else if (IsFastSmiElementsKind(from_kind) && + IsFastDoubleElementsKind(to_kind)) { + Register fixed_object_reg = ToRegister(instr->temp()); ASSERT(fixed_object_reg.is(rdx)); ASSERT(new_map_reg.is(rbx)); __ movq(fixed_object_reg, object_reg); CallCode(isolate()->builtins()->TransitionElementsSmiToDouble(), RelocInfo::CODE_TARGET, instr); - } else if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS) { - Register fixed_object_reg = ToRegister(instr->temp_reg()); + } else if (IsFastDoubleElementsKind(from_kind) && + IsFastObjectElementsKind(to_kind)) { + Register fixed_object_reg = ToRegister(instr->temp()); ASSERT(fixed_object_reg.is(rdx)); ASSERT(new_map_reg.is(rbx)); __ movq(fixed_object_reg, object_reg); @@ -3588,7 +3924,7 @@ void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) { }; DeferredStringCharCodeAt* deferred = - new DeferredStringCharCodeAt(this, instr); + new(zone()) DeferredStringCharCodeAt(this, instr); StringCharLoadGenerator::Generate(masm(), ToRegister(instr->string()), @@ -3622,9 +3958,7 @@ void LCodeGen::DoDeferredStringCharCodeAt(LStringCharCodeAt* instr) { __ push(index); } CallRuntimeFromDeferred(Runtime::kStringCharCodeAt, 2, instr); - if (FLAG_debug_code) { - __ AbortIfNotSmi(rax); - } + __ AssertSmi(rax); __ SmiToInteger32(rax, rax); __ StoreToSafepointRegisterSlot(result, rax); } @@ -3642,7 +3976,7 @@ void LCodeGen::DoStringCharFromCode(LStringCharFromCode* instr) { }; DeferredStringCharFromCode* deferred = - new DeferredStringCharFromCode(this, instr); + new(zone()) DeferredStringCharFromCode(this, instr); ASSERT(instr->hydrogen()->value()->representation().IsInteger32()); Register char_code = ToRegister(instr->char_code()); @@ -3686,7 +4020,7 @@ void LCodeGen::DoStringLength(LStringLength* instr) { void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) { - LOperand* input = instr->InputAt(0); + LOperand* input = instr->value(); ASSERT(input->IsRegister() || input->IsStackSlot()); LOperand* output = instr->result(); ASSERT(output->IsDoubleRegister()); @@ -3698,8 +4032,19 @@ void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) { } +void LCodeGen::DoUint32ToDouble(LUint32ToDouble* instr) { + LOperand* input = instr->value(); + LOperand* output = instr->result(); + LOperand* temp = instr->temp(); + + __ LoadUint32(ToDoubleRegister(output), + ToRegister(input), + ToDoubleRegister(temp)); +} + + void LCodeGen::DoNumberTagI(LNumberTagI* instr) { - LOperand* input = instr->InputAt(0); + LOperand* input = instr->value(); ASSERT(input->IsRegister() && input->Equals(instr->result())); Register reg = ToRegister(input); @@ -3707,6 +4052,69 @@ void LCodeGen::DoNumberTagI(LNumberTagI* instr) { } +void LCodeGen::DoNumberTagU(LNumberTagU* instr) { + class DeferredNumberTagU: public LDeferredCode { + public: + DeferredNumberTagU(LCodeGen* codegen, LNumberTagU* instr) + : LDeferredCode(codegen), instr_(instr) { } + virtual void Generate() { + codegen()->DoDeferredNumberTagU(instr_); + } + virtual LInstruction* instr() { return instr_; } + private: + LNumberTagU* instr_; + }; + + LOperand* input = instr->value(); + ASSERT(input->IsRegister() && input->Equals(instr->result())); + Register reg = ToRegister(input); + + DeferredNumberTagU* deferred = new(zone()) DeferredNumberTagU(this, instr); + __ cmpl(reg, Immediate(Smi::kMaxValue)); + __ j(above, deferred->entry()); + __ Integer32ToSmi(reg, reg); + __ bind(deferred->exit()); +} + + +void LCodeGen::DoDeferredNumberTagU(LNumberTagU* instr) { + Label slow; + Register reg = ToRegister(instr->value()); + Register tmp = reg.is(rax) ? rcx : rax; + + // Preserve the value of all registers. + PushSafepointRegistersScope scope(this); + + Label done; + // Load value into xmm1 which will be preserved across potential call to + // runtime (MacroAssembler::EnterExitFrameEpilogue preserves only allocatable + // XMM registers on x64). + __ LoadUint32(xmm1, reg, xmm0); + + if (FLAG_inline_new) { + __ AllocateHeapNumber(reg, tmp, &slow); + __ jmp(&done, Label::kNear); + } + + // Slow case: Call the runtime system to do the number allocation. + __ bind(&slow); + + // Put a valid pointer value in the stack slot where the result + // register is stored, as this register is in the pointer map, but contains an + // integer value. + __ StoreToSafepointRegisterSlot(reg, Immediate(0)); + + CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr); + if (!reg.is(rax)) __ movq(reg, rax); + + // Done. Put the value in xmm1 into the value of the allocated heap + // number. + __ bind(&done); + __ movsd(FieldOperand(reg, HeapNumber::kValueOffset), xmm1); + __ StoreToSafepointRegisterSlot(reg, reg); +} + + void LCodeGen::DoNumberTagD(LNumberTagD* instr) { class DeferredNumberTagD: public LDeferredCode { public: @@ -3718,11 +4126,11 @@ void LCodeGen::DoNumberTagD(LNumberTagD* instr) { LNumberTagD* instr_; }; - XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0)); + XMMRegister input_reg = ToDoubleRegister(instr->value()); Register reg = ToRegister(instr->result()); - Register tmp = ToRegister(instr->TempAt(0)); + Register tmp = ToRegister(instr->temp()); - DeferredNumberTagD* deferred = new DeferredNumberTagD(this, instr); + DeferredNumberTagD* deferred = new(zone()) DeferredNumberTagD(this, instr); if (FLAG_inline_new) { __ AllocateHeapNumber(reg, tmp, deferred->entry()); } else { @@ -3751,19 +4159,21 @@ void LCodeGen::DoDeferredNumberTagD(LNumberTagD* instr) { void LCodeGen::DoSmiTag(LSmiTag* instr) { - ASSERT(instr->InputAt(0)->Equals(instr->result())); - Register input = ToRegister(instr->InputAt(0)); + ASSERT(instr->value()->Equals(instr->result())); + Register input = ToRegister(instr->value()); ASSERT(!instr->hydrogen_value()->CheckFlag(HValue::kCanOverflow)); __ Integer32ToSmi(input, input); } void LCodeGen::DoSmiUntag(LSmiUntag* instr) { - ASSERT(instr->InputAt(0)->Equals(instr->result())); - Register input = ToRegister(instr->InputAt(0)); + ASSERT(instr->value()->Equals(instr->result())); + Register input = ToRegister(instr->value()); if (instr->needs_check()) { Condition is_smi = __ CheckSmi(input); DeoptimizeIf(NegateCondition(is_smi), instr->environment()); + } else { + __ AssertSmi(input); } __ SmiToInteger32(input, input); } @@ -3821,7 +4231,7 @@ void LCodeGen::EmitNumberUntagD(Register input_reg, void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) { Label done, heap_number; - Register input_reg = ToRegister(instr->InputAt(0)); + Register input_reg = ToRegister(instr->value()); // Heap number map check. __ CompareRoot(FieldOperand(input_reg, HeapObject::kMapOffset), @@ -3847,7 +4257,7 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) { // Deoptimize if we don't have a heap number. DeoptimizeIf(not_equal, instr->environment()); - XMMRegister xmm_temp = ToDoubleRegister(instr->TempAt(0)); + XMMRegister xmm_temp = ToDoubleRegister(instr->temp()); __ movsd(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset)); __ cvttsd2si(input_reg, xmm0); __ cvtlsi2sd(xmm_temp, input_reg); @@ -3877,12 +4287,12 @@ void LCodeGen::DoTaggedToI(LTaggedToI* instr) { LTaggedToI* instr_; }; - LOperand* input = instr->InputAt(0); + LOperand* input = instr->value(); ASSERT(input->IsRegister()); ASSERT(input->Equals(instr->result())); Register input_reg = ToRegister(input); - DeferredTaggedToI* deferred = new DeferredTaggedToI(this, instr); + DeferredTaggedToI* deferred = new(zone()) DeferredTaggedToI(this, instr); __ JumpIfNotSmi(input_reg, deferred->entry()); __ SmiToInteger32(input_reg, input_reg); __ bind(deferred->exit()); @@ -3890,7 +4300,7 @@ void LCodeGen::DoTaggedToI(LTaggedToI* instr) { void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) { - LOperand* input = instr->InputAt(0); + LOperand* input = instr->value(); ASSERT(input->IsRegister()); LOperand* result = instr->result(); ASSERT(result->IsDoubleRegister()); @@ -3906,7 +4316,7 @@ void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) { void LCodeGen::DoDoubleToI(LDoubleToI* instr) { - LOperand* input = instr->InputAt(0); + LOperand* input = instr->value(); ASSERT(input->IsDoubleRegister()); LOperand* result = instr->result(); ASSERT(result->IsRegister()); @@ -3946,21 +4356,21 @@ void LCodeGen::DoDoubleToI(LDoubleToI* instr) { void LCodeGen::DoCheckSmi(LCheckSmi* instr) { - LOperand* input = instr->InputAt(0); + LOperand* input = instr->value(); Condition cc = masm()->CheckSmi(ToRegister(input)); DeoptimizeIf(NegateCondition(cc), instr->environment()); } void LCodeGen::DoCheckNonSmi(LCheckNonSmi* instr) { - LOperand* input = instr->InputAt(0); + LOperand* input = instr->value(); Condition cc = masm()->CheckSmi(ToRegister(input)); DeoptimizeIf(cc, instr->environment()); } void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) { - Register input = ToRegister(instr->InputAt(0)); + Register input = ToRegister(instr->value()); __ movq(kScratchRegister, FieldOperand(input, HeapObject::kMapOffset)); @@ -4032,7 +4442,7 @@ void LCodeGen::DoCheckMapCommon(Register reg, void LCodeGen::DoCheckMaps(LCheckMaps* instr) { - LOperand* input = instr->InputAt(0); + LOperand* input = instr->value(); ASSERT(input->IsRegister()); Register reg = ToRegister(input); @@ -4052,8 +4462,7 @@ void LCodeGen::DoCheckMaps(LCheckMaps* instr) { void LCodeGen::DoClampDToUint8(LClampDToUint8* instr) { XMMRegister value_reg = ToDoubleRegister(instr->unclamped()); Register result_reg = ToRegister(instr->result()); - Register temp_reg = ToRegister(instr->TempAt(0)); - __ ClampDoubleToUint8(value_reg, xmm0, result_reg, temp_reg); + __ ClampDoubleToUint8(value_reg, xmm0, result_reg); } @@ -4067,8 +4476,7 @@ void LCodeGen::DoClampIToUint8(LClampIToUint8* instr) { void LCodeGen::DoClampTToUint8(LClampTToUint8* instr) { ASSERT(instr->unclamped()->Equals(instr->result())); Register input_reg = ToRegister(instr->unclamped()); - Register temp_reg = ToRegister(instr->TempAt(0)); - XMMRegister temp_xmm_reg = ToDoubleRegister(instr->TempAt(1)); + XMMRegister temp_xmm_reg = ToDoubleRegister(instr->temp_xmm()); Label is_smi, done, heap_number; __ JumpIfSmi(input_reg, &is_smi); @@ -4088,7 +4496,7 @@ void LCodeGen::DoClampTToUint8(LClampTToUint8* instr) { // Heap number __ bind(&heap_number); __ movsd(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset)); - __ ClampDoubleToUint8(xmm0, temp_xmm_reg, input_reg, temp_reg); + __ ClampDoubleToUint8(xmm0, temp_xmm_reg, input_reg); __ jmp(&done, Label::kNear); // smi @@ -4101,7 +4509,8 @@ void LCodeGen::DoClampTToUint8(LClampTToUint8* instr) { void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) { - Register reg = ToRegister(instr->TempAt(0)); + ASSERT(instr->temp()->Equals(instr->result())); + Register reg = ToRegister(instr->temp()); Handle<JSObject> holder = instr->holder(); Handle<JSObject> current_prototype = instr->prototype(); @@ -4136,10 +4545,11 @@ void LCodeGen::DoAllocateObject(LAllocateObject* instr) { LAllocateObject* instr_; }; - DeferredAllocateObject* deferred = new DeferredAllocateObject(this, instr); + DeferredAllocateObject* deferred = + new(zone()) DeferredAllocateObject(this, instr); Register result = ToRegister(instr->result()); - Register scratch = ToRegister(instr->TempAt(0)); + Register scratch = ToRegister(instr->temp()); Handle<JSFunction> constructor = instr->hydrogen()->constructor(); Handle<Map> initial_map(constructor->initial_map()); int instance_size = initial_map->instance_size(); @@ -4172,7 +4582,7 @@ void LCodeGen::DoAllocateObject(LAllocateObject* instr) { __ movq(map, FieldOperand(scratch, JSFunction::kPrototypeOrInitialMapOffset)); if (FLAG_debug_code) { - __ AbortIfSmi(map); + __ AssertNotSmi(map); __ cmpb(FieldOperand(map, Map::kInstanceSizeOffset), Immediate(instance_size >> kPointerSizeLog2)); __ Assert(equal, "Unexpected instance size"); @@ -4222,14 +4632,15 @@ void LCodeGen::DoDeferredAllocateObject(LAllocateObject* instr) { void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) { - Heap* heap = isolate()->heap(); + Handle<FixedArray> literals(instr->environment()->closure()->literals()); ElementsKind boilerplate_elements_kind = instr->hydrogen()->boilerplate_elements_kind(); // Deopt if the array literal boilerplate ElementsKind is of a type different // than the expected one. The check isn't necessary if the boilerplate has - // already been converted to FAST_ELEMENTS. - if (boilerplate_elements_kind != FAST_ELEMENTS) { + // already been converted to TERMINAL_FAST_ELEMENTS_KIND. + if (CanTransitionToMoreGeneralFastElementsKind( + boilerplate_elements_kind, true)) { __ LoadHeapObject(rax, instr->hydrogen()->boilerplate_object()); __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset)); // Load the map's "bit field 2". @@ -4242,12 +4653,11 @@ void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) { } // Set up the parameters to the stub/runtime call. - __ movq(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); - __ push(FieldOperand(rax, JSFunction::kLiteralsOffset)); + __ PushHeapObject(literals); __ Push(Smi::FromInt(instr->hydrogen()->literal_index())); // Boilerplate already exists, constant elements are never accessed. // Pass an empty fixed array. - __ Push(Handle<FixedArray>(heap->empty_fixed_array())); + __ Push(isolate()->factory()->empty_fixed_array()); // Pick the right runtime function or stub to call. int length = instr->hydrogen()->length(); @@ -4375,10 +4785,11 @@ void LCodeGen::DoFastLiteral(LFastLiteral* instr) { ElementsKind boilerplate_elements_kind = instr->hydrogen()->boilerplate()->GetElementsKind(); - // Deopt if the literal boilerplate ElementsKind is of a type different than - // the expected one. The check isn't necessary if the boilerplate has already - // been converted to FAST_ELEMENTS. - if (boilerplate_elements_kind != FAST_ELEMENTS) { + // Deopt if the array literal boilerplate ElementsKind is of a type different + // than the expected one. The check isn't necessary if the boilerplate has + // already been converted to TERMINAL_FAST_ELEMENTS_KIND. + if (CanTransitionToMoreGeneralFastElementsKind( + boilerplate_elements_kind, true)) { __ LoadHeapObject(rbx, instr->hydrogen()->boilerplate()); __ movq(rcx, FieldOperand(rbx, HeapObject::kMapOffset)); // Load the map's "bit field 2". @@ -4440,7 +4851,7 @@ void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) { void LCodeGen::DoToFastProperties(LToFastProperties* instr) { - ASSERT(ToRegister(instr->InputAt(0)).is(rax)); + ASSERT(ToRegister(instr->value()).is(rax)); __ push(rax); CallRuntime(Runtime::kToFastProperties, 1, instr); } @@ -4449,14 +4860,12 @@ void LCodeGen::DoToFastProperties(LToFastProperties* instr) { void LCodeGen::DoRegExpLiteral(LRegExpLiteral* instr) { Label materialized; // Registers will be used as follows: - // rdi = JS function. // rcx = literals array. // rbx = regexp literal. // rax = regexp literal clone. - __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); - __ movq(rcx, FieldOperand(rdi, JSFunction::kLiteralsOffset)); - int literal_offset = FixedArray::kHeaderSize + - instr->hydrogen()->literal_index() * kPointerSize; + int literal_offset = + FixedArray::OffsetOfElementAt(instr->hydrogen()->literal_index()); + __ LoadHeapObject(rcx, instr->hydrogen()->literals()); __ movq(rbx, FieldOperand(rcx, literal_offset)); __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex); __ j(not_equal, &materialized, Label::kNear); @@ -4519,7 +4928,7 @@ void LCodeGen::DoFunctionLiteral(LFunctionLiteral* instr) { void LCodeGen::DoTypeof(LTypeof* instr) { - LOperand* input = instr->InputAt(0); + LOperand* input = instr->value(); EmitPushTaggedOperand(input); CallRuntime(Runtime::kTypeof, 1, instr); } @@ -4543,7 +4952,7 @@ void LCodeGen::EmitPushTaggedOperand(LOperand* operand) { void LCodeGen::DoTypeofIsAndBranch(LTypeofIsAndBranch* instr) { - Register input = ToRegister(instr->InputAt(0)); + Register input = ToRegister(instr->value()); 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); @@ -4629,7 +5038,7 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label, void LCodeGen::DoIsConstructCallAndBranch(LIsConstructCallAndBranch* instr) { - Register temp = ToRegister(instr->TempAt(0)); + Register temp = ToRegister(instr->temp()); int true_block = chunk_->LookupDestination(instr->true_block_id()); int false_block = chunk_->LookupDestination(instr->false_block_id()); @@ -4756,7 +5165,7 @@ void LCodeGen::DoStackCheck(LStackCheck* instr) { ASSERT(instr->hydrogen()->is_backwards_branch()); // Perform stack overflow check if this goto needs it before jumping. DeferredStackCheck* deferred_stack_check = - new DeferredStackCheck(this, instr); + new(zone()) DeferredStackCheck(this, instr); __ CompareRoot(rsp, Heap::kStackLimitRootIndex); __ j(below, deferred_stack_check->entry()); EnsureSpaceForLazyDeopt(Deoptimizer::patch_size()); @@ -4825,11 +5234,19 @@ void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) { void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) { Register map = ToRegister(instr->map()); Register result = ToRegister(instr->result()); + Label load_cache, done; + __ EnumLength(result, map); + __ Cmp(result, Smi::FromInt(0)); + __ j(not_equal, &load_cache); + __ LoadRoot(result, Heap::kEmptyFixedArrayRootIndex); + __ jmp(&done); + __ bind(&load_cache); __ LoadInstanceDescriptors(map, result); __ movq(result, - FieldOperand(result, DescriptorArray::kEnumerationIndexOffset)); + FieldOperand(result, DescriptorArray::kEnumCacheOffset)); __ movq(result, FieldOperand(result, FixedArray::SizeFor(instr->idx()))); + __ bind(&done); Condition cc = masm()->CheckSmi(result); DeoptimizeIf(cc, instr->environment()); } diff --git a/src/3rdparty/v8/src/x64/lithium-codegen-x64.h b/src/3rdparty/v8/src/x64/lithium-codegen-x64.h index 73e1a9b..0f8a62a 100644 --- a/src/3rdparty/v8/src/x64/lithium-codegen-x64.h +++ b/src/3rdparty/v8/src/x64/lithium-codegen-x64.h @@ -46,21 +46,24 @@ class SafepointGenerator; class LCodeGen BASE_EMBEDDED { public: LCodeGen(LChunk* chunk, MacroAssembler* assembler, CompilationInfo* info) - : chunk_(chunk), + : zone_(info->zone()), + chunk_(static_cast<LPlatformChunk*>(chunk)), masm_(assembler), info_(info), current_block_(-1), current_instruction_(-1), instructions_(chunk->instructions()), - deoptimizations_(4), - jump_table_(4), - deoptimization_literals_(8), + deoptimizations_(4, info->zone()), + jump_table_(4, info->zone()), + deoptimization_literals_(8, info->zone()), inlined_function_count_(0), scope_(info->scope()), status_(UNUSED), - deferred_(8), + translations_(info->zone()), + deferred_(8, info->zone()), osr_pc_offset_(-1), last_lazy_deopt_pc_(0), + safepoints_(info->zone()), resolver_(this), expected_safepoint_kind_(Safepoint::kSimple) { PopulateDeoptimizationLiteralsWithInlinedFunctions(); @@ -72,6 +75,7 @@ class LCodeGen BASE_EMBEDDED { Isolate* isolate() const { return info_->isolate(); } Factory* factory() const { return isolate()->factory(); } Heap* heap() const { return isolate()->heap(); } + Zone* zone() const { return zone_; } // Support for converting LOperands to assembler types. Register ToRegister(LOperand* op) const; @@ -94,6 +98,7 @@ class LCodeGen BASE_EMBEDDED { // Deferred code support. void DoDeferredNumberTagD(LNumberTagD* instr); + void DoDeferredNumberTagU(LNumberTagU* instr); void DoDeferredTaggedToI(LTaggedToI* instr); void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr); void DoDeferredStackCheck(LStackCheck* instr); @@ -112,7 +117,10 @@ class LCodeGen BASE_EMBEDDED { void DoGap(LGap* instr); // Emit frame translation commands for an environment. - void WriteTranslation(LEnvironment* environment, Translation* translation); + void WriteTranslation(LEnvironment* environment, + Translation* translation, + int* arguments_index, + int* arguments_count); // Declare methods that deal with the individual node types. #define DECLARE_DO(type) void Do##type(L##type* node); @@ -136,7 +144,7 @@ class LCodeGen BASE_EMBEDDED { return info()->is_classic_mode() ? kNonStrictMode : kStrictMode; } - LChunk* chunk() const { return chunk_; } + LPlatformChunk* chunk() const { return chunk_; } Scope* scope() const { return scope_; } HGraph* graph() const { return chunk_->graph(); } @@ -152,10 +160,10 @@ class LCodeGen BASE_EMBEDDED { int GetStackSlotCount() const { return chunk()->spill_slot_count(); } int GetParameterCount() const { return scope()->num_parameters(); } - void Abort(const char* format, ...); + void Abort(const char* reason); void Comment(const char* format, ...); - void AddDeferredCode(LDeferredCode* code) { deferred_.Add(code); } + void AddDeferredCode(LDeferredCode* code) { deferred_.Add(code, zone()); } // Code generation passes. Returns true if code generation should // continue. @@ -219,7 +227,10 @@ class LCodeGen BASE_EMBEDDED { void AddToTranslation(Translation* translation, LOperand* op, - bool is_tagged); + bool is_tagged, + bool is_uint32, + int arguments_index, + int arguments_count); void PopulateDeoptimizationData(Handle<Code> code); int DefineDeoptimizationLiteral(Handle<Object> literal); @@ -267,6 +278,11 @@ class LCodeGen BASE_EMBEDDED { bool deoptimize_on_minus_zero, LEnvironment* env); + + void DeoptIfTaggedButNotSmi(LEnvironment* environment, + HValue* value, + LOperand* operand); + // 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. @@ -296,7 +312,8 @@ class LCodeGen BASE_EMBEDDED { void EmitLoadFieldOrConstantFunction(Register result, Register object, Handle<Map> type, - Handle<String> name); + Handle<String> name, + LEnvironment* env); // Emits code for pushing either a tagged constant, a (non-double) // register, or a stack slot operand. @@ -318,8 +335,17 @@ class LCodeGen BASE_EMBEDDED { }; void EnsureSpaceForLazyDeopt(int space_needed); - - LChunk* const chunk_; + void DoLoadKeyedExternalArray(LLoadKeyed* instr); + void DoLoadKeyedFixedDoubleArray(LLoadKeyed* instr); + void DoLoadKeyedFixedArray(LLoadKeyed* instr); + void DoStoreKeyedExternalArray(LStoreKeyed* instr); + void DoStoreKeyedFixedDoubleArray(LStoreKeyed* instr); + void DoStoreKeyedFixedArray(LStoreKeyed* instr); + template <class T> + void PrepareKeyForKeyedOp(T* hydrogen_instr, LOperand* key); + + Zone* zone_; + LPlatformChunk* const chunk_; MacroAssembler* const masm_; CompilationInfo* const info_; diff --git a/src/3rdparty/v8/src/x64/lithium-gap-resolver-x64.cc b/src/3rdparty/v8/src/x64/lithium-gap-resolver-x64.cc index 877ea8c..22183a2 100644 --- a/src/3rdparty/v8/src/x64/lithium-gap-resolver-x64.cc +++ b/src/3rdparty/v8/src/x64/lithium-gap-resolver-x64.cc @@ -36,7 +36,7 @@ namespace v8 { namespace internal { LGapResolver::LGapResolver(LCodeGen* owner) - : cgen_(owner), moves_(32) {} + : cgen_(owner), moves_(32, owner->zone()) {} void LGapResolver::Resolve(LParallelMove* parallel_move) { @@ -74,7 +74,7 @@ void LGapResolver::BuildInitialMoveList(LParallelMove* parallel_move) { const ZoneList<LMoveOperands>* moves = parallel_move->move_operands(); for (int i = 0; i < moves->length(); ++i) { LMoveOperands move = moves->at(i); - if (!move.IsRedundant()) moves_.Add(move); + if (!move.IsRedundant()) moves_.Add(move, cgen_->zone()); } Verify(); } diff --git a/src/3rdparty/v8/src/x64/lithium-x64.cc b/src/3rdparty/v8/src/x64/lithium-x64.cc index 6d723a5..3985dc0 100644 --- a/src/3rdparty/v8/src/x64/lithium-x64.cc +++ b/src/3rdparty/v8/src/x64/lithium-x64.cc @@ -179,6 +179,7 @@ const char* LArithmeticT::Mnemonic() const { case Token::BIT_AND: return "bit-and-t"; case Token::BIT_OR: return "bit-or-t"; case Token::BIT_XOR: return "bit-xor-t"; + case Token::ROR: return "ror-t"; case Token::SHL: return "sal-t"; case Token::SAR: return "sar-t"; case Token::SHR: return "shr-t"; @@ -196,22 +197,22 @@ void LGoto::PrintDataTo(StringStream* stream) { void LBranch::PrintDataTo(StringStream* stream) { stream->Add("B%d | B%d on ", true_block_id(), false_block_id()); - InputAt(0)->PrintTo(stream); + value()->PrintTo(stream); } void LCmpIDAndBranch::PrintDataTo(StringStream* stream) { stream->Add("if "); - InputAt(0)->PrintTo(stream); + left()->PrintTo(stream); stream->Add(" %s ", Token::String(op())); - InputAt(1)->PrintTo(stream); + right()->PrintTo(stream); stream->Add(" then B%d else B%d", true_block_id(), false_block_id()); } void LIsNilAndBranch::PrintDataTo(StringStream* stream) { stream->Add("if "); - InputAt(0)->PrintTo(stream); + value()->PrintTo(stream); stream->Add(kind() == kStrictEquality ? " === " : " == "); stream->Add(nil() == kNullValue ? "null" : "undefined"); stream->Add(" then B%d else B%d", true_block_id(), false_block_id()); @@ -220,57 +221,57 @@ void LIsNilAndBranch::PrintDataTo(StringStream* stream) { void LIsObjectAndBranch::PrintDataTo(StringStream* stream) { stream->Add("if is_object("); - InputAt(0)->PrintTo(stream); + value()->PrintTo(stream); stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); } void LIsStringAndBranch::PrintDataTo(StringStream* stream) { stream->Add("if is_string("); - InputAt(0)->PrintTo(stream); + value()->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("); - InputAt(0)->PrintTo(stream); + value()->PrintTo(stream); stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); } void LIsUndetectableAndBranch::PrintDataTo(StringStream* stream) { stream->Add("if is_undetectable("); - InputAt(0)->PrintTo(stream); + value()->PrintTo(stream); stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); } void LStringCompareAndBranch::PrintDataTo(StringStream* stream) { stream->Add("if string_compare("); - InputAt(0)->PrintTo(stream); - InputAt(1)->PrintTo(stream); + left()->PrintTo(stream); + right()->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("); - InputAt(0)->PrintTo(stream); + value()->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("); - InputAt(0)->PrintTo(stream); + value()->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("); - InputAt(0)->PrintTo(stream); + value()->PrintTo(stream); stream->Add(", \"%o\") then B%d else B%d", *hydrogen()->class_name(), true_block_id(), @@ -280,7 +281,7 @@ void LClassOfTestAndBranch::PrintDataTo(StringStream* stream) { void LTypeofIsAndBranch::PrintDataTo(StringStream* stream) { stream->Add("if typeof "); - InputAt(0)->PrintTo(stream); + value()->PrintTo(stream); stream->Add(" == \"%s\" then B%d else B%d", *hydrogen()->type_literal()->ToCString(), true_block_id(), false_block_id()); @@ -294,26 +295,26 @@ void LCallConstantFunction::PrintDataTo(StringStream* stream) { void LUnaryMathOperation::PrintDataTo(StringStream* stream) { stream->Add("/%s ", hydrogen()->OpName()); - InputAt(0)->PrintTo(stream); + value()->PrintTo(stream); } void LLoadContextSlot::PrintDataTo(StringStream* stream) { - InputAt(0)->PrintTo(stream); + context()->PrintTo(stream); stream->Add("[%d]", slot_index()); } void LStoreContextSlot::PrintDataTo(StringStream* stream) { - InputAt(0)->PrintTo(stream); + context()->PrintTo(stream); stream->Add("[%d] <- ", slot_index()); - InputAt(1)->PrintTo(stream); + value()->PrintTo(stream); } void LInvokeFunction::PrintDataTo(StringStream* stream) { stream->Add("= "); - InputAt(0)->PrintTo(stream); + function()->PrintTo(stream); stream->Add(" #%d / ", arity()); } @@ -342,7 +343,7 @@ void LCallKnownGlobal::PrintDataTo(StringStream* stream) { void LCallNew::PrintDataTo(StringStream* stream) { stream->Add("= "); - InputAt(0)->PrintTo(stream); + constructor()->PrintTo(stream); stream->Add(" #%d / ", arity()); } @@ -358,56 +359,20 @@ void LAccessArgumentsAt::PrintDataTo(StringStream* stream) { } -int LChunk::GetNextSpillIndex(bool is_double) { +int LPlatformChunk::GetNextSpillIndex(bool is_double) { return spill_slot_count_++; } -LOperand* LChunk::GetNextSpillSlot(bool is_double) { +LOperand* LPlatformChunk::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); + return LDoubleStackSlot::Create(index, zone()); } else { - return LStackSlot::Create(index); - } -} - - -void LChunk::MarkEmptyBlocks() { - HPhase phase("L_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 (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())); - } - } - } + return LStackSlot::Create(index, zone()); } } @@ -430,16 +395,7 @@ void LStoreNamedGeneric::PrintDataTo(StringStream* stream) { } -void LStoreKeyedFastElement::PrintDataTo(StringStream* stream) { - object()->PrintTo(stream); - stream->Add("["); - key()->PrintTo(stream); - stream->Add("] <- "); - value()->PrintTo(stream); -} - - -void LStoreKeyedFastDoubleElement::PrintDataTo(StringStream* stream) { +void LStoreKeyed::PrintDataTo(StringStream* stream) { elements()->PrintTo(stream); stream->Add("["); key()->PrintTo(stream); @@ -463,83 +419,9 @@ void LTransitionElementsKind::PrintDataTo(StringStream* stream) { } -void LChunk::AddInstruction(LInstruction* instr, HBasicBlock* block) { - LInstructionGap* gap = new(graph_->zone()) LInstructionGap(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); - } -} - - -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 - 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 + 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() { +LPlatformChunk* LChunkBuilder::Build() { ASSERT(is_unused()); - chunk_ = new(zone()) LChunk(info(), graph()); + chunk_ = new(zone()) LPlatformChunk(info(), graph()); HPhase phase("L_Building chunk", chunk_); status_ = BUILDING; const ZoneList<HBasicBlock*>* blocks = graph()->blocks(); @@ -554,17 +436,8 @@ LChunk* LChunkBuilder::Build() { } -void LChunkBuilder::Abort(const char* format, ...) { - if (FLAG_trace_bailout) { - SmartArrayPointer<char> name( - info()->shared_info()->DebugName()->ToCString()); - PrintF("Aborting LChunk building in @\"%s\": ", *name); - va_list arguments; - va_start(arguments, format); - OS::VPrint(format, arguments); - va_end(arguments); - PrintF("\n"); - } +void LCodeGen::Abort(const char* reason) { + info()->set_bailout_reason(reason); status_ = ABORTED; } @@ -735,7 +608,7 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr, ASSERT(hinstr->next()->IsSimulate()); HSimulate* sim = HSimulate::cast(hinstr->next()); ASSERT(instruction_pending_deoptimization_environment_ == NULL); - ASSERT(pending_deoptimization_ast_id_ == AstNode::kNoNumber); + ASSERT(pending_deoptimization_ast_id_.IsNone()); instruction_pending_deoptimization_environment_ = instr; pending_deoptimization_ast_id_ = sim->ast_id(); } @@ -757,7 +630,7 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr, LInstruction* LChunkBuilder::AssignPointerMap(LInstruction* instr) { ASSERT(!instr->HasPointerMap()); - instr->set_pointer_map(new(zone()) LPointerMap(position_)); + instr->set_pointer_map(new(zone()) LPointerMap(position_, zone())); return instr; } @@ -830,13 +703,16 @@ LInstruction* LChunkBuilder::DoShift(Token::Value op, // Shift operations can only deoptimize if we do a logical shift by 0 and // the result cannot be truncated to int32. - bool may_deopt = (op == Token::SHR && constant_value == 0); bool does_deopt = false; - if (may_deopt) { - for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) { - if (!it.value()->CheckFlag(HValue::kTruncatingToInt32)) { - does_deopt = true; - break; + if (op == Token::SHR && constant_value == 0) { + if (FLAG_opt_safe_uint32_operations) { + does_deopt = !instr->CheckFlag(HInstruction::kUint32); + } else { + for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) { + if (!it.value()->CheckFlag(HValue::kTruncatingToInt32)) { + does_deopt = true; + break; + } } } } @@ -969,8 +845,8 @@ LEnvironment* LChunkBuilder::CreateEnvironment( LEnvironment* outer = CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator); - int ast_id = hydrogen_env->ast_id(); - ASSERT(ast_id != AstNode::kNoNumber || + BailoutId ast_id = hydrogen_env->ast_id(); + ASSERT(!ast_id.IsNone() || hydrogen_env->frame_type() != JS_FUNCTION); int value_count = hydrogen_env->length(); LEnvironment* result = new(zone()) LEnvironment( @@ -980,7 +856,9 @@ LEnvironment* LChunkBuilder::CreateEnvironment( hydrogen_env->parameter_count(), argument_count_, value_count, - outer); + outer, + hydrogen_env->entry(), + zone()); int argument_index = *argument_index_accumulator; for (int i = 0; i < value_count; ++i) { if (hydrogen_env->is_special_index(i)) continue; @@ -994,7 +872,9 @@ LEnvironment* LChunkBuilder::CreateEnvironment( } else { op = UseAny(value); } - result->AddValue(op, value->representation()); + result->AddValue(op, + value->representation(), + value->CheckFlag(HInstruction::kUint32)); } if (hydrogen_env->frame_type() == JS_FUNCTION) { @@ -1221,6 +1101,11 @@ LInstruction* LChunkBuilder::DoCallRuntime(HCallRuntime* instr) { } +LInstruction* LChunkBuilder::DoRor(HRor* instr) { + return DoShift(Token::ROR, instr); +} + + LInstruction* LChunkBuilder::DoShr(HShr* instr) { return DoShift(Token::SHR, instr); } @@ -1285,12 +1170,55 @@ LInstruction* LChunkBuilder::DoDiv(HDiv* instr) { } -LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) { - UNIMPLEMENTED(); +HValue* LChunkBuilder::SimplifiedDividendForMathFloorOfDiv(HValue* dividend) { + // A value with an integer representation does not need to be transformed. + if (dividend->representation().IsInteger32()) { + return dividend; + // A change from an integer32 can be replaced by the integer32 value. + } else if (dividend->IsChange() && + HChange::cast(dividend)->from().IsInteger32()) { + return HChange::cast(dividend)->value(); + } + return NULL; +} + + +HValue* LChunkBuilder::SimplifiedDivisorForMathFloorOfDiv(HValue* divisor) { + if (divisor->IsConstant() && + HConstant::cast(divisor)->HasInteger32Value()) { + HConstant* constant_val = HConstant::cast(divisor); + return constant_val->CopyToRepresentation(Representation::Integer32(), + divisor->block()->zone()); + } return NULL; } +LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) { + HValue* right = instr->right(); + ASSERT(right->IsConstant() && HConstant::cast(right)->HasInteger32Value()); + LOperand* divisor = chunk_->DefineConstantOperand(HConstant::cast(right)); + int32_t divisor_si = HConstant::cast(right)->Integer32Value(); + if (divisor_si == 0) { + LOperand* dividend = UseRegister(instr->left()); + return AssignEnvironment(DefineAsRegister( + new(zone()) LMathFloorOfDiv(dividend, divisor, NULL))); + } else if (IsPowerOf2(abs(divisor_si))) { + LOperand* dividend = UseRegisterAtStart(instr->left()); + LInstruction* result = DefineAsRegister( + new(zone()) LMathFloorOfDiv(dividend, divisor, NULL)); + return divisor_si < 0 ? AssignEnvironment(result) : result; + } else { + // use two r64 + LOperand* dividend = UseRegisterAtStart(instr->left()); + LOperand* temp = TempRegister(); + LInstruction* result = DefineAsRegister( + new(zone()) LMathFloorOfDiv(dividend, divisor, temp)); + return divisor_si < 0 ? AssignEnvironment(result) : result; + } +} + + LInstruction* LChunkBuilder::DoMod(HMod* instr) { if (instr->representation().IsInteger32()) { ASSERT(instr->left()->representation().IsInteger32()); @@ -1396,6 +1324,26 @@ LInstruction* LChunkBuilder::DoAdd(HAdd* instr) { } +LInstruction* LChunkBuilder::DoMathMinMax(HMathMinMax* instr) { + LOperand* left = NULL; + LOperand* right = NULL; + if (instr->representation().IsInteger32()) { + ASSERT(instr->left()->representation().IsInteger32()); + ASSERT(instr->right()->representation().IsInteger32()); + left = UseRegisterAtStart(instr->LeastConstantOperand()); + right = UseOrConstantAtStart(instr->MostConstantOperand()); + } else { + ASSERT(instr->representation().IsDouble()); + ASSERT(instr->left()->representation().IsDouble()); + ASSERT(instr->right()->representation().IsDouble()); + left = UseRegisterAtStart(instr->left()); + right = UseRegisterAtStart(instr->right()); + } + LMathMinMax* minmax = new(zone()) LMathMinMax(left, right); + return DefineSameAsFirst(minmax); +} + + LInstruction* LChunkBuilder::DoPower(HPower* instr) { ASSERT(instr->representation().IsDouble()); // We call a C function for double power. It can't trigger a GC. @@ -1578,6 +1526,12 @@ LInstruction* LChunkBuilder::DoFixedArrayBaseLength( } +LInstruction* LChunkBuilder::DoMapEnumLength(HMapEnumLength* instr) { + LOperand* map = UseRegisterAtStart(instr->value()); + return DefineAsRegister(new(zone()) LMapEnumLength(map)); +} + + LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) { LOperand* object = UseRegisterAtStart(instr->value()); return DefineAsRegister(new(zone()) LElementsKind(object)); @@ -1593,8 +1547,8 @@ LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) { LInstruction* LChunkBuilder::DoDateField(HDateField* instr) { LOperand* object = UseFixed(instr->value(), rax); - LDateField* result = new LDateField(object, instr->index()); - return MarkAsCall(DefineFixed(result, rax), instr); + LDateField* result = new(zone()) LDateField(object, instr->index()); + return MarkAsCall(DefineFixed(result, rax), instr, CAN_DEOPTIMIZE_EAGERLY); } @@ -1642,14 +1596,13 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { } else { ASSERT(to.IsInteger32()); LOperand* value = UseRegister(instr->value()); - bool needs_check = !instr->value()->type().IsSmi(); - if (needs_check) { + if (instr->value()->type().IsSmi()) { + return DefineSameAsFirst(new(zone()) LSmiUntag(value, false)); + } else { bool truncating = instr->CanTruncateToInt32(); LOperand* xmm_temp = truncating ? NULL : FixedTemp(xmm1); LTaggedToI* res = new(zone()) LTaggedToI(value, xmm_temp); return AssignEnvironment(DefineSameAsFirst(res)); - } else { - return DefineSameAsFirst(new(zone()) LSmiUntag(value, needs_check)); } } } else if (from.IsDouble()) { @@ -1670,16 +1623,26 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) { if (to.IsTagged()) { HValue* val = instr->value(); LOperand* value = UseRegister(val); - if (val->HasRange() && val->range()->IsInSmiRange()) { + if (val->CheckFlag(HInstruction::kUint32)) { + LOperand* temp = FixedTemp(xmm1); + LNumberTagU* result = new(zone()) LNumberTagU(value, temp); + return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result))); + } else if (val->HasRange() && val->range()->IsInSmiRange()) { return DefineSameAsFirst(new(zone()) LSmiTag(value)); } else { LNumberTagI* result = new(zone()) LNumberTagI(value); return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result))); } } else { - ASSERT(to.IsDouble()); - LOperand* value = Use(instr->value()); - return DefineAsRegister(new(zone()) LInteger32ToDouble(value)); + if (instr->value()->CheckFlag(HInstruction::kUint32)) { + LOperand* temp = FixedTemp(xmm1); + return DefineAsRegister( + new(zone()) LUint32ToDouble(UseRegister(instr->value()), temp)); + } else { + ASSERT(to.IsDouble()); + LOperand* value = Use(instr->value()); + return DefineAsRegister(new(zone()) LInteger32ToDouble(value)); + } } } UNREACHABLE(); @@ -1701,9 +1664,9 @@ LInstruction* LChunkBuilder::DoCheckInstanceType(HCheckInstanceType* instr) { LInstruction* LChunkBuilder::DoCheckPrototypeMaps(HCheckPrototypeMaps* instr) { - LOperand* temp = TempRegister(); + LUnallocated* temp = TempRegister(); LCheckPrototypeMaps* result = new(zone()) LCheckPrototypeMaps(temp); - return AssignEnvironment(result); + return AssignEnvironment(Define(result, temp)); } @@ -1731,8 +1694,7 @@ LInstruction* LChunkBuilder::DoClampToUint8(HClampToUint8* instr) { Representation input_rep = value->representation(); LOperand* reg = UseRegister(value); if (input_rep.IsDouble()) { - return DefineAsRegister(new(zone()) LClampDToUint8(reg, - TempRegister())); + return DefineAsRegister(new(zone()) LClampDToUint8(reg)); } else if (input_rep.IsInteger32()) { return DefineSameAsFirst(new(zone()) LClampIToUint8(reg)); } else { @@ -1740,7 +1702,6 @@ LInstruction* LChunkBuilder::DoClampToUint8(HClampToUint8* instr) { // Register allocator doesn't (yet) support allocation of double // temps. Reserve xmm1 explicitly. LClampTToUint8* result = new(zone()) LClampTToUint8(reg, - TempRegister(), FixedTemp(xmm1)); return AssignEnvironment(DefineSameAsFirst(result)); } @@ -1879,50 +1840,35 @@ LInstruction* LChunkBuilder::DoLoadExternalArrayPointer( } -LInstruction* LChunkBuilder::DoLoadKeyedFastElement( - HLoadKeyedFastElement* instr) { - ASSERT(instr->representation().IsTagged()); - ASSERT(instr->key()->representation().IsInteger32()); - LOperand* obj = UseRegisterAtStart(instr->object()); - LOperand* key = UseRegisterOrConstantAtStart(instr->key()); - LLoadKeyedFastElement* result = new(zone()) LLoadKeyedFastElement(obj, key); - if (instr->RequiresHoleCheck()) AssignEnvironment(result); - return DefineAsRegister(result); -} - - -LInstruction* LChunkBuilder::DoLoadKeyedFastDoubleElement( - HLoadKeyedFastDoubleElement* instr) { - ASSERT(instr->representation().IsDouble()); - ASSERT(instr->key()->representation().IsInteger32()); +LInstruction* LChunkBuilder::DoLoadKeyed(HLoadKeyed* instr) { + ASSERT(instr->key()->representation().IsInteger32() || + instr->key()->representation().IsTagged()); + ElementsKind elements_kind = instr->elements_kind(); + bool clobbers_key = ArrayOpClobbersKey<HLoadKeyed>(instr); + LOperand* key = clobbers_key + ? UseTempRegister(instr->key()) + : UseRegisterOrConstantAtStart(instr->key()); LOperand* elements = UseRegisterAtStart(instr->elements()); - LOperand* key = UseRegisterOrConstantAtStart(instr->key()); - LLoadKeyedFastDoubleElement* result = - new(zone()) LLoadKeyedFastDoubleElement(elements, key); - return AssignEnvironment(DefineAsRegister(result)); -} + LLoadKeyed* result = new(zone()) LLoadKeyed(elements, key); +#ifdef DEBUG + if (instr->is_external()) { + ASSERT( + (instr->representation().IsInteger32() && + (elements_kind != EXTERNAL_FLOAT_ELEMENTS) && + (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) || + (instr->representation().IsDouble() && + ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) || + (elements_kind == EXTERNAL_DOUBLE_ELEMENTS)))); + } +#endif -LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement( - HLoadKeyedSpecializedArrayElement* instr) { - ElementsKind elements_kind = instr->elements_kind(); - ASSERT( - (instr->representation().IsInteger32() && - (elements_kind != EXTERNAL_FLOAT_ELEMENTS) && - (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) || - (instr->representation().IsDouble() && - ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) || - (elements_kind == EXTERNAL_DOUBLE_ELEMENTS)))); - ASSERT(instr->key()->representation().IsInteger32()); - LOperand* external_pointer = UseRegister(instr->external_pointer()); - LOperand* key = UseRegisterOrConstant(instr->key()); - LLoadKeyedSpecializedArrayElement* result = - new(zone()) LLoadKeyedSpecializedArrayElement(external_pointer, key); - LInstruction* load_instr = DefineAsRegister(result); + DefineAsRegister(result); + bool can_deoptimize = instr->RequiresHoleCheck() || + (elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS); // An unsigned int array load might overflow and cause a deopt, make sure it // has an environment. - return (elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) ? - AssignEnvironment(load_instr) : load_instr; + return can_deoptimize ? AssignEnvironment(result) : result; } @@ -1935,63 +1881,36 @@ LInstruction* LChunkBuilder::DoLoadKeyedGeneric(HLoadKeyedGeneric* instr) { } -LInstruction* LChunkBuilder::DoStoreKeyedFastElement( - HStoreKeyedFastElement* instr) { +LInstruction* LChunkBuilder::DoStoreKeyed(HStoreKeyed* instr) { bool needs_write_barrier = instr->NeedsWriteBarrier(); - ASSERT(instr->value()->representation().IsTagged()); - ASSERT(instr->object()->representation().IsTagged()); - ASSERT(instr->key()->representation().IsInteger32()); - - LOperand* obj = UseTempRegister(instr->object()); + bool clobbers_key = ArrayOpClobbersKey<HStoreKeyed>(instr); + LOperand* key = (clobbers_key || needs_write_barrier) + ? UseTempRegister(instr->key()) + : UseRegisterOrConstantAtStart(instr->key()); LOperand* val = needs_write_barrier ? UseTempRegister(instr->value()) : UseRegisterAtStart(instr->value()); - LOperand* key = needs_write_barrier - ? UseTempRegister(instr->key()) - : UseRegisterOrConstantAtStart(instr->key()); - return new(zone()) LStoreKeyedFastElement(obj, key, val); -} - - -LInstruction* LChunkBuilder::DoStoreKeyedFastDoubleElement( - HStoreKeyedFastDoubleElement* instr) { - ASSERT(instr->value()->representation().IsDouble()); - ASSERT(instr->elements()->representation().IsTagged()); - ASSERT(instr->key()->representation().IsInteger32()); - LOperand* elements = UseRegisterAtStart(instr->elements()); - LOperand* val = UseTempRegister(instr->value()); - LOperand* key = UseRegisterOrConstantAtStart(instr->key()); - return new(zone()) LStoreKeyedFastDoubleElement(elements, key, val); -} - - -LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement( - HStoreKeyedSpecializedArrayElement* instr) { - ElementsKind elements_kind = instr->elements_kind(); - ASSERT( - (instr->value()->representation().IsInteger32() && - (elements_kind != EXTERNAL_FLOAT_ELEMENTS) && - (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) || - (instr->value()->representation().IsDouble() && - ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) || - (elements_kind == EXTERNAL_DOUBLE_ELEMENTS)))); - ASSERT(instr->external_pointer()->representation().IsExternal()); - ASSERT(instr->key()->representation().IsInteger32()); - - LOperand* external_pointer = UseRegister(instr->external_pointer()); - bool val_is_temp_register = - elements_kind == EXTERNAL_PIXEL_ELEMENTS || - elements_kind == EXTERNAL_FLOAT_ELEMENTS; - LOperand* val = val_is_temp_register - ? UseTempRegister(instr->value()) - : UseRegister(instr->value()); - LOperand* key = UseRegisterOrConstant(instr->key()); +#ifdef DEBUG + if (!instr->is_external()) { + ASSERT(instr->elements()->representation().IsTagged()); + } else { + ElementsKind elements_kind = instr->elements_kind(); + ASSERT( + (instr->value()->representation().IsInteger32() && + (elements_kind != EXTERNAL_FLOAT_ELEMENTS) && + (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) || + (instr->value()->representation().IsDouble() && + ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) || + (elements_kind == EXTERNAL_DOUBLE_ELEMENTS)))); + ASSERT(instr->elements()->representation().IsExternal()); + } +#endif - return new(zone()) LStoreKeyedSpecializedArrayElement(external_pointer, - key, - val); + LStoreKeyed* result = new(zone()) LStoreKeyed(elements, key, val); + ASSERT(result != NULL); + return result; } @@ -2012,8 +1931,9 @@ LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) { LInstruction* LChunkBuilder::DoTransitionElementsKind( HTransitionElementsKind* instr) { - if (instr->original_map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS && - instr->transitioned_map()->elements_kind() == FAST_ELEMENTS) { + ElementsKind from_kind = instr->original_map()->elements_kind(); + ElementsKind to_kind = instr->transitioned_map()->elements_kind(); + if (IsSimpleMapChangeTransition(from_kind, to_kind)) { LOperand* object = UseRegister(instr->object()); LOperand* new_map_reg = TempRegister(); LOperand* temp_reg = TempRegister(); @@ -2035,10 +1955,19 @@ LInstruction* LChunkBuilder::DoTransitionElementsKind( LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) { bool needs_write_barrier = instr->NeedsWriteBarrier(); - - LOperand* obj = needs_write_barrier - ? UseTempRegister(instr->object()) - : UseRegisterAtStart(instr->object()); + bool needs_write_barrier_for_map = !instr->transition().is_null() && + instr->NeedsWriteBarrierForMap(); + + LOperand* obj; + if (needs_write_barrier) { + obj = instr->is_in_object() + ? UseRegister(instr->object()) + : UseTempRegister(instr->object()); + } else { + obj = needs_write_barrier_for_map + ? UseRegister(instr->object()) + : UseRegisterAtStart(instr->object()); + } LOperand* val = needs_write_barrier ? UseTempRegister(instr->value()) @@ -2046,8 +1975,8 @@ LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) { // 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; + LOperand* temp = (!instr->is_in_object() || needs_write_barrier || + needs_write_barrier_for_map) ? TempRegister() : NULL; return new(zone()) LStoreNamedField(obj, val, temp); } @@ -2092,7 +2021,7 @@ LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) { LInstruction* LChunkBuilder::DoAllocateObject(HAllocateObject* instr) { - LAllocateObject* result = new LAllocateObject(TempRegister()); + LAllocateObject* result = new(zone()) LAllocateObject(TempRegister()); return AssignPointerMap(DefineAsRegister(result)); } @@ -2131,6 +2060,7 @@ LInstruction* LChunkBuilder::DoDeleteProperty(HDeleteProperty* instr) { LInstruction* LChunkBuilder::DoOsrEntry(HOsrEntry* instr) { + ASSERT(argument_count_ == 0); allocator_->MarkAsOsrEntry(); current_block_->last_environment()->set_ast_id(instr->ast_id()); return AssignEnvironment(new(zone()) LOsrEntry); @@ -2169,12 +2099,10 @@ LInstruction* LChunkBuilder::DoArgumentsObject(HArgumentsObject* instr) { LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) { - LOperand* arguments = UseRegister(instr->arguments()); + LOperand* args = UseRegister(instr->arguments()); LOperand* length = UseTempRegister(instr->length()); LOperand* index = Use(instr->index()); - LAccessArgumentsAt* result = - new(zone()) LAccessArgumentsAt(arguments, length, index); - return AssignEnvironment(DefineAsRegister(result)); + return DefineAsRegister(new(zone()) LAccessArgumentsAt(args, length, index)); } @@ -2228,7 +2156,7 @@ LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) { instruction_pending_deoptimization_environment_-> SetDeferredLazyDeoptimizationEnvironment(result->environment()); instruction_pending_deoptimization_environment_ = NULL; - pending_deoptimization_ast_id_ = AstNode::kNoNumber; + pending_deoptimization_ast_id_ = BailoutId::None(); return result; } @@ -2254,10 +2182,11 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) { instr->function(), undefined, instr->call_kind(), - instr->is_construct()); + instr->inlining_kind()); if (instr->arguments_var() != NULL) { inner->Bind(instr->arguments_var(), graph()->GetArgumentsObject()); } + inner->set_entry(instr); current_block_->UpdateEnvironment(inner); chunk_->AddInlinedClosure(instr->closure()); return NULL; @@ -2269,7 +2198,7 @@ LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) { HEnvironment* env = current_block_->last_environment(); - if (instr->arguments_pushed()) { + if (env->entry()->arguments_pushed()) { int argument_count = env->arguments_environment()->parameter_count(); pop = new(zone()) LDrop(argument_count); argument_count_ -= argument_count; diff --git a/src/3rdparty/v8/src/x64/lithium-x64.h b/src/3rdparty/v8/src/x64/lithium-x64.h index ac1a5db..a437a2b 100644 --- a/src/3rdparty/v8/src/x64/lithium-x64.h +++ b/src/3rdparty/v8/src/x64/lithium-x64.h @@ -96,6 +96,7 @@ class LCodeGen; V(ElementsKind) \ V(FastLiteral) \ V(FixedArrayBaseLength) \ + V(MapEnumLength) \ V(FunctionLiteral) \ V(GetCachedArrayIndex) \ V(GlobalObject) \ @@ -108,6 +109,7 @@ class LCodeGen; V(InstanceOfKnownGlobal) \ V(InstructionGap) \ V(Integer32ToDouble) \ + V(Uint32ToDouble) \ V(InvokeFunction) \ V(IsConstructCallAndBranch) \ V(IsNilAndBranch) \ @@ -115,7 +117,6 @@ class LCodeGen; V(IsStringAndBranch) \ V(IsSmiAndBranch) \ V(IsUndetectableAndBranch) \ - V(StringCompareAndBranch) \ V(JSArrayLength) \ V(Label) \ V(LazyBailout) \ @@ -125,17 +126,18 @@ class LCodeGen; V(LoadFunctionPrototype) \ V(LoadGlobalCell) \ V(LoadGlobalGeneric) \ - V(LoadKeyedFastDoubleElement) \ - V(LoadKeyedFastElement) \ + V(LoadKeyed) \ V(LoadKeyedGeneric) \ - V(LoadKeyedSpecializedArrayElement) \ V(LoadNamedField) \ V(LoadNamedFieldPolymorphic) \ V(LoadNamedGeneric) \ + V(MathFloorOfDiv) \ + V(MathMinMax) \ V(ModI) \ V(MulI) \ V(NumberTagD) \ V(NumberTagI) \ + V(NumberTagU) \ V(NumberUntagD) \ V(ObjectLiteral) \ V(OsrEntry) \ @@ -153,15 +155,14 @@ class LCodeGen; V(StoreContextSlot) \ V(StoreGlobalCell) \ V(StoreGlobalGeneric) \ - V(StoreKeyedFastDoubleElement) \ - V(StoreKeyedFastElement) \ + V(StoreKeyed) \ V(StoreKeyedGeneric) \ - V(StoreKeyedSpecializedArrayElement) \ V(StoreNamedField) \ V(StoreNamedGeneric) \ V(StringAdd) \ V(StringCharCodeAt) \ V(StringCharFromCode) \ + V(StringCompareAndBranch) \ V(StringLength) \ V(SubI) \ V(TaggedToI) \ @@ -257,11 +258,6 @@ class LInstruction: public ZoneObject { virtual bool HasResult() const = 0; virtual LOperand* result() = 0; - virtual int InputCount() = 0; - virtual LOperand* InputAt(int i) = 0; - virtual int TempCount() = 0; - virtual LOperand* TempAt(int i) = 0; - LOperand* FirstInput() { return InputAt(0); } LOperand* Output() { return HasResult() ? result() : NULL; } @@ -270,6 +266,15 @@ class LInstruction: public ZoneObject { #endif private: + // Iterator support. + friend class InputIterator; + virtual int InputCount() = 0; + virtual LOperand* InputAt(int i) = 0; + + friend class TempIterator; + virtual int TempCount() = 0; + virtual LOperand* TempAt(int i) = 0; + LEnvironment* environment_; SetOncePointer<LPointerMap> pointer_map_; HValue* hydrogen_value_; @@ -289,16 +294,18 @@ class LTemplateInstruction: public LInstruction { void set_result(LOperand* operand) { results_[0] = operand; } LOperand* result() { return results_[0]; } - int InputCount() { return I; } - LOperand* InputAt(int i) { return inputs_[i]; } - - int TempCount() { return T; } - LOperand* TempAt(int i) { return temps_[i]; } - protected: EmbeddedContainer<LOperand*, R> results_; EmbeddedContainer<LOperand*, I> inputs_; EmbeddedContainer<LOperand*, T> temps_; + + private: + // Iterator support. + virtual int InputCount() { return I; } + virtual LOperand* InputAt(int i) { return inputs_[i]; } + + virtual int TempCount() { return T; } + virtual LOperand* TempAt(int i) { return temps_[i]; } }; @@ -333,8 +340,11 @@ class LGap: public LTemplateInstruction<0, 0, 0> { LAST_INNER_POSITION = AFTER }; - LParallelMove* GetOrCreateParallelMove(InnerPosition pos) { - if (parallel_moves_[pos] == NULL) parallel_moves_[pos] = new LParallelMove; + LParallelMove* GetOrCreateParallelMove(InnerPosition pos, + Zone* zone) { + if (parallel_moves_[pos] == NULL) { + parallel_moves_[pos] = new(zone) LParallelMove(zone); + } return parallel_moves_[pos]; } @@ -462,10 +472,10 @@ class LWrapReceiver: public LTemplateInstruction<1, 2, 0> { inputs_[1] = function; } - DECLARE_CONCRETE_INSTRUCTION(WrapReceiver, "wrap-receiver") - LOperand* receiver() { return inputs_[0]; } LOperand* function() { return inputs_[1]; } + + DECLARE_CONCRETE_INSTRUCTION(WrapReceiver, "wrap-receiver") }; @@ -481,12 +491,12 @@ class LApplyArguments: public LTemplateInstruction<1, 4, 0> { inputs_[3] = elements; } - DECLARE_CONCRETE_INSTRUCTION(ApplyArguments, "apply-arguments") - LOperand* function() { return inputs_[0]; } LOperand* receiver() { return inputs_[1]; } LOperand* length() { return inputs_[2]; } LOperand* elements() { return inputs_[3]; } + + DECLARE_CONCRETE_INSTRUCTION(ApplyArguments, "apply-arguments") }; @@ -498,12 +508,12 @@ class LAccessArgumentsAt: public LTemplateInstruction<1, 3, 0> { inputs_[2] = index; } - DECLARE_CONCRETE_INSTRUCTION(AccessArgumentsAt, "access-arguments-at") - LOperand* arguments() { return inputs_[0]; } LOperand* length() { return inputs_[1]; } LOperand* index() { return inputs_[2]; } + DECLARE_CONCRETE_INSTRUCTION(AccessArgumentsAt, "access-arguments-at") + virtual void PrintDataTo(StringStream* stream); }; @@ -514,6 +524,8 @@ class LArgumentsLength: public LTemplateInstruction<1, 1, 0> { inputs_[0] = elements; } + LOperand* elements() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(ArgumentsLength, "arguments-length") }; @@ -533,6 +545,10 @@ class LModI: public LTemplateInstruction<1, 2, 1> { temps_[0] = temp; } + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } + LOperand* temp() { return temps_[0]; } + DECLARE_CONCRETE_INSTRUCTION(ModI, "mod-i") DECLARE_HYDROGEN_ACCESSOR(Mod) }; @@ -546,11 +562,34 @@ class LDivI: public LTemplateInstruction<1, 2, 1> { temps_[0] = temp; } + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } + LOperand* temp() { return temps_[0]; } + DECLARE_CONCRETE_INSTRUCTION(DivI, "div-i") DECLARE_HYDROGEN_ACCESSOR(Div) }; +class LMathFloorOfDiv: public LTemplateInstruction<1, 2, 1> { + public: + LMathFloorOfDiv(LOperand* left, + LOperand* right, + LOperand* temp = NULL) { + inputs_[0] = left; + inputs_[1] = right; + temps_[0] = temp; + } + + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } + LOperand* temp() { return temps_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(MathFloorOfDiv, "math-floor-of-div") + DECLARE_HYDROGEN_ACCESSOR(MathFloorOfDiv) +}; + + class LMulI: public LTemplateInstruction<1, 2, 0> { public: LMulI(LOperand* left, LOperand* right) { @@ -558,6 +597,9 @@ class LMulI: public LTemplateInstruction<1, 2, 0> { inputs_[1] = right; } + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } + DECLARE_CONCRETE_INSTRUCTION(MulI, "mul-i") DECLARE_HYDROGEN_ACCESSOR(Mul) }; @@ -570,6 +612,9 @@ class LCmpIDAndBranch: public LControlInstruction<2, 0> { inputs_[1] = right; } + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } + DECLARE_CONCRETE_INSTRUCTION(CmpIDAndBranch, "cmp-id-and-branch") DECLARE_HYDROGEN_ACCESSOR(CompareIDAndBranch) @@ -588,6 +633,8 @@ class LUnaryMathOperation: public LTemplateInstruction<1, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(UnaryMathOperation, "unary-math-operation") DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation) @@ -603,6 +650,9 @@ class LCmpObjectEqAndBranch: public LControlInstruction<2, 0> { inputs_[1] = right; } + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } + DECLARE_CONCRETE_INSTRUCTION(CmpObjectEqAndBranch, "cmp-object-eq-and-branch") }; @@ -614,6 +664,8 @@ class LCmpConstantEqAndBranch: public LControlInstruction<1, 0> { inputs_[0] = left; } + LOperand* left() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(CmpConstantEqAndBranch, "cmp-constant-eq-and-branch") DECLARE_HYDROGEN_ACCESSOR(CompareConstantEqAndBranch) @@ -627,6 +679,9 @@ class LIsNilAndBranch: public LControlInstruction<1, 1> { temps_[0] = temp; } + LOperand* value() { return inputs_[0]; } + LOperand* temp() { return temps_[0]; } + DECLARE_CONCRETE_INSTRUCTION(IsNilAndBranch, "is-nil-and-branch") DECLARE_HYDROGEN_ACCESSOR(IsNilAndBranch) @@ -643,6 +698,8 @@ class LIsObjectAndBranch: public LControlInstruction<1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(IsObjectAndBranch, "is-object-and-branch") DECLARE_HYDROGEN_ACCESSOR(IsObjectAndBranch) @@ -657,6 +714,9 @@ class LIsStringAndBranch: public LControlInstruction<1, 1> { temps_[0] = temp; } + LOperand* value() { return inputs_[0]; } + LOperand* temp() { return temps_[0]; } + DECLARE_CONCRETE_INSTRUCTION(IsStringAndBranch, "is-string-and-branch") DECLARE_HYDROGEN_ACCESSOR(IsStringAndBranch) @@ -670,6 +730,8 @@ class LIsSmiAndBranch: public LControlInstruction<1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(IsSmiAndBranch, "is-smi-and-branch") DECLARE_HYDROGEN_ACCESSOR(IsSmiAndBranch) @@ -684,6 +746,9 @@ class LIsUndetectableAndBranch: public LControlInstruction<1, 1> { temps_[0] = temp; } + LOperand* value() { return inputs_[0]; } + LOperand* temp() { return temps_[0]; } + DECLARE_CONCRETE_INSTRUCTION(IsUndetectableAndBranch, "is-undetectable-and-branch") DECLARE_HYDROGEN_ACCESSOR(IsUndetectableAndBranch) @@ -699,6 +764,9 @@ class LStringCompareAndBranch: public LControlInstruction<2, 0> { inputs_[1] = right; } + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } + DECLARE_CONCRETE_INSTRUCTION(StringCompareAndBranch, "string-compare-and-branch") DECLARE_HYDROGEN_ACCESSOR(StringCompareAndBranch) @@ -715,6 +783,8 @@ class LHasInstanceTypeAndBranch: public LControlInstruction<1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(HasInstanceTypeAndBranch, "has-instance-type-and-branch") DECLARE_HYDROGEN_ACCESSOR(HasInstanceTypeAndBranch) @@ -729,6 +799,8 @@ class LGetCachedArrayIndex: public LTemplateInstruction<1, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(GetCachedArrayIndex, "get-cached-array-index") DECLARE_HYDROGEN_ACCESSOR(GetCachedArrayIndex) }; @@ -740,6 +812,8 @@ class LHasCachedArrayIndexAndBranch: public LControlInstruction<1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(HasCachedArrayIndexAndBranch, "has-cached-array-index-and-branch") DECLARE_HYDROGEN_ACCESSOR(HasCachedArrayIndexAndBranch) @@ -756,6 +830,10 @@ class LClassOfTestAndBranch: public LControlInstruction<1, 2> { temps_[1] = temp2; } + LOperand* value() { return inputs_[0]; } + LOperand* temp() { return temps_[0]; } + LOperand* temp2() { return temps_[1]; } + DECLARE_CONCRETE_INSTRUCTION(ClassOfTestAndBranch, "class-of-test-and-branch") DECLARE_HYDROGEN_ACCESSOR(ClassOfTestAndBranch) @@ -771,6 +849,9 @@ class LCmpT: public LTemplateInstruction<1, 2, 0> { inputs_[1] = right; } + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } + DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t") DECLARE_HYDROGEN_ACCESSOR(CompareGeneric) @@ -799,6 +880,9 @@ class LInstanceOf: public LTemplateInstruction<1, 2, 0> { inputs_[1] = right; } + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } + DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of") }; @@ -810,6 +894,9 @@ class LInstanceOfKnownGlobal: public LTemplateInstruction<1, 1, 1> { temps_[0] = temp; } + LOperand* value() { return inputs_[0]; } + LOperand* temp() { return temps_[0]; } + DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal, "instance-of-known-global") DECLARE_HYDROGEN_ACCESSOR(InstanceOfKnownGlobal) @@ -838,6 +925,7 @@ class LBoundsCheck: public LTemplateInstruction<0, 2, 0> { LOperand* length() { return inputs_[1]; } DECLARE_CONCRETE_INSTRUCTION(BoundsCheck, "bounds-check") + DECLARE_HYDROGEN_ACCESSOR(BoundsCheck) }; @@ -848,6 +936,9 @@ class LBitI: public LTemplateInstruction<1, 2, 0> { inputs_[1] = right; } + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } + Token::Value op() const { return hydrogen()->op(); } DECLARE_CONCRETE_INSTRUCTION(BitI, "bit-i") @@ -864,7 +955,8 @@ class LShiftI: public LTemplateInstruction<1, 2, 0> { } Token::Value op() const { return op_; } - + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } bool can_deopt() const { return can_deopt_; } DECLARE_CONCRETE_INSTRUCTION(ShiftI, "shift-i") @@ -882,6 +974,9 @@ class LSubI: public LTemplateInstruction<1, 2, 0> { inputs_[1] = right; } + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } + DECLARE_CONCRETE_INSTRUCTION(SubI, "sub-i") DECLARE_HYDROGEN_ACCESSOR(Sub) }; @@ -901,6 +996,9 @@ class LConstantD: public LTemplateInstruction<1, 0, 1> { explicit LConstantD(LOperand* temp) { temps_[0] = temp; } + + LOperand* temp() { return temps_[0]; } + DECLARE_CONCRETE_INSTRUCTION(ConstantD, "constant-d") DECLARE_HYDROGEN_ACCESSOR(Constant) @@ -923,6 +1021,8 @@ class LBranch: public LControlInstruction<1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(Branch, "branch") DECLARE_HYDROGEN_ACCESSOR(Branch) @@ -936,6 +1036,8 @@ class LCmpMapAndBranch: public LTemplateInstruction<0, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(CmpMapAndBranch, "cmp-map-and-branch") DECLARE_HYDROGEN_ACCESSOR(CompareMap) @@ -957,6 +1059,8 @@ class LJSArrayLength: public LTemplateInstruction<1, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(JSArrayLength, "js-array-length") DECLARE_HYDROGEN_ACCESSOR(JSArrayLength) }; @@ -968,18 +1072,34 @@ class LFixedArrayBaseLength: public LTemplateInstruction<1, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(FixedArrayBaseLength, "fixed-array-base-length") DECLARE_HYDROGEN_ACCESSOR(FixedArrayBaseLength) }; +class LMapEnumLength: public LTemplateInstruction<1, 1, 0> { + public: + explicit LMapEnumLength(LOperand* value) { + inputs_[0] = value; + } + + LOperand* value() { return inputs_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(MapEnumLength, "map-enum-length") +}; + + class LElementsKind: public LTemplateInstruction<1, 1, 0> { public: explicit LElementsKind(LOperand* value) { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(ElementsKind, "elements-kind") DECLARE_HYDROGEN_ACCESSOR(ElementsKind) }; @@ -991,6 +1111,8 @@ class LValueOf: public LTemplateInstruction<1, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(ValueOf, "value-of") DECLARE_HYDROGEN_ACCESSOR(ValueOf) }; @@ -1002,11 +1124,12 @@ class LDateField: public LTemplateInstruction<1, 1, 0> { inputs_[0] = date; } + LOperand* date() { return inputs_[0]; } + Smi* index() const { return index_; } + DECLARE_CONCRETE_INSTRUCTION(ValueOf, "date-field") DECLARE_HYDROGEN_ACCESSOR(ValueOf) - Smi* index() const { return index_; } - private: Smi* index_; }; @@ -1018,6 +1141,8 @@ class LThrow: public LTemplateInstruction<0, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(Throw, "throw") }; @@ -1028,6 +1153,8 @@ class LBitNotI: public LTemplateInstruction<1, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(BitNotI, "bit-not-i") }; @@ -1039,11 +1166,29 @@ class LAddI: public LTemplateInstruction<1, 2, 0> { inputs_[1] = right; } + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } + DECLARE_CONCRETE_INSTRUCTION(AddI, "add-i") DECLARE_HYDROGEN_ACCESSOR(Add) }; +class LMathMinMax: public LTemplateInstruction<1, 2, 0> { + public: + LMathMinMax(LOperand* left, LOperand* right) { + inputs_[0] = left; + inputs_[1] = right; + } + + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } + + DECLARE_CONCRETE_INSTRUCTION(MathMinMax, "min-max") + DECLARE_HYDROGEN_ACCESSOR(MathMinMax) +}; + + class LPower: public LTemplateInstruction<1, 2, 0> { public: LPower(LOperand* left, LOperand* right) { @@ -1051,6 +1196,9 @@ class LPower: public LTemplateInstruction<1, 2, 0> { inputs_[1] = right; } + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } + DECLARE_CONCRETE_INSTRUCTION(Power, "power") DECLARE_HYDROGEN_ACCESSOR(Power) }; @@ -1062,6 +1210,8 @@ class LRandom: public LTemplateInstruction<1, 1, 0> { inputs_[0] = global_object; } + LOperand* global_object() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(Random, "random") DECLARE_HYDROGEN_ACCESSOR(Random) }; @@ -1076,6 +1226,8 @@ class LArithmeticD: public LTemplateInstruction<1, 2, 0> { } Token::Value op() const { return op_; } + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } virtual Opcode opcode() const { return LInstruction::kArithmeticD; } virtual void CompileToNative(LCodeGen* generator); @@ -1094,12 +1246,14 @@ class LArithmeticT: public LTemplateInstruction<1, 2, 0> { inputs_[1] = right; } + Token::Value op() const { return op_; } + LOperand* left() { return inputs_[0]; } + LOperand* right() { return inputs_[1]; } + virtual Opcode opcode() const { return LInstruction::kArithmeticT; } virtual void CompileToNative(LCodeGen* generator); virtual const char* Mnemonic() const; - Token::Value op() const { return op_; } - private: Token::Value op_; }; @@ -1111,6 +1265,8 @@ class LReturn: public LTemplateInstruction<0, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(Return, "return") }; @@ -1121,6 +1277,8 @@ class LLoadNamedField: public LTemplateInstruction<1, 1, 0> { inputs_[0] = object; } + LOperand* object() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load-named-field") DECLARE_HYDROGEN_ACCESSOR(LoadNamedField) }; @@ -1172,6 +1330,8 @@ class LLoadElements: public LTemplateInstruction<1, 1, 0> { inputs_[0] = object; } + LOperand* object() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(LoadElements, "load-elements") }; @@ -1182,64 +1342,43 @@ class LLoadExternalArrayPointer: public LTemplateInstruction<1, 1, 0> { inputs_[0] = object; } + LOperand* object() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(LoadExternalArrayPointer, "load-external-array-pointer") }; -class LLoadKeyedFastElement: public LTemplateInstruction<1, 2, 0> { +class LLoadKeyed: public LTemplateInstruction<1, 2, 0> { public: - LLoadKeyedFastElement(LOperand* elements, LOperand* key) { + LLoadKeyed(LOperand* elements, LOperand* key) { inputs_[0] = elements; inputs_[1] = key; } - DECLARE_CONCRETE_INSTRUCTION(LoadKeyedFastElement, "load-keyed-fast-element") - DECLARE_HYDROGEN_ACCESSOR(LoadKeyedFastElement) - - LOperand* elements() { return inputs_[0]; } - LOperand* key() { return inputs_[1]; } - uint32_t additional_index() const { return hydrogen()->index_offset(); } -}; - + DECLARE_CONCRETE_INSTRUCTION(LoadKeyed, "load-keyed") + DECLARE_HYDROGEN_ACCESSOR(LoadKeyed) -class LLoadKeyedFastDoubleElement: public LTemplateInstruction<1, 2, 0> { - public: - LLoadKeyedFastDoubleElement(LOperand* elements, LOperand* key) { - inputs_[0] = elements; - inputs_[1] = key; + bool is_external() const { + return hydrogen()->is_external(); } - - DECLARE_CONCRETE_INSTRUCTION(LoadKeyedFastDoubleElement, - "load-keyed-fast-double-element") - DECLARE_HYDROGEN_ACCESSOR(LoadKeyedFastDoubleElement) - LOperand* elements() { return inputs_[0]; } LOperand* key() { return inputs_[1]; } uint32_t additional_index() const { return hydrogen()->index_offset(); } -}; - - -class LLoadKeyedSpecializedArrayElement: public LTemplateInstruction<1, 2, 0> { - public: - LLoadKeyedSpecializedArrayElement(LOperand* external_pointer, LOperand* key) { - inputs_[0] = external_pointer; - inputs_[1] = key; - } - - DECLARE_CONCRETE_INSTRUCTION(LoadKeyedSpecializedArrayElement, - "load-keyed-specialized-array-element") - DECLARE_HYDROGEN_ACCESSOR(LoadKeyedSpecializedArrayElement) - - LOperand* external_pointer() { return inputs_[0]; } - LOperand* key() { return inputs_[1]; } ElementsKind elements_kind() const { return hydrogen()->elements_kind(); } - uint32_t additional_index() const { return hydrogen()->index_offset(); } }; +template <class T> +inline static bool ArrayOpClobbersKey(T *value) { + CHECK(value->IsLoadKeyed() || value->IsStoreKeyed()); + return !value->IsConstant() && (value->key()->representation().IsTagged() + || value->IsDehoisted()); +} + + class LLoadKeyedGeneric: public LTemplateInstruction<1, 2, 0> { public: LLoadKeyedGeneric(LOperand* obj, LOperand* key) { @@ -1283,10 +1422,11 @@ class LStoreGlobalCell: public LTemplateInstruction<0, 1, 1> { temps_[0] = temp; } + LOperand* value() { return inputs_[0]; } + LOperand* temp() { return temps_[0]; } + DECLARE_CONCRETE_INSTRUCTION(StoreGlobalCell, "store-global-cell") DECLARE_HYDROGEN_ACCESSOR(StoreGlobalCell) - - LOperand* value() { return inputs_[0]; } }; @@ -1298,12 +1438,13 @@ class LStoreGlobalGeneric: public LTemplateInstruction<0, 2, 0> { inputs_[1] = value; } + LOperand* global_object() { return inputs_[0]; } + LOperand* value() { return inputs_[1]; } + DECLARE_CONCRETE_INSTRUCTION(StoreGlobalGeneric, "store-global-generic") DECLARE_HYDROGEN_ACCESSOR(StoreGlobalGeneric) - LOperand* global_object() { return InputAt(0); } Handle<Object> name() const { return hydrogen()->name(); } - LOperand* value() { return InputAt(1); } StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); } }; @@ -1314,10 +1455,11 @@ class LLoadContextSlot: public LTemplateInstruction<1, 1, 0> { inputs_[0] = context; } + LOperand* context() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(LoadContextSlot, "load-context-slot") DECLARE_HYDROGEN_ACCESSOR(LoadContextSlot) - LOperand* context() { return InputAt(0); } int slot_index() { return hydrogen()->slot_index(); } virtual void PrintDataTo(StringStream* stream); @@ -1332,11 +1474,13 @@ class LStoreContextSlot: public LTemplateInstruction<0, 2, 1> { temps_[0] = temp; } + LOperand* context() { return inputs_[0]; } + LOperand* value() { return inputs_[1]; } + LOperand* temp() { return temps_[0]; } + DECLARE_CONCRETE_INSTRUCTION(StoreContextSlot, "store-context-slot") DECLARE_HYDROGEN_ACCESSOR(StoreContextSlot) - LOperand* context() { return InputAt(0); } - LOperand* value() { return InputAt(1); } int slot_index() { return hydrogen()->slot_index(); } virtual void PrintDataTo(StringStream* stream); @@ -1349,6 +1493,8 @@ class LPushArgument: public LTemplateInstruction<0, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(PushArgument, "push-argument") }; @@ -1385,9 +1531,9 @@ class LOuterContext: public LTemplateInstruction<1, 1, 0> { inputs_[0] = context; } - DECLARE_CONCRETE_INSTRUCTION(OuterContext, "outer-context") + LOperand* context() { return inputs_[0]; } - LOperand* context() { return InputAt(0); } + DECLARE_CONCRETE_INSTRUCTION(OuterContext, "outer-context") }; @@ -1416,9 +1562,9 @@ class LGlobalReceiver: public LTemplateInstruction<1, 1, 0> { inputs_[0] = global_object; } - DECLARE_CONCRETE_INSTRUCTION(GlobalReceiver, "global-receiver") + LOperand* global() { return inputs_[0]; } - LOperand* global() { return InputAt(0); } + DECLARE_CONCRETE_INSTRUCTION(GlobalReceiver, "global-receiver") }; @@ -1440,11 +1586,11 @@ class LInvokeFunction: public LTemplateInstruction<1, 1, 0> { inputs_[0] = function; } + LOperand* function() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(InvokeFunction, "invoke-function") DECLARE_HYDROGEN_ACCESSOR(InvokeFunction) - LOperand* function() { return inputs_[0]; } - virtual void PrintDataTo(StringStream* stream); int arity() const { return hydrogen()->argument_count() - 1; } @@ -1531,6 +1677,8 @@ class LCallNew: public LTemplateInstruction<1, 1, 0> { inputs_[0] = constructor; } + LOperand* constructor() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(CallNew, "call-new") DECLARE_HYDROGEN_ACCESSOR(CallNew) @@ -1556,20 +1704,52 @@ class LInteger32ToDouble: public LTemplateInstruction<1, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(Integer32ToDouble, "int32-to-double") }; +class LUint32ToDouble: public LTemplateInstruction<1, 1, 1> { + public: + explicit LUint32ToDouble(LOperand* value, LOperand* temp) { + inputs_[0] = value; + temps_[0] = temp; + } + + LOperand* value() { return inputs_[0]; } + LOperand* temp() { return temps_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(Uint32ToDouble, "uint32-to-double") +}; + + class LNumberTagI: public LTemplateInstruction<1, 1, 0> { public: explicit LNumberTagI(LOperand* value) { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(NumberTagI, "number-tag-i") }; +class LNumberTagU: public LTemplateInstruction<1, 1, 1> { + public: + explicit LNumberTagU(LOperand* value, LOperand* temp) { + inputs_[0] = value; + temps_[0] = temp; + } + + LOperand* value() { return inputs_[0]; } + LOperand* temp() { return temps_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(NumberTagU, "number-tag-u") +}; + + class LNumberTagD: public LTemplateInstruction<1, 1, 1> { public: explicit LNumberTagD(LOperand* value, LOperand* temp) { @@ -1577,6 +1757,9 @@ class LNumberTagD: public LTemplateInstruction<1, 1, 1> { temps_[0] = temp; } + LOperand* value() { return inputs_[0]; } + LOperand* temp() { return temps_[0]; } + DECLARE_CONCRETE_INSTRUCTION(NumberTagD, "number-tag-d") }; @@ -1588,6 +1771,8 @@ class LDoubleToI: public LTemplateInstruction<1, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(DoubleToI, "double-to-i") DECLARE_HYDROGEN_ACCESSOR(UnaryOperation) @@ -1603,6 +1788,9 @@ class LTaggedToI: public LTemplateInstruction<1, 1, 1> { temps_[0] = temp; } + LOperand* value() { return inputs_[0]; } + LOperand* temp() { return temps_[0]; } + DECLARE_CONCRETE_INSTRUCTION(TaggedToI, "tagged-to-i") DECLARE_HYDROGEN_ACCESSOR(UnaryOperation) @@ -1616,6 +1804,8 @@ class LSmiTag: public LTemplateInstruction<1, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(SmiTag, "smi-tag") }; @@ -1626,6 +1816,8 @@ class LNumberUntagD: public LTemplateInstruction<1, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(NumberUntagD, "double-untag") DECLARE_HYDROGEN_ACCESSOR(Change); }; @@ -1638,10 +1830,11 @@ class LSmiUntag: public LTemplateInstruction<1, 1, 0> { inputs_[0] = value; } - DECLARE_CONCRETE_INSTRUCTION(SmiUntag, "smi-untag") - + LOperand* value() { return inputs_[0]; } bool needs_check() const { return needs_check_; } + DECLARE_CONCRETE_INSTRUCTION(SmiUntag, "smi-untag") + private: bool needs_check_; }; @@ -1655,14 +1848,15 @@ class LStoreNamedField: public LTemplateInstruction<0, 2, 1> { temps_[0] = temp; } + LOperand* object() { return inputs_[0]; } + LOperand* value() { return inputs_[1]; } + LOperand* temp() { return temps_[0]; } + DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field") DECLARE_HYDROGEN_ACCESSOR(StoreNamedField) virtual void PrintDataTo(StringStream* stream); - LOperand* object() { return inputs_[0]; } - LOperand* value() { return inputs_[1]; } - Handle<Object> name() const { return hydrogen()->name(); } bool is_in_object() { return hydrogen()->is_in_object(); } int offset() { return hydrogen()->offset(); } @@ -1677,88 +1871,42 @@ class LStoreNamedGeneric: public LTemplateInstruction<0, 2, 0> { inputs_[1] = value; } + LOperand* object() { return inputs_[0]; } + LOperand* value() { return inputs_[1]; } + DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric, "store-named-generic") DECLARE_HYDROGEN_ACCESSOR(StoreNamedGeneric) virtual void PrintDataTo(StringStream* stream); - LOperand* object() { return inputs_[0]; } - LOperand* value() { return inputs_[1]; } Handle<Object> name() const { return hydrogen()->name(); } StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); } }; -class LStoreKeyedFastElement: public LTemplateInstruction<0, 3, 0> { +class LStoreKeyed: public LTemplateInstruction<0, 3, 0> { public: - LStoreKeyedFastElement(LOperand* obj, LOperand* key, LOperand* val) { - inputs_[0] = obj; + LStoreKeyed(LOperand* object, LOperand* key, LOperand* value) { + inputs_[0] = object; inputs_[1] = key; - inputs_[2] = val; + inputs_[2] = value; } - DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFastElement, - "store-keyed-fast-element") - DECLARE_HYDROGEN_ACCESSOR(StoreKeyedFastElement) - - virtual void PrintDataTo(StringStream* stream); - - LOperand* object() { return inputs_[0]; } + bool is_external() const { return hydrogen()->is_external(); } + LOperand* elements() { return inputs_[0]; } LOperand* key() { return inputs_[1]; } LOperand* value() { return inputs_[2]; } - uint32_t additional_index() const { return hydrogen()->index_offset(); } -}; + ElementsKind elements_kind() const { return hydrogen()->elements_kind(); } - -class LStoreKeyedFastDoubleElement: public LTemplateInstruction<0, 3, 0> { - public: - LStoreKeyedFastDoubleElement(LOperand* elements, - LOperand* key, - LOperand* val) { - inputs_[0] = elements; - inputs_[1] = key; - inputs_[2] = val; - } - - DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFastDoubleElement, - "store-keyed-fast-double-element") - DECLARE_HYDROGEN_ACCESSOR(StoreKeyedFastDoubleElement) + DECLARE_CONCRETE_INSTRUCTION(StoreKeyed, "store-keyed") + DECLARE_HYDROGEN_ACCESSOR(StoreKeyed) virtual void PrintDataTo(StringStream* stream); - - LOperand* elements() { return inputs_[0]; } - LOperand* key() { return inputs_[1]; } - LOperand* value() { return inputs_[2]; } - bool NeedsCanonicalization() { return hydrogen()->NeedsCanonicalization(); } uint32_t additional_index() const { return hydrogen()->index_offset(); } }; -class LStoreKeyedSpecializedArrayElement: public LTemplateInstruction<0, 3, 0> { - public: - LStoreKeyedSpecializedArrayElement(LOperand* external_pointer, - LOperand* key, - LOperand* val) { - inputs_[0] = external_pointer; - inputs_[1] = key; - inputs_[2] = val; - } - - DECLARE_CONCRETE_INSTRUCTION(StoreKeyedSpecializedArrayElement, - "store-keyed-specialized-array-element") - DECLARE_HYDROGEN_ACCESSOR(StoreKeyedSpecializedArrayElement) - - LOperand* external_pointer() { return inputs_[0]; } - LOperand* key() { return inputs_[1]; } - LOperand* value() { return inputs_[2]; } - ElementsKind elements_kind() const { - return hydrogen()->elements_kind(); - } - uint32_t additional_index() const { return hydrogen()->index_offset(); } -}; - - class LStoreKeyedGeneric: public LTemplateInstruction<0, 3, 0> { public: LStoreKeyedGeneric(LOperand* object, LOperand* key, LOperand* value) { @@ -1767,14 +1915,15 @@ class LStoreKeyedGeneric: public LTemplateInstruction<0, 3, 0> { inputs_[2] = value; } + LOperand* object() { return inputs_[0]; } + LOperand* key() { return inputs_[1]; } + LOperand* value() { return inputs_[2]; } + DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store-keyed-generic") DECLARE_HYDROGEN_ACCESSOR(StoreKeyedGeneric) virtual void PrintDataTo(StringStream* stream); - LOperand* object() { return inputs_[0]; } - LOperand* key() { return inputs_[1]; } - LOperand* value() { return inputs_[2]; } StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); } }; @@ -1783,21 +1932,22 @@ class LTransitionElementsKind: public LTemplateInstruction<1, 1, 2> { public: LTransitionElementsKind(LOperand* object, LOperand* new_map_temp, - LOperand* temp_reg) { + LOperand* temp) { inputs_[0] = object; temps_[0] = new_map_temp; - temps_[1] = temp_reg; + temps_[1] = temp; } + LOperand* object() { return inputs_[0]; } + LOperand* new_map_temp() { return temps_[0]; } + LOperand* temp() { return temps_[1]; } + DECLARE_CONCRETE_INSTRUCTION(TransitionElementsKind, "transition-elements-kind") DECLARE_HYDROGEN_ACCESSOR(TransitionElementsKind) virtual void PrintDataTo(StringStream* stream); - LOperand* object() { return inputs_[0]; } - LOperand* new_map_reg() { return temps_[0]; } - LOperand* temp_reg() { return temps_[1]; } Handle<Map> original_map() { return hydrogen()->original_map(); } Handle<Map> transitioned_map() { return hydrogen()->transitioned_map(); } }; @@ -1810,11 +1960,11 @@ class LStringAdd: public LTemplateInstruction<1, 2, 0> { inputs_[1] = right; } - DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string-add") - DECLARE_HYDROGEN_ACCESSOR(StringAdd) - LOperand* left() { return inputs_[0]; } LOperand* right() { return inputs_[1]; } + + DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string-add") + DECLARE_HYDROGEN_ACCESSOR(StringAdd) }; @@ -1825,11 +1975,11 @@ class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> { inputs_[1] = index; } - DECLARE_CONCRETE_INSTRUCTION(StringCharCodeAt, "string-char-code-at") - DECLARE_HYDROGEN_ACCESSOR(StringCharCodeAt) - LOperand* string() { return inputs_[0]; } LOperand* index() { return inputs_[1]; } + + DECLARE_CONCRETE_INSTRUCTION(StringCharCodeAt, "string-char-code-at") + DECLARE_HYDROGEN_ACCESSOR(StringCharCodeAt) }; @@ -1839,10 +1989,10 @@ class LStringCharFromCode: public LTemplateInstruction<1, 1, 0> { inputs_[0] = char_code; } + LOperand* char_code() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string-char-from-code") DECLARE_HYDROGEN_ACCESSOR(StringCharFromCode) - - LOperand* char_code() { return inputs_[0]; } }; @@ -1852,10 +2002,10 @@ class LStringLength: public LTemplateInstruction<1, 1, 0> { inputs_[0] = string; } + LOperand* string() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(StringLength, "string-length") DECLARE_HYDROGEN_ACCESSOR(StringLength) - - LOperand* string() { return inputs_[0]; } }; @@ -1865,7 +2015,7 @@ class LCheckFunction: public LTemplateInstruction<0, 1, 0> { inputs_[0] = value; } - LOperand* value() { return InputAt(0); } + LOperand* value() { return inputs_[0]; } DECLARE_CONCRETE_INSTRUCTION(CheckFunction, "check-function") DECLARE_HYDROGEN_ACCESSOR(CheckFunction) @@ -1878,6 +2028,8 @@ class LCheckInstanceType: public LTemplateInstruction<0, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(CheckInstanceType, "check-instance-type") DECLARE_HYDROGEN_ACCESSOR(CheckInstanceType) }; @@ -1889,17 +2041,21 @@ class LCheckMaps: public LTemplateInstruction<0, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(CheckMaps, "check-maps") DECLARE_HYDROGEN_ACCESSOR(CheckMaps) }; -class LCheckPrototypeMaps: public LTemplateInstruction<0, 0, 1> { +class LCheckPrototypeMaps: public LTemplateInstruction<1, 0, 1> { public: explicit LCheckPrototypeMaps(LOperand* temp) { temps_[0] = temp; } + LOperand* temp() { return temps_[0]; } + DECLARE_CONCRETE_INSTRUCTION(CheckPrototypeMaps, "check-prototype-maps") DECLARE_HYDROGEN_ACCESSOR(CheckPrototypeMaps) @@ -1914,15 +2070,16 @@ class LCheckSmi: public LTemplateInstruction<0, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(CheckSmi, "check-smi") }; -class LClampDToUint8: public LTemplateInstruction<1, 1, 1> { +class LClampDToUint8: public LTemplateInstruction<1, 1, 0> { public: - LClampDToUint8(LOperand* value, LOperand* temp) { - inputs_[0] = value; - temps_[0] = temp; + explicit LClampDToUint8(LOperand* unclamped) { + inputs_[0] = unclamped; } LOperand* unclamped() { return inputs_[0]; } @@ -1933,8 +2090,8 @@ class LClampDToUint8: public LTemplateInstruction<1, 1, 1> { class LClampIToUint8: public LTemplateInstruction<1, 1, 0> { public: - explicit LClampIToUint8(LOperand* value) { - inputs_[0] = value; + explicit LClampIToUint8(LOperand* unclamped) { + inputs_[0] = unclamped; } LOperand* unclamped() { return inputs_[0]; } @@ -1943,17 +2100,16 @@ class LClampIToUint8: public LTemplateInstruction<1, 1, 0> { }; -class LClampTToUint8: public LTemplateInstruction<1, 1, 2> { +class LClampTToUint8: public LTemplateInstruction<1, 1, 1> { public: - LClampTToUint8(LOperand* value, - LOperand* temp, - LOperand* temp2) { - inputs_[0] = value; - temps_[0] = temp; - temps_[1] = temp2; + LClampTToUint8(LOperand* unclamped, + LOperand* temp_xmm) { + inputs_[0] = unclamped; + temps_[0] = temp_xmm; } LOperand* unclamped() { return inputs_[0]; } + LOperand* temp_xmm() { return temps_[0]; } DECLARE_CONCRETE_INSTRUCTION(ClampTToUint8, "clamp-t-to-uint8") }; @@ -1965,6 +2121,8 @@ class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(CheckNonSmi, "check-non-smi") }; @@ -1975,6 +2133,8 @@ class LAllocateObject: public LTemplateInstruction<1, 0, 1> { temps_[0] = temp; } + LOperand* temp() { return temps_[0]; } + DECLARE_CONCRETE_INSTRUCTION(AllocateObject, "allocate-object") DECLARE_HYDROGEN_ACCESSOR(AllocateObject) }; @@ -2023,6 +2183,8 @@ class LToFastProperties: public LTemplateInstruction<1, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(ToFastProperties, "to-fast-properties") DECLARE_HYDROGEN_ACCESSOR(ToFastProperties) }; @@ -2034,6 +2196,8 @@ class LTypeof: public LTemplateInstruction<1, 1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(Typeof, "typeof") }; @@ -2044,6 +2208,8 @@ class LTypeofIsAndBranch: public LControlInstruction<1, 0> { inputs_[0] = value; } + LOperand* value() { return inputs_[0]; } + DECLARE_CONCRETE_INSTRUCTION(TypeofIsAndBranch, "typeof-is-and-branch") DECLARE_HYDROGEN_ACCESSOR(TypeofIsAndBranch) @@ -2059,6 +2225,8 @@ class LIsConstructCallAndBranch: public LControlInstruction<0, 1> { temps_[0] = temp; } + LOperand* temp() { return temps_[0]; } + DECLARE_CONCRETE_INSTRUCTION(IsConstructCallAndBranch, "is-construct-call-and-branch") DECLARE_HYDROGEN_ACCESSOR(IsConstructCallAndBranch) @@ -2072,10 +2240,10 @@ class LDeleteProperty: public LTemplateInstruction<1, 2, 0> { inputs_[1] = key; } - DECLARE_CONCRETE_INSTRUCTION(DeleteProperty, "delete-property") - LOperand* object() { return inputs_[0]; } LOperand* key() { return inputs_[1]; } + + DECLARE_CONCRETE_INSTRUCTION(DeleteProperty, "delete-property") }; @@ -2171,69 +2339,13 @@ class LLoadFieldByIndex: public LTemplateInstruction<1, 2, 0> { class LChunkBuilder; -class LChunk: public ZoneObject { +class LPlatformChunk: public LChunk { public: - explicit LChunk(CompilationInfo* info, HGraph* graph) - : spill_slot_count_(0), - info_(info), - graph_(graph), - instructions_(32), - pointer_maps_(8), - inlined_closures_(1) { } - - void AddInstruction(LInstruction* instruction, HBasicBlock* block); - LConstantOperand* DefineConstantOperand(HConstant* constant); - Handle<Object> LookupLiteral(LConstantOperand* operand) const; - Representation LookupLiteralRepresentation(LConstantOperand* operand) const; + LPlatformChunk(CompilationInfo* info, HGraph* graph) + : LChunk(info, graph) { } 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_; } - CompilationInfo* info() const { return info_; } - 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(); - } - - const ZoneList<Handle<JSFunction> >* inlined_closures() const { - return &inlined_closures_; - } - - void AddInlinedClosure(Handle<JSFunction> closure) { - inlined_closures_.Add(closure); - } - - private: - int spill_slot_count_; - CompilationInfo* info_; - HGraph* const graph_; - ZoneList<LInstruction*> instructions_; - ZoneList<LPointerMap*> pointer_maps_; - ZoneList<Handle<JSFunction> > inlined_closures_; }; @@ -2243,7 +2355,7 @@ class LChunkBuilder BASE_EMBEDDED { : chunk_(NULL), info_(info), graph_(graph), - zone_(graph->isolate()->zone()), + zone_(graph->zone()), status_(UNUSED), current_instruction_(NULL), current_block_(NULL), @@ -2252,16 +2364,19 @@ class LChunkBuilder BASE_EMBEDDED { allocator_(allocator), position_(RelocInfo::kNoPosition), instruction_pending_deoptimization_environment_(NULL), - pending_deoptimization_ast_id_(AstNode::kNoNumber) { } + pending_deoptimization_ast_id_(BailoutId::None()) { } // Build the sequence for the graph. - LChunk* Build(); + LPlatformChunk* Build(); // Declare methods that deal with the individual node types. #define DECLARE_DO(type) LInstruction* Do##type(H##type* node); HYDROGEN_CONCRETE_INSTRUCTION_LIST(DECLARE_DO) #undef DECLARE_DO + static HValue* SimplifiedDividendForMathFloorOfDiv(HValue* val); + static HValue* SimplifiedDivisorForMathFloorOfDiv(HValue* val); + private: enum Status { UNUSED, @@ -2270,7 +2385,7 @@ class LChunkBuilder BASE_EMBEDDED { ABORTED }; - LChunk* chunk() const { return chunk_; } + LPlatformChunk* chunk() const { return chunk_; } CompilationInfo* info() const { return info_; } HGraph* graph() const { return graph_; } Zone* zone() const { return zone_; } @@ -2280,7 +2395,7 @@ class LChunkBuilder BASE_EMBEDDED { bool is_done() const { return status_ == DONE; } bool is_aborted() const { return status_ == ABORTED; } - void Abort(const char* format, ...); + void Abort(const char* reason); // Methods for getting operands for Use / Define / Temp. LUnallocated* ToUnallocated(Register reg); @@ -2374,7 +2489,7 @@ class LChunkBuilder BASE_EMBEDDED { LInstruction* DoArithmeticT(Token::Value op, HArithmeticBinaryOperation* instr); - LChunk* chunk_; + LPlatformChunk* chunk_; CompilationInfo* info_; HGraph* const graph_; Zone* zone_; @@ -2386,7 +2501,7 @@ class LChunkBuilder BASE_EMBEDDED { LAllocator* allocator_; int position_; LInstruction* instruction_pending_deoptimization_environment_; - int pending_deoptimization_ast_id_; + BailoutId pending_deoptimization_ast_id_; DISALLOW_COPY_AND_ASSIGN(LChunkBuilder); }; diff --git a/src/3rdparty/v8/src/x64/macro-assembler-x64.cc b/src/3rdparty/v8/src/x64/macro-assembler-x64.cc index 3d380a2..962c2e8 100644 --- a/src/3rdparty/v8/src/x64/macro-assembler-x64.cc +++ b/src/3rdparty/v8/src/x64/macro-assembler-x64.cc @@ -53,9 +53,17 @@ MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size) } -static intptr_t RootRegisterDelta(ExternalReference other, Isolate* isolate) { +static const int kInvalidRootRegisterDelta = -1; + + +intptr_t MacroAssembler::RootRegisterDelta(ExternalReference other) { + if (predictable_code_size() && + (other.address() < reinterpret_cast<Address>(isolate()) || + other.address() >= reinterpret_cast<Address>(isolate() + 1))) { + return kInvalidRootRegisterDelta; + } Address roots_register_value = kRootRegisterBias + - reinterpret_cast<Address>(isolate->heap()->roots_array_start()); + reinterpret_cast<Address>(isolate()->heap()->roots_array_start()); intptr_t delta = other.address() - roots_register_value; return delta; } @@ -64,8 +72,8 @@ static intptr_t RootRegisterDelta(ExternalReference other, Isolate* isolate) { Operand MacroAssembler::ExternalOperand(ExternalReference target, Register scratch) { if (root_array_available_ && !Serializer::enabled()) { - intptr_t delta = RootRegisterDelta(target, isolate()); - if (is_int32(delta)) { + intptr_t delta = RootRegisterDelta(target); + if (delta != kInvalidRootRegisterDelta && is_int32(delta)) { Serializer::TooLateToEnableNow(); return Operand(kRootRegister, static_cast<int32_t>(delta)); } @@ -77,8 +85,8 @@ Operand MacroAssembler::ExternalOperand(ExternalReference target, void MacroAssembler::Load(Register destination, ExternalReference source) { if (root_array_available_ && !Serializer::enabled()) { - intptr_t delta = RootRegisterDelta(source, isolate()); - if (is_int32(delta)) { + intptr_t delta = RootRegisterDelta(source); + if (delta != kInvalidRootRegisterDelta && is_int32(delta)) { Serializer::TooLateToEnableNow(); movq(destination, Operand(kRootRegister, static_cast<int32_t>(delta))); return; @@ -96,8 +104,8 @@ void MacroAssembler::Load(Register destination, ExternalReference source) { void MacroAssembler::Store(ExternalReference destination, Register source) { if (root_array_available_ && !Serializer::enabled()) { - intptr_t delta = RootRegisterDelta(destination, isolate()); - if (is_int32(delta)) { + intptr_t delta = RootRegisterDelta(destination); + if (delta != kInvalidRootRegisterDelta && is_int32(delta)) { Serializer::TooLateToEnableNow(); movq(Operand(kRootRegister, static_cast<int32_t>(delta)), source); return; @@ -116,8 +124,8 @@ void MacroAssembler::Store(ExternalReference destination, Register source) { void MacroAssembler::LoadAddress(Register destination, ExternalReference source) { if (root_array_available_ && !Serializer::enabled()) { - intptr_t delta = RootRegisterDelta(source, isolate()); - if (is_int32(delta)) { + intptr_t delta = RootRegisterDelta(source); + if (delta != kInvalidRootRegisterDelta && is_int32(delta)) { Serializer::TooLateToEnableNow(); lea(destination, Operand(kRootRegister, static_cast<int32_t>(delta))); return; @@ -133,8 +141,8 @@ int MacroAssembler::LoadAddressSize(ExternalReference source) { // This calculation depends on the internals of LoadAddress. // It's correctness is ensured by the asserts in the Call // instruction below. - intptr_t delta = RootRegisterDelta(source, isolate()); - if (is_int32(delta)) { + intptr_t delta = RootRegisterDelta(source); + if (delta != kInvalidRootRegisterDelta && is_int32(delta)) { Serializer::TooLateToEnableNow(); // Operand is lea(scratch, Operand(kRootRegister, delta)); // Opcodes : REX.W 8D ModRM Disp8/Disp32 - 4 or 7. @@ -216,7 +224,7 @@ void MacroAssembler::RememberedSetHelper(Register object, // For debug tests. Register scratch, SaveFPRegsMode save_fp, RememberedSetFinalAction and_then) { - if (FLAG_debug_code) { + if (emit_debug_code()) { Label ok; JumpIfNotInNewSpace(object, scratch, &ok, Label::kNear); int3(); @@ -388,16 +396,14 @@ void MacroAssembler::RecordWrite(Register object, ASSERT(!object.is(value)); ASSERT(!object.is(address)); ASSERT(!value.is(address)); - if (emit_debug_code()) { - AbortIfSmi(object); - } + AssertNotSmi(object); if (remembered_set_action == OMIT_REMEMBERED_SET && !FLAG_incremental_marking) { return; } - if (FLAG_debug_code) { + if (emit_debug_code()) { Label ok; cmpq(value, Operand(address, 0)); j(equal, &ok, Label::kNear); @@ -538,7 +544,7 @@ void MacroAssembler::Abort(const char* msg) { } -void MacroAssembler::CallStub(CodeStub* stub, unsigned ast_id) { +void MacroAssembler::CallStub(CodeStub* stub, TypeFeedbackId ast_id) { ASSERT(AllowThisStubCall(stub)); // Calls are not allowed in some stubs Call(stub->GetCode(), RelocInfo::CODE_TARGET, ast_id); } @@ -743,17 +749,52 @@ void MacroAssembler::CallApiFunctionAndReturn(Address function_address, Cmp(Operand(rsi, 0), factory->the_hole_value()); j(not_equal, &promote_scheduled_exception); +#if ENABLE_EXTRA_CHECKS + // Check if the function returned a valid JavaScript value. + Label ok; + Register return_value = rax; + Register map = rcx; + + JumpIfSmi(return_value, &ok, Label::kNear); + movq(map, FieldOperand(return_value, HeapObject::kMapOffset)); + + CmpInstanceType(map, FIRST_NONSTRING_TYPE); + j(below, &ok, Label::kNear); + + CmpInstanceType(map, FIRST_SPEC_OBJECT_TYPE); + j(above_equal, &ok, Label::kNear); + + CompareRoot(map, Heap::kHeapNumberMapRootIndex); + j(equal, &ok, Label::kNear); + + CompareRoot(return_value, Heap::kUndefinedValueRootIndex); + j(equal, &ok, Label::kNear); + + CompareRoot(return_value, Heap::kTrueValueRootIndex); + j(equal, &ok, Label::kNear); + + CompareRoot(return_value, Heap::kFalseValueRootIndex); + j(equal, &ok, Label::kNear); + + CompareRoot(return_value, Heap::kNullValueRootIndex); + j(equal, &ok, Label::kNear); + + Abort("API call returned invalid object"); + + bind(&ok); +#endif + LeaveApiExitFrame(); ret(stack_space * kPointerSize); - bind(&promote_scheduled_exception); - TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1); - bind(&empty_result); // It was zero; the result is undefined. - Move(rax, factory->undefined_value()); + LoadRoot(rax, Heap::kUndefinedValueRootIndex); jmp(&prologue); + bind(&promote_scheduled_exception); + TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1); + // HandleScope limit has changed. Delete allocated extensions. bind(&delete_allocated_handles); movq(Operand(base_reg, kLimitOffset), prev_limit_reg); @@ -798,7 +839,7 @@ void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, void MacroAssembler::GetBuiltinFunction(Register target, Builtins::JavaScript id) { // Load the builtins object into target register. - movq(target, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); + movq(target, Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX))); movq(target, FieldOperand(target, GlobalObject::kBuiltinsOffset)); movq(target, FieldOperand(target, JSBuiltinsObject::OffsetOfFunctionWithId(id))); @@ -892,6 +933,38 @@ void MacroAssembler::Set(const Operand& dst, int64_t x) { } } + +bool MacroAssembler::IsUnsafeInt(const int x) { + static const int kMaxBits = 17; + return !is_intn(x, kMaxBits); +} + + +void MacroAssembler::SafeMove(Register dst, Smi* src) { + ASSERT(!dst.is(kScratchRegister)); + ASSERT(kSmiValueSize == 32); // JIT cookie can be converted to Smi. + if (IsUnsafeInt(src->value()) && jit_cookie() != 0) { + Move(dst, Smi::FromInt(src->value() ^ jit_cookie())); + Move(kScratchRegister, Smi::FromInt(jit_cookie())); + xor_(dst, kScratchRegister); + } else { + Move(dst, src); + } +} + + +void MacroAssembler::SafePush(Smi* src) { + ASSERT(kSmiValueSize == 32); // JIT cookie can be converted to Smi. + if (IsUnsafeInt(src->value()) && jit_cookie() != 0) { + Push(Smi::FromInt(src->value() ^ jit_cookie())); + Move(kScratchRegister, Smi::FromInt(jit_cookie())); + xor_(Operand(rsp, 0), kScratchRegister); + } else { + Push(src); + } +} + + // ---------------------------------------------------------------------------- // Smi tagging, untagging and tag detection. @@ -1040,18 +1113,14 @@ void MacroAssembler::SmiTest(Register src) { void MacroAssembler::SmiCompare(Register smi1, Register smi2) { - if (emit_debug_code()) { - AbortIfNotSmi(smi1); - AbortIfNotSmi(smi2); - } + AssertSmi(smi1); + AssertSmi(smi2); cmpq(smi1, smi2); } void MacroAssembler::SmiCompare(Register dst, Smi* src) { - if (emit_debug_code()) { - AbortIfNotSmi(dst); - } + AssertSmi(dst); Cmp(dst, src); } @@ -1068,27 +1137,21 @@ void MacroAssembler::Cmp(Register dst, Smi* src) { void MacroAssembler::SmiCompare(Register dst, const Operand& src) { - if (emit_debug_code()) { - AbortIfNotSmi(dst); - AbortIfNotSmi(src); - } + AssertSmi(dst); + AssertSmi(src); cmpq(dst, src); } void MacroAssembler::SmiCompare(const Operand& dst, Register src) { - if (emit_debug_code()) { - AbortIfNotSmi(dst); - AbortIfNotSmi(src); - } + AssertSmi(dst); + AssertSmi(src); cmpq(dst, src); } void MacroAssembler::SmiCompare(const Operand& dst, Smi* src) { - if (emit_debug_code()) { - AbortIfNotSmi(dst); - } + AssertSmi(dst); cmpl(Operand(dst, kSmiShift / kBitsPerByte), Immediate(src->value())); } @@ -2165,7 +2228,7 @@ void MacroAssembler::JumpIfInstanceTypeIsNotSequentialAscii( kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask; andl(scratch, Immediate(kFlatAsciiStringMask)); - cmpl(scratch, Immediate(kStringTag | kSeqStringTag | kAsciiStringTag)); + cmpl(scratch, Immediate(kStringTag | kSeqStringTag | kOneByteStringTag)); j(not_equal, failure, near_jump); } @@ -2377,7 +2440,7 @@ void MacroAssembler::Call(Address destination, RelocInfo::Mode rmode) { void MacroAssembler::Call(Handle<Code> code_object, RelocInfo::Mode rmode, - unsigned ast_id) { + TypeFeedbackId ast_id) { #ifdef DEBUG int end_position = pc_offset() + CallSize(code_object); #endif @@ -2460,6 +2523,12 @@ MacroAssembler::kSafepointPushRegisterIndices[Register::kNumRegisters] = { }; +void MacroAssembler::StoreToSafepointRegisterSlot(Register dst, + const Immediate& imm) { + movq(SafepointRegisterSlot(dst), imm); +} + + void MacroAssembler::StoreToSafepointRegisterSlot(Register dst, Register src) { movq(SafepointRegisterSlot(dst), src); } @@ -2658,10 +2727,12 @@ void MacroAssembler::CmpInstanceType(Register map, InstanceType type) { void MacroAssembler::CheckFastElements(Register map, Label* fail, Label::Distance distance) { - STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); - STATIC_ASSERT(FAST_ELEMENTS == 1); + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); + STATIC_ASSERT(FAST_ELEMENTS == 2); + STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); cmpb(FieldOperand(map, Map::kBitField2Offset), - Immediate(Map::kMaximumBitField2FastElementValue)); + Immediate(Map::kMaximumBitField2FastHoleyElementValue)); j(above, fail, distance); } @@ -2669,23 +2740,26 @@ void MacroAssembler::CheckFastElements(Register map, void MacroAssembler::CheckFastObjectElements(Register map, Label* fail, Label::Distance distance) { - STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); - STATIC_ASSERT(FAST_ELEMENTS == 1); + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); + STATIC_ASSERT(FAST_ELEMENTS == 2); + STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); cmpb(FieldOperand(map, Map::kBitField2Offset), - Immediate(Map::kMaximumBitField2FastSmiOnlyElementValue)); + Immediate(Map::kMaximumBitField2FastHoleySmiElementValue)); j(below_equal, fail, distance); cmpb(FieldOperand(map, Map::kBitField2Offset), - Immediate(Map::kMaximumBitField2FastElementValue)); + Immediate(Map::kMaximumBitField2FastHoleyElementValue)); j(above, fail, distance); } -void MacroAssembler::CheckFastSmiOnlyElements(Register map, - Label* fail, - Label::Distance distance) { - STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); +void MacroAssembler::CheckFastSmiElements(Register map, + Label* fail, + Label::Distance distance) { + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); cmpb(FieldOperand(map, Map::kBitField2Offset), - Immediate(Map::kMaximumBitField2FastSmiOnlyElementValue)); + Immediate(Map::kMaximumBitField2FastHoleySmiElementValue)); j(above, fail, distance); } @@ -2749,24 +2823,18 @@ void MacroAssembler::CompareMap(Register obj, CompareMapMode mode) { Cmp(FieldOperand(obj, HeapObject::kMapOffset), map); if (mode == ALLOW_ELEMENT_TRANSITION_MAPS) { - Map* transitioned_fast_element_map( - map->LookupElementsTransitionMap(FAST_ELEMENTS, NULL)); - ASSERT(transitioned_fast_element_map == NULL || - map->elements_kind() != FAST_ELEMENTS); - if (transitioned_fast_element_map != NULL) { - j(equal, early_success, Label::kNear); - Cmp(FieldOperand(obj, HeapObject::kMapOffset), - Handle<Map>(transitioned_fast_element_map)); - } - - Map* transitioned_double_map( - map->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, NULL)); - ASSERT(transitioned_double_map == NULL || - map->elements_kind() == FAST_SMI_ONLY_ELEMENTS); - if (transitioned_double_map != NULL) { - j(equal, early_success, Label::kNear); - Cmp(FieldOperand(obj, HeapObject::kMapOffset), - Handle<Map>(transitioned_double_map)); + ElementsKind kind = map->elements_kind(); + if (IsFastElementsKind(kind)) { + bool packed = IsFastPackedElementsKind(kind); + Map* current_map = *map; + while (CanTransitionToMoreGeneralFastElementsKind(kind, packed)) { + kind = GetNextMoreGeneralFastElementsKind(kind, packed); + current_map = current_map->LookupElementsTransitionMap(kind); + if (!current_map) break; + j(equal, early_success, Label::kNear); + Cmp(FieldOperand(obj, HeapObject::kMapOffset), + Handle<Map>(current_map)); + } } } } @@ -2800,33 +2868,66 @@ void MacroAssembler::ClampUint8(Register reg) { void MacroAssembler::ClampDoubleToUint8(XMMRegister input_reg, XMMRegister temp_xmm_reg, - Register result_reg, - Register temp_reg) { + Register result_reg) { Label done; - Set(result_reg, 0); + Label conv_failure; xorps(temp_xmm_reg, temp_xmm_reg); - ucomisd(input_reg, temp_xmm_reg); - j(below, &done, Label::kNear); - uint64_t one_half = BitCast<uint64_t, double>(0.5); - Set(temp_reg, one_half); - movq(temp_xmm_reg, temp_reg); - addsd(temp_xmm_reg, input_reg); - cvttsd2si(result_reg, temp_xmm_reg); + cvtsd2si(result_reg, input_reg); testl(result_reg, Immediate(0xFFFFFF00)); j(zero, &done, Label::kNear); + cmpl(result_reg, Immediate(0x80000000)); + j(equal, &conv_failure, Label::kNear); + movl(result_reg, Immediate(0)); + setcc(above, result_reg); + subl(result_reg, Immediate(1)); + andl(result_reg, Immediate(255)); + jmp(&done, Label::kNear); + bind(&conv_failure); + Set(result_reg, 0); + ucomisd(input_reg, temp_xmm_reg); + j(below, &done, Label::kNear); Set(result_reg, 255); bind(&done); } +static double kUint32Bias = + static_cast<double>(static_cast<uint32_t>(0xFFFFFFFF)) + 1; + + +void MacroAssembler::LoadUint32(XMMRegister dst, + Register src, + XMMRegister scratch) { + Label done; + cmpl(src, Immediate(0)); + movq(kScratchRegister, + reinterpret_cast<int64_t>(&kUint32Bias), + RelocInfo::NONE); + movsd(scratch, Operand(kScratchRegister, 0)); + cvtlsi2sd(dst, src); + j(not_sign, &done, Label::kNear); + addsd(dst, scratch); + bind(&done); +} + + void MacroAssembler::LoadInstanceDescriptors(Register map, Register descriptors) { - movq(descriptors, FieldOperand(map, - Map::kInstanceDescriptorsOrBitField3Offset)); - Label not_smi; - JumpIfNotSmi(descriptors, ¬_smi, Label::kNear); - Move(descriptors, isolate()->factory()->empty_descriptor_array()); - bind(¬_smi); + movq(descriptors, FieldOperand(map, Map::kDescriptorsOffset)); +} + + +void MacroAssembler::NumberOfOwnDescriptors(Register dst, Register map) { + movq(dst, FieldOperand(map, Map::kBitField3Offset)); + DecodeField<Map::NumberOfOwnDescriptorsBits>(dst); +} + + +void MacroAssembler::EnumLength(Register dst, Register map) { + STATIC_ASSERT(Map::EnumLengthBits::kShift == 0); + movq(dst, FieldOperand(map, Map::kBitField3Offset)); + Move(kScratchRegister, Smi::FromInt(Map::EnumLengthBits::kMask)); + and_(dst, kScratchRegister); } @@ -2845,61 +2946,75 @@ void MacroAssembler::DispatchMap(Register obj, } -void MacroAssembler::AbortIfNotNumber(Register object) { - Label ok; - Condition is_smi = CheckSmi(object); - j(is_smi, &ok, Label::kNear); - Cmp(FieldOperand(object, HeapObject::kMapOffset), - isolate()->factory()->heap_number_map()); - Assert(equal, "Operand not a number"); - bind(&ok); +void MacroAssembler::AssertNumber(Register object) { + if (emit_debug_code()) { + Label ok; + Condition is_smi = CheckSmi(object); + j(is_smi, &ok, Label::kNear); + Cmp(FieldOperand(object, HeapObject::kMapOffset), + isolate()->factory()->heap_number_map()); + Check(equal, "Operand is not a number"); + bind(&ok); + } } -void MacroAssembler::AbortIfSmi(Register object) { - Condition is_smi = CheckSmi(object); - Assert(NegateCondition(is_smi), "Operand is a smi"); +void MacroAssembler::AssertNotSmi(Register object) { + if (emit_debug_code()) { + Condition is_smi = CheckSmi(object); + Check(NegateCondition(is_smi), "Operand is a smi"); + } } -void MacroAssembler::AbortIfNotSmi(Register object) { - Condition is_smi = CheckSmi(object); - Assert(is_smi, "Operand is not a smi"); +void MacroAssembler::AssertSmi(Register object) { + if (emit_debug_code()) { + Condition is_smi = CheckSmi(object); + Check(is_smi, "Operand is not a smi"); + } } -void MacroAssembler::AbortIfNotSmi(const Operand& object) { - Condition is_smi = CheckSmi(object); - Assert(is_smi, "Operand is not a smi"); +void MacroAssembler::AssertSmi(const Operand& object) { + if (emit_debug_code()) { + Condition is_smi = CheckSmi(object); + Check(is_smi, "Operand is not a smi"); + } } -void MacroAssembler::AbortIfNotZeroExtended(Register int32_register) { - ASSERT(!int32_register.is(kScratchRegister)); - movq(kScratchRegister, 0x100000000l, RelocInfo::NONE); - cmpq(kScratchRegister, int32_register); - Assert(above_equal, "32 bit value in register is not zero-extended"); +void MacroAssembler::AssertZeroExtended(Register int32_register) { + if (emit_debug_code()) { + ASSERT(!int32_register.is(kScratchRegister)); + movq(kScratchRegister, 0x100000000l, RelocInfo::NONE); + cmpq(kScratchRegister, int32_register); + Check(above_equal, "32 bit value in register is not zero-extended"); + } } -void MacroAssembler::AbortIfNotString(Register object) { - testb(object, Immediate(kSmiTagMask)); - Assert(not_equal, "Operand is not a string"); - push(object); - movq(object, FieldOperand(object, HeapObject::kMapOffset)); - CmpInstanceType(object, FIRST_NONSTRING_TYPE); - pop(object); - Assert(below, "Operand is not a string"); +void MacroAssembler::AssertString(Register object) { + if (emit_debug_code()) { + testb(object, Immediate(kSmiTagMask)); + Check(not_equal, "Operand is a smi and not a string"); + push(object); + movq(object, FieldOperand(object, HeapObject::kMapOffset)); + CmpInstanceType(object, FIRST_NONSTRING_TYPE); + pop(object); + Check(below, "Operand is not a string"); + } } -void MacroAssembler::AbortIfNotRootValue(Register src, - Heap::RootListIndex root_value_index, - const char* message) { - ASSERT(!src.is(kScratchRegister)); - LoadRoot(kScratchRegister, root_value_index); - cmpq(src, kScratchRegister); - Check(equal, message); +void MacroAssembler::AssertRootValue(Register src, + Heap::RootListIndex root_value_index, + const char* message) { + if (emit_debug_code()) { + ASSERT(!src.is(kScratchRegister)); + LoadRoot(kScratchRegister, root_value_index); + cmpq(src, kScratchRegister); + Check(equal, message); + } } @@ -3396,20 +3511,21 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, cmpq(scratch, Immediate(0)); Check(not_equal, "we should not have an empty lexical context"); } - // Load the global context of the current context. - int offset = Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; + // Load the native context of the current context. + int offset = + Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize; movq(scratch, FieldOperand(scratch, offset)); - movq(scratch, FieldOperand(scratch, GlobalObject::kGlobalContextOffset)); + movq(scratch, FieldOperand(scratch, GlobalObject::kNativeContextOffset)); - // Check the context is a global context. + // Check the context is a native context. if (emit_debug_code()) { Cmp(FieldOperand(scratch, HeapObject::kMapOffset), - isolate()->factory()->global_context_map()); - Check(equal, "JSGlobalObject::global_context should be a global context."); + isolate()->factory()->native_context_map()); + Check(equal, "JSGlobalObject::native_context should be a native context."); } // Check if both contexts are the same. - cmpq(scratch, FieldOperand(holder_reg, JSGlobalProxy::kContextOffset)); + cmpq(scratch, FieldOperand(holder_reg, JSGlobalProxy::kNativeContextOffset)); j(equal, &same_contexts); // Compare security tokens. @@ -3417,23 +3533,24 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, // compatible with the security token in the receiving global // object. - // Check the context is a global context. + // Check the context is a native context. if (emit_debug_code()) { // Preserve original value of holder_reg. push(holder_reg); - movq(holder_reg, FieldOperand(holder_reg, JSGlobalProxy::kContextOffset)); + movq(holder_reg, + FieldOperand(holder_reg, JSGlobalProxy::kNativeContextOffset)); CompareRoot(holder_reg, Heap::kNullValueRootIndex); Check(not_equal, "JSGlobalProxy::context() should not be null."); - // Read the first word and compare to global_context_map(), + // Read the first word and compare to native_context_map(), movq(holder_reg, FieldOperand(holder_reg, HeapObject::kMapOffset)); - CompareRoot(holder_reg, Heap::kGlobalContextMapRootIndex); - Check(equal, "JSGlobalObject::global_context should be a global context."); + CompareRoot(holder_reg, Heap::kNativeContextMapRootIndex); + Check(equal, "JSGlobalObject::native_context should be a native context."); pop(holder_reg); } movq(kScratchRegister, - FieldOperand(holder_reg, JSGlobalProxy::kContextOffset)); + FieldOperand(holder_reg, JSGlobalProxy::kNativeContextOffset)); int token_offset = Context::kHeaderSize + Context::SECURITY_TOKEN_INDEX * kPointerSize; movq(scratch, FieldOperand(scratch, token_offset)); @@ -3955,7 +4072,7 @@ void MacroAssembler::CopyBytes(Register destination, int min_length, Register scratch) { ASSERT(min_length >= 0); - if (FLAG_debug_code) { + if (emit_debug_code()) { cmpl(length, Immediate(min_length)); Assert(greater_equal, "Invalid min_length"); } @@ -4053,31 +4170,43 @@ void MacroAssembler::LoadTransitionedArrayMapConditional( Register scratch, Label* no_map_match) { // Load the global or builtins object from the current context. - movq(scratch, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); - movq(scratch, FieldOperand(scratch, GlobalObject::kGlobalContextOffset)); + movq(scratch, + Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX))); + movq(scratch, FieldOperand(scratch, GlobalObject::kNativeContextOffset)); // Check that the function's map is the same as the expected cached map. - int expected_index = - Context::GetContextMapIndexFromElementsKind(expected_kind); - cmpq(map_in_out, Operand(scratch, Context::SlotOffset(expected_index))); + movq(scratch, Operand(scratch, + Context::SlotOffset(Context::JS_ARRAY_MAPS_INDEX))); + + int offset = expected_kind * kPointerSize + + FixedArrayBase::kHeaderSize; + cmpq(map_in_out, FieldOperand(scratch, offset)); j(not_equal, no_map_match); // Use the transitioned cached map. - int trans_index = - Context::GetContextMapIndexFromElementsKind(transitioned_kind); - movq(map_in_out, Operand(scratch, Context::SlotOffset(trans_index))); + offset = transitioned_kind * kPointerSize + + FixedArrayBase::kHeaderSize; + movq(map_in_out, FieldOperand(scratch, offset)); } void MacroAssembler::LoadInitialArrayMap( - Register function_in, Register scratch, Register map_out) { + Register function_in, Register scratch, + Register map_out, bool can_have_holes) { ASSERT(!function_in.is(map_out)); Label done; movq(map_out, FieldOperand(function_in, JSFunction::kPrototypeOrInitialMapOffset)); if (!FLAG_smi_only_arrays) { - LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, - FAST_ELEMENTS, + ElementsKind kind = can_have_holes ? FAST_HOLEY_ELEMENTS : FAST_ELEMENTS; + LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, + kind, + map_out, + scratch, + &done); + } else if (can_have_holes) { + LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, + FAST_HOLEY_SMI_ELEMENTS, map_out, scratch, &done); @@ -4093,10 +4222,11 @@ static const int kRegisterPassedArguments = 6; void MacroAssembler::LoadGlobalFunction(int index, Register function) { // Load the global or builtins object from the current context. - movq(function, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); - // Load the global context from the global or builtins object. - movq(function, FieldOperand(function, GlobalObject::kGlobalContextOffset)); - // Load the function from the global context. + movq(function, + Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX))); + // Load the native context from the global or builtins object. + movq(function, FieldOperand(function, GlobalObject::kNativeContextOffset)); + // Load the function from the native context. movq(function, Operand(function, Context::SlotOffset(index))); } @@ -4321,7 +4451,7 @@ void MacroAssembler::EnsureNotWhite( testq(Operand(bitmap_scratch, MemoryChunk::kHeaderSize), mask_scratch); j(not_zero, &done, Label::kNear); - if (FLAG_debug_code) { + if (emit_debug_code()) { // Check for impossible bit pattern. Label ok; push(mask_scratch); @@ -4373,7 +4503,7 @@ void MacroAssembler::EnsureNotWhite( bind(¬_external); // Sequential string, either ASCII or UC16. - ASSERT(kAsciiStringTag == 0x04); + ASSERT(kOneByteStringTag == 0x04); and_(length, Immediate(kStringEncodingMask)); xor_(length, Immediate(kStringEncodingMask)); addq(length, Immediate(0x04)); @@ -4396,44 +4526,38 @@ void MacroAssembler::EnsureNotWhite( void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) { - Label next; + Label next, start; Register empty_fixed_array_value = r8; LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex); - Register empty_descriptor_array_value = r9; - LoadRoot(empty_descriptor_array_value, - Heap::kEmptyDescriptorArrayRootIndex); movq(rcx, rax); - bind(&next); - - // Check that there are no elements. Register rcx contains the - // current JS object we've reached through the prototype chain. - cmpq(empty_fixed_array_value, - FieldOperand(rcx, JSObject::kElementsOffset)); - j(not_equal, call_runtime); - // Check that instance descriptors are not empty so that we can - // check for an enum cache. Leave the map in rbx for the subsequent - // prototype load. + // Check if the enum length field is properly initialized, indicating that + // there is an enum cache. movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset)); - movq(rdx, FieldOperand(rbx, Map::kInstanceDescriptorsOrBitField3Offset)); - JumpIfSmi(rdx, call_runtime); - // Check that there is an enum cache in the non-empty instance - // descriptors (rdx). This is the case if the next enumeration - // index field does not contain a smi. - movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumerationIndexOffset)); - JumpIfSmi(rdx, call_runtime); + EnumLength(rdx, rbx); + Cmp(rdx, Smi::FromInt(Map::kInvalidEnumCache)); + j(equal, call_runtime); + + jmp(&start); + + bind(&next); + + movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset)); // For all objects but the receiver, check that the cache is empty. - Label check_prototype; - cmpq(rcx, rax); - j(equal, &check_prototype, Label::kNear); - movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumCacheBridgeCacheOffset)); - cmpq(rdx, empty_fixed_array_value); + EnumLength(rdx, rbx); + Cmp(rdx, Smi::FromInt(0)); + j(not_equal, call_runtime); + + bind(&start); + + // Check that there are no elements. Register rcx contains the current JS + // object we've reached through the prototype chain. + cmpq(empty_fixed_array_value, + FieldOperand(rcx, JSObject::kElementsOffset)); j(not_equal, call_runtime); - // Load the prototype from the map and loop if non-null. - bind(&check_prototype); movq(rcx, FieldOperand(rbx, Map::kPrototypeOffset)); cmpq(rcx, null_value); j(not_equal, &next); diff --git a/src/3rdparty/v8/src/x64/macro-assembler-x64.h b/src/3rdparty/v8/src/x64/macro-assembler-x64.h index 5ec8873..fdddc13 100644 --- a/src/3rdparty/v8/src/x64/macro-assembler-x64.h +++ b/src/3rdparty/v8/src/x64/macro-assembler-x64.h @@ -317,6 +317,7 @@ class MacroAssembler: public Assembler { void PopSafepointRegisters() { Popad(); } // Store the value in register src in the safepoint register stack // slot for register dst. + void StoreToSafepointRegisterSlot(Register dst, const Immediate& imm); void StoreToSafepointRegisterSlot(Register dst, Register src); void LoadFromSafepointRegisterSlot(Register dst, Register src); @@ -774,6 +775,11 @@ class MacroAssembler: public Assembler { // Move if the registers are not identical. void Move(Register target, Register source); + // Support for constant splitting. + bool IsUnsafeInt(const int x); + void SafeMove(Register dst, Smi* src); + void SafePush(Smi* src); + // Bit-field support. void TestBit(const Operand& dst, int bit_index); @@ -817,7 +823,7 @@ class MacroAssembler: public Assembler { void Call(ExternalReference ext); void Call(Handle<Code> code_object, RelocInfo::Mode rmode, - unsigned ast_id = kNoASTId); + TypeFeedbackId ast_id = TypeFeedbackId::None()); // The size of the code generated for different call instructions. int CallSize(Address destination, RelocInfo::Mode rmode) { @@ -877,9 +883,9 @@ class MacroAssembler: public Assembler { // Check if a map for a JSObject indicates that the object has fast smi only // elements. Jump to the specified label if it does not. - void CheckFastSmiOnlyElements(Register map, - Label* fail, - Label::Distance distance = Label::kFar); + void CheckFastSmiElements(Register map, + Label* fail, + Label::Distance distance = Label::kFar); // Check to see if maybe_number can be stored as a double in // FastDoubleElements. If it can, store it at the index specified by index in @@ -936,32 +942,45 @@ class MacroAssembler: public Assembler { void ClampDoubleToUint8(XMMRegister input_reg, XMMRegister temp_xmm_reg, - Register result_reg, - Register temp_reg); + Register result_reg); + + void LoadUint32(XMMRegister dst, Register src, XMMRegister scratch); void LoadInstanceDescriptors(Register map, Register descriptors); + void EnumLength(Register dst, Register map); + void NumberOfOwnDescriptors(Register dst, Register map); + + template<typename Field> + void DecodeField(Register reg) { + static const int shift = Field::kShift + kSmiShift; + static const int mask = Field::kMask >> Field::kShift; + shr(reg, Immediate(shift)); + and_(reg, Immediate(mask)); + shl(reg, Immediate(kSmiShift)); + } - // Abort execution if argument is not a number. Used in debug code. - void AbortIfNotNumber(Register object); + // Abort execution if argument is not a number, enabled via --debug-code. + void AssertNumber(Register object); - // Abort execution if argument is a smi. Used in debug code. - void AbortIfSmi(Register object); + // Abort execution if argument is a smi, enabled via --debug-code. + void AssertNotSmi(Register object); - // Abort execution if argument is not a smi. Used in debug code. - void AbortIfNotSmi(Register object); - void AbortIfNotSmi(const Operand& object); + // Abort execution if argument is not a smi, enabled via --debug-code. + void AssertSmi(Register object); + void AssertSmi(const Operand& object); // Abort execution if a 64 bit register containing a 32 bit payload does not - // have zeros in the top 32 bits. - void AbortIfNotZeroExtended(Register reg); + // have zeros in the top 32 bits, enabled via --debug-code. + void AssertZeroExtended(Register reg); - // Abort execution if argument is a string. Used in debug code. - void AbortIfNotString(Register object); + // Abort execution if argument is not a string, enabled via --debug-code. + void AssertString(Register object); - // Abort execution if argument is not the root value with the given index. - void AbortIfNotRootValue(Register src, - Heap::RootListIndex root_value_index, - const char* message); + // Abort execution if argument is not the root value with the given index, + // enabled via --debug-code. + void AssertRootValue(Register src, + Heap::RootListIndex root_value_index, + const char* message); // --------------------------------------------------------------------------- // Exception handling @@ -1128,8 +1147,8 @@ class MacroAssembler: public Assembler { void LoadContext(Register dst, int context_chain_length); // Conditionally load the cached Array transitioned map of type - // transitioned_kind from the global context if the map in register - // map_in_out is the cached Array map in the global context of + // transitioned_kind from the native context if the map in register + // map_in_out is the cached Array map in the native context of // expected_kind. void LoadTransitionedArrayMapConditional( ElementsKind expected_kind, @@ -1141,7 +1160,8 @@ class MacroAssembler: public Assembler { // Load the initial map for new Arrays from a JSFunction. void LoadInitialArrayMap(Register function_in, Register scratch, - Register map_out); + Register map_out, + bool can_have_holes); // Load the global function with the given index. void LoadGlobalFunction(int index, Register function); @@ -1154,7 +1174,7 @@ class MacroAssembler: public Assembler { // Runtime calls // Call a code stub. - void CallStub(CodeStub* stub, unsigned ast_id = kNoASTId); + void CallStub(CodeStub* stub, TypeFeedbackId ast_id = TypeFeedbackId::None()); // Tail call a code stub (jump). void TailCallStub(CodeStub* stub); @@ -1322,6 +1342,8 @@ class MacroAssembler: public Assembler { // modified. It may be the "smi 1 constant" register. Register GetSmiConstant(Smi* value); + intptr_t RootRegisterDelta(ExternalReference other); + // Moves the smi value to the destination register. void LoadSmiConstant(Register dst, Smi* value); @@ -1441,12 +1463,12 @@ inline Operand ContextOperand(Register context, int index) { inline Operand GlobalObjectOperand() { - return ContextOperand(rsi, Context::GLOBAL_INDEX); + return ContextOperand(rsi, Context::GLOBAL_OBJECT_INDEX); } static inline Operand QmlGlobalObjectOperand() { - return ContextOperand(rsi, Context::QML_GLOBAL_INDEX); + return ContextOperand(rsi, Context::QML_GLOBAL_OBJECT_INDEX); } diff --git a/src/3rdparty/v8/src/x64/regexp-macro-assembler-x64.cc b/src/3rdparty/v8/src/x64/regexp-macro-assembler-x64.cc index bf232bf..86f7bfe 100644 --- a/src/3rdparty/v8/src/x64/regexp-macro-assembler-x64.cc +++ b/src/3rdparty/v8/src/x64/regexp-macro-assembler-x64.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 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: @@ -44,21 +44,23 @@ namespace internal { /* * This assembler uses the following register assignment convention - * - rdx : currently loaded character(s) as ASCII or UC16. Must be loaded using - * LoadCurrentCharacter before using any of the dispatch methods. - * - rdi : current position in input, as negative offset from end of string. + * - rdx : Currently loaded character(s) as ASCII or UC16. Must be loaded + * using LoadCurrentCharacter before using any of the dispatch methods. + * Temporarily stores the index of capture start after a matching pass + * for a global regexp. + * - rdi : Current position in input, as negative offset from end of string. * Please notice that this is the byte offset, not the character - * offset! Is always a 32-bit signed (negative) offset, but must be + * offset! Is always a 32-bit signed (negative) offset, but must be * maintained sign-extended to 64 bits, since it is used as index. - * - rsi : end of input (points to byte after last character in input), + * - rsi : End of input (points to byte after last character in input), * so that rsi+rdi points to the current character. - * - rbp : frame pointer. Used to access arguments, local variables and + * - rbp : Frame pointer. Used to access arguments, local variables and * RegExp registers. - * - rsp : points to tip of C stack. - * - rcx : points to tip of backtrack stack. The backtrack stack contains - * only 32-bit values. Most are offsets from some base (e.g., character + * - rsp : Points to tip of C stack. + * - rcx : Points to tip of backtrack stack. The backtrack stack contains + * only 32-bit values. Most are offsets from some base (e.g., character * positions from end of string or code location from Code* pointer). - * - r8 : code object pointer. Used to convert between absolute and + * - r8 : Code object pointer. Used to convert between absolute and * code-object-relative addresses. * * The registers rax, rbx, r9 and r11 are free to use for computations. @@ -72,20 +74,22 @@ namespace internal { * * The stack will have the following content, in some order, indexable from the * frame pointer (see, e.g., kStackHighEnd): - * - Isolate* isolate (Address of the current isolate) + * - Isolate* isolate (address of the current isolate) * - direct_call (if 1, direct call from JavaScript code, if 0 call * through the runtime system) - * - stack_area_base (High end of the memory area to use as + * - stack_area_base (high end of the memory area to use as * backtracking stack) + * - capture array size (may fit multiple sets of matches) * - int* capture_array (int[num_saved_registers_], for output). - * - end of input (Address of end of string) - * - start of input (Address of first character in string) + * - end of input (address of end of string) + * - start of input (address of first character in string) * - start index (character index of start) * - String* input_string (input string) * - return address * - backup of callee save registers (rbx, possibly rsi and rdi). + * - success counter (only useful for global regexp to count matches) * - Offset of location before start of input (effectively character - * position -1). Used to initialize capture registers to a non-position. + * position -1). Used to initialize capture registers to a non-position. * - At start of string (if 1, we are starting at the start of the * string, otherwise 0) * - register 0 rbp[-n] (Only positions must be stored in the first @@ -94,7 +98,7 @@ namespace internal { * * The first num_saved_registers_ registers are initialized to point to * "character -1" in the string (i.e., char_size() bytes before the first - * character of the string). The remaining registers starts out uninitialized. + * character of the string). The remaining registers starts out uninitialized. * * The first seven values must be provided by the calling code by * calling the code's entry address cast to a function pointer with the @@ -113,10 +117,12 @@ namespace internal { RegExpMacroAssemblerX64::RegExpMacroAssemblerX64( Mode mode, - int registers_to_save) - : masm_(Isolate::Current(), NULL, kRegExpCodeSize), + int registers_to_save, + Zone* zone) + : NativeRegExpMacroAssembler(zone), + masm_(Isolate::Current(), NULL, kRegExpCodeSize), no_root_array_scope_(&masm_), - code_relative_fixup_positions_(4), + code_relative_fixup_positions_(4, zone), mode_(mode), num_registers_(registers_to_save), num_saved_registers_(registers_to_save), @@ -347,6 +353,14 @@ void RegExpMacroAssemblerX64::CheckNotBackReferenceIgnoreCase( // In either case succeed immediately. __ j(equal, &fallthrough); + // ----------------------- + // rdx - Start of capture + // rbx - length of capture + // Check that there are sufficient characters left in the input. + __ movl(rax, rdi); + __ addl(rax, rbx); + BranchOrBacktrack(greater, on_no_match); + if (mode_ == ASCII) { Label loop_increment; if (on_no_match == NULL) { @@ -523,15 +537,6 @@ void RegExpMacroAssemblerX64::CheckNotBackReference( } -void RegExpMacroAssemblerX64::CheckNotRegistersEqual(int reg1, - int reg2, - Label* on_not_equal) { - __ movq(rax, register_location(reg1)); - __ cmpq(rax, register_location(reg2)); - BranchOrBacktrack(not_equal, on_not_equal); -} - - void RegExpMacroAssemblerX64::CheckNotCharacter(uint32_t c, Label* on_not_equal) { __ cmpl(current_character(), Immediate(c)); @@ -744,13 +749,16 @@ bool RegExpMacroAssemblerX64::CheckSpecialCharacterClass(uc16 type, void RegExpMacroAssemblerX64::Fail() { - ASSERT(FAILURE == 0); // Return value for failure is zero. - __ Set(rax, 0); + STATIC_ASSERT(FAILURE == 0); // Return value for failure is zero. + if (!global()) { + __ Set(rax, FAILURE); + } __ jmp(&exit_label_); } Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) { + Label return_rax; // Finalize code - write the entry point code now we know how many // registers we need. // Entry code: @@ -784,7 +792,7 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) { ASSERT_EQ(kInputStart, -3 * kPointerSize); ASSERT_EQ(kInputEnd, -4 * kPointerSize); ASSERT_EQ(kRegisterOutput, -5 * kPointerSize); - ASSERT_EQ(kStackHighEnd, -6 * kPointerSize); + ASSERT_EQ(kNumOutputRegisters, -6 * kPointerSize); __ push(rdi); __ push(rsi); __ push(rdx); @@ -795,7 +803,8 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) { __ push(rbx); // Callee-save #endif - __ push(Immediate(0)); // Make room for "at start" constant. + __ push(Immediate(0)); // Number of successful matches in a global regexp. + __ push(Immediate(0)); // Make room for "input start - 1" constant. // Check if we have space on the stack for registers. Label stack_limit_hit; @@ -815,14 +824,14 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) { // Exit with OutOfMemory exception. There is not enough space on the stack // for our working registers. __ Set(rax, EXCEPTION); - __ jmp(&exit_label_); + __ jmp(&return_rax); __ bind(&stack_limit_hit); __ Move(code_object_pointer(), masm_.CodeObject()); CallCheckStackGuardState(); // Preserves no registers beside rbp and rsp. __ testq(rax, rax); // If returned value is non-zero, we exit with the returned value as result. - __ j(not_zero, &exit_label_); + __ j(not_zero, &return_rax); __ bind(&stack_ok); @@ -847,19 +856,7 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) { // position registers. __ movq(Operand(rbp, kInputStartMinusOne), rax); - if (num_saved_registers_ > 0) { - // Fill saved registers with initial value = start offset - 1 - // Fill in stack push order, to avoid accessing across an unwritten - // page (a problem on Windows). - __ Set(rcx, kRegisterZero); - Label init_loop; - __ bind(&init_loop); - __ movq(Operand(rbp, rcx, times_1, 0), rax); - __ subq(rcx, Immediate(kPointerSize)); - __ cmpq(rcx, - Immediate(kRegisterZero - num_saved_registers_ * kPointerSize)); - __ j(greater, &init_loop); - } +#ifdef WIN32 // Ensure that we have written to each stack page, in order. Skipping a page // on Windows can cause segmentation faults. Assuming page size is 4k. const int kPageSize = 4096; @@ -869,21 +866,49 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) { i += kRegistersPerPage) { __ movq(register_location(i), rax); // One write every page. } +#endif // WIN32 - // Initialize backtrack stack pointer. - __ movq(backtrack_stackpointer(), Operand(rbp, kStackHighEnd)); // Initialize code object pointer. __ Move(code_object_pointer(), masm_.CodeObject()); - // Load previous char as initial value of current-character. - Label at_start; - __ cmpb(Operand(rbp, kStartIndex), Immediate(0)); - __ j(equal, &at_start); - LoadCurrentCharacterUnchecked(-1, 1); // Load previous char. - __ jmp(&start_label_); - __ bind(&at_start); + + Label load_char_start_regexp, start_regexp; + // Load newline if index is at start, previous character otherwise. + __ cmpl(Operand(rbp, kStartIndex), Immediate(0)); + __ j(not_equal, &load_char_start_regexp, Label::kNear); __ Set(current_character(), '\n'); - __ jmp(&start_label_); + __ jmp(&start_regexp, Label::kNear); + + // Global regexp restarts matching here. + __ bind(&load_char_start_regexp); + // Load previous char as initial value of current character register. + LoadCurrentCharacterUnchecked(-1, 1); + __ bind(&start_regexp); + + // Initialize on-stack registers. + if (num_saved_registers_ > 0) { + // Fill saved registers with initial value = start offset - 1 + // Fill in stack push order, to avoid accessing across an unwritten + // page (a problem on Windows). + if (num_saved_registers_ > 8) { + __ Set(rcx, kRegisterZero); + Label init_loop; + __ bind(&init_loop); + __ movq(Operand(rbp, rcx, times_1, 0), rax); + __ subq(rcx, Immediate(kPointerSize)); + __ cmpq(rcx, + Immediate(kRegisterZero - num_saved_registers_ * kPointerSize)); + __ j(greater, &init_loop); + } else { // Unroll the loop. + for (int i = 0; i < num_saved_registers_; i++) { + __ movq(register_location(i), rax); + } + } + } + // Initialize backtrack stack pointer. + __ movq(backtrack_stackpointer(), Operand(rbp, kStackHighEnd)); + + __ jmp(&start_label_); // Exit code: if (success_label_.is_linked()) { @@ -902,6 +927,10 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) { } for (int i = 0; i < num_saved_registers_; i++) { __ movq(rax, register_location(i)); + if (i == 0 && global_with_zero_length_check()) { + // Keep capture start in rdx for the zero-length check later. + __ movq(rdx, rax); + } __ addq(rax, rcx); // Convert to index from start, not end. if (mode_ == UC16) { __ sar(rax, Immediate(1)); // Convert byte index to character index. @@ -909,12 +938,57 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) { __ movl(Operand(rbx, i * kIntSize), rax); } } - __ Set(rax, SUCCESS); + + if (global()) { + // Restart matching if the regular expression is flagged as global. + // Increment success counter. + __ incq(Operand(rbp, kSuccessfulCaptures)); + // Capture results have been stored, so the number of remaining global + // output registers is reduced by the number of stored captures. + __ movsxlq(rcx, Operand(rbp, kNumOutputRegisters)); + __ subq(rcx, Immediate(num_saved_registers_)); + // Check whether we have enough room for another set of capture results. + __ cmpq(rcx, Immediate(num_saved_registers_)); + __ j(less, &exit_label_); + + __ movq(Operand(rbp, kNumOutputRegisters), rcx); + // Advance the location for output. + __ addq(Operand(rbp, kRegisterOutput), + Immediate(num_saved_registers_ * kIntSize)); + + // Prepare rax to initialize registers with its value in the next run. + __ movq(rax, Operand(rbp, kInputStartMinusOne)); + + if (global_with_zero_length_check()) { + // Special case for zero-length matches. + // rdx: capture start index + __ cmpq(rdi, rdx); + // Not a zero-length match, restart. + __ j(not_equal, &load_char_start_regexp); + // rdi (offset from the end) is zero if we already reached the end. + __ testq(rdi, rdi); + __ j(zero, &exit_label_, Label::kNear); + // Advance current position after a zero-length match. + if (mode_ == UC16) { + __ addq(rdi, Immediate(2)); + } else { + __ incq(rdi); + } + } + + __ jmp(&load_char_start_regexp); + } else { + __ movq(rax, Immediate(SUCCESS)); + } } - // Exit and return rax __ bind(&exit_label_); + if (global()) { + // Return the number of successful captures. + __ movq(rax, Operand(rbp, kSuccessfulCaptures)); + } + __ bind(&return_rax); #ifdef _WIN64 // Restore callee save registers. __ lea(rsp, Operand(rbp, kLastCalleeSaveRegister)); @@ -951,7 +1025,7 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) { __ testq(rax, rax); // If returning non-zero, we should end execution with the given // result as return value. - __ j(not_zero, &exit_label_); + __ j(not_zero, &return_rax); // Restore registers. __ Move(code_object_pointer(), masm_.CodeObject()); @@ -1012,7 +1086,7 @@ Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) { __ bind(&exit_with_exception); // Exit with Result EXCEPTION(-1) to signal thrown exception. __ Set(rax, EXCEPTION); - __ jmp(&exit_label_); + __ jmp(&return_rax); } FixupCodeRelativePositions(); @@ -1135,8 +1209,9 @@ void RegExpMacroAssemblerX64::SetRegister(int register_index, int to) { } -void RegExpMacroAssemblerX64::Succeed() { +bool RegExpMacroAssemblerX64::Succeed() { __ jmp(&success_label_); + return global(); } diff --git a/src/3rdparty/v8/src/x64/regexp-macro-assembler-x64.h b/src/3rdparty/v8/src/x64/regexp-macro-assembler-x64.h index cd24b60..a082cf2 100644 --- a/src/3rdparty/v8/src/x64/regexp-macro-assembler-x64.h +++ b/src/3rdparty/v8/src/x64/regexp-macro-assembler-x64.h @@ -1,4 +1,4 @@ -// Copyright 2010 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -41,7 +41,7 @@ namespace internal { class RegExpMacroAssemblerX64: public NativeRegExpMacroAssembler { public: - RegExpMacroAssemblerX64(Mode mode, int registers_to_save); + RegExpMacroAssemblerX64(Mode mode, int registers_to_save, Zone* zone); virtual ~RegExpMacroAssemblerX64(); virtual int stack_limit_slack(); virtual void AdvanceCurrentPosition(int by); @@ -66,7 +66,6 @@ class RegExpMacroAssemblerX64: public NativeRegExpMacroAssembler { virtual void CheckNotBackReference(int start_reg, Label* on_no_match); 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, @@ -109,7 +108,7 @@ class RegExpMacroAssemblerX64: public NativeRegExpMacroAssembler { virtual void ReadStackPointerFromRegister(int reg); virtual void SetCurrentPositionFromEnd(int by); virtual void SetRegister(int register_index, int to); - virtual void Succeed(); + virtual bool Succeed(); virtual void WriteCurrentPositionToRegister(int reg, int cp_offset); virtual void ClearRegisters(int reg_from, int reg_to); virtual void WriteStackPointerToRegister(int reg); @@ -154,7 +153,12 @@ class RegExpMacroAssemblerX64: public NativeRegExpMacroAssembler { static const int kInputStart = kStartIndex + kPointerSize; static const int kInputEnd = kInputStart + kPointerSize; static const int kRegisterOutput = kInputEnd + kPointerSize; - static const int kStackHighEnd = kRegisterOutput + kPointerSize; + // For the case of global regular expression, we have room to store at least + // one set of capture results. For the case of non-global regexp, we ignore + // this value. NumOutputRegisters is passed as 32-bit value. The upper + // 32 bit of this 64-bit stack slot may contain garbage. + static const int kNumOutputRegisters = kRegisterOutput + kPointerSize; + static const int kStackHighEnd = kNumOutputRegisters + kPointerSize; // DirectCall is passed as 32 bit int (values 0 or 1). static const int kDirectCall = kStackHighEnd + kPointerSize; static const int kIsolate = kDirectCall + kPointerSize; @@ -167,8 +171,12 @@ class RegExpMacroAssemblerX64: public NativeRegExpMacroAssembler { static const int kInputStart = kStartIndex - kPointerSize; static const int kInputEnd = kInputStart - kPointerSize; static const int kRegisterOutput = kInputEnd - kPointerSize; - static const int kStackHighEnd = kRegisterOutput - kPointerSize; - static const int kDirectCall = kFrameAlign; + // For the case of global regular expression, we have room to store at least + // one set of capture results. For the case of non-global regexp, we ignore + // this value. + static const int kNumOutputRegisters = kRegisterOutput - kPointerSize; + static const int kStackHighEnd = kFrameAlign; + static const int kDirectCall = kStackHighEnd + kPointerSize; static const int kIsolate = kDirectCall + kPointerSize; #endif @@ -183,14 +191,14 @@ class RegExpMacroAssemblerX64: public NativeRegExpMacroAssembler { // AMD64 Calling Convention has only one callee-save register that // we use. We push this after the frame pointer (and after the // parameters). - static const int kBackup_rbx = kStackHighEnd - kPointerSize; + static const int kBackup_rbx = kNumOutputRegisters - kPointerSize; static const int kLastCalleeSaveRegister = kBackup_rbx; #endif + static const int kSuccessfulCaptures = kLastCalleeSaveRegister - kPointerSize; // When adding local variables remember to push space for them in // the frame in GetCode. - static const int kInputStartMinusOne = - kLastCalleeSaveRegister - kPointerSize; + static const int kInputStartMinusOne = kSuccessfulCaptures - kPointerSize; // First register address. Following registers are below it on the stack. static const int kRegisterZero = kInputStartMinusOne - kPointerSize; @@ -232,7 +240,7 @@ class RegExpMacroAssemblerX64: public NativeRegExpMacroAssembler { void BranchOrBacktrack(Condition condition, Label* to); void MarkPositionForCodeRelativeFixup() { - code_relative_fixup_positions_.Add(masm_.pc_offset()); + code_relative_fixup_positions_.Add(masm_.pc_offset(), zone()); } void FixupCodeRelativePositions(); diff --git a/src/3rdparty/v8/src/x64/simulator-x64.h b/src/3rdparty/v8/src/x64/simulator-x64.h index df8423a..8aba701 100644 --- a/src/3rdparty/v8/src/x64/simulator-x64.h +++ b/src/3rdparty/v8/src/x64/simulator-x64.h @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 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,12 +40,12 @@ namespace internal { (entry(p0, p1, p2, p3, p4)) typedef int (*regexp_matcher)(String*, int, const byte*, - const byte*, int*, Address, int, Isolate*); + const byte*, int*, int, Address, int, Isolate*); // Call the generated regexp code directly. The code at the entry address should // expect eight int/pointer sized arguments and return an int. -#define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7) \ - (FUNCTION_CAST<regexp_matcher>(entry)(p0, p1, p2, p3, p4, p5, p6, p7)) +#define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7, p8) \ + (FUNCTION_CAST<regexp_matcher>(entry)(p0, p1, p2, p3, p4, p5, p6, p7, p8)) #define TRY_CATCH_FROM_ADDRESS(try_catch_address) \ (reinterpret_cast<TryCatch*>(try_catch_address)) diff --git a/src/3rdparty/v8/src/x64/stub-cache-x64.cc b/src/3rdparty/v8/src/x64/stub-cache-x64.cc index 5721e9b..b120efb 100644 --- a/src/3rdparty/v8/src/x64/stub-cache-x64.cc +++ b/src/3rdparty/v8/src/x64/stub-cache-x64.cc @@ -228,15 +228,15 @@ void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm, 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. + Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX))); + // Load the native context from the global or builtins object. __ movq(prototype, - FieldOperand(prototype, GlobalObject::kGlobalContextOffset)); - // Load the function from the global context. + FieldOperand(prototype, GlobalObject::kNativeContextOffset)); + // Load the function from the native 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)); + FieldOperand(prototype, JSFunction::kPrototypeOrInitialMapOffset)); // Load the prototype from the initial map. __ movq(prototype, FieldOperand(prototype, Map::kPrototypeOffset)); } @@ -249,13 +249,13 @@ void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype( Label* miss) { Isolate* isolate = masm->isolate(); // Check we're still in the same context. - __ Move(prototype, isolate->global()); - __ cmpq(Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)), + __ Move(prototype, isolate->global_object()); + __ cmpq(Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)), prototype); __ j(not_equal, miss); // Get the global function with the given index. Handle<JSFunction> function( - JSFunction::cast(isolate->global_context()->get(index))); + JSFunction::cast(isolate->native_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. @@ -731,10 +731,22 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, Handle<JSObject> object, int index, Handle<Map> transition, + Handle<String> name, Register receiver_reg, Register name_reg, - Register scratch, + Register scratch1, + Register scratch2, Label* miss_label) { + LookupResult lookup(masm->isolate()); + object->Lookup(*name, &lookup); + if (lookup.IsFound() && (lookup.IsReadOnly() || !lookup.IsCacheable())) { + // In sloppy mode, we could just return the value and be done. However, we + // might be in strict mode, where we have to throw. Since we cannot tell, + // go into slow case unconditionally. + __ jmp(miss_label); + return; + } + // Check that the map of the object hasn't changed. CompareMapMode mode = transition.is_null() ? ALLOW_ELEMENT_TRANSITION_MAPS : REQUIRE_EXACT_MAP; @@ -743,7 +755,32 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, // Perform global security token check if needed. if (object->IsJSGlobalProxy()) { - __ CheckAccessGlobalProxy(receiver_reg, scratch, miss_label); + __ CheckAccessGlobalProxy(receiver_reg, scratch1, miss_label); + } + + // Check that we are allowed to write this. + if (!transition.is_null() && object->GetPrototype()->IsJSObject()) { + JSObject* holder; + if (lookup.IsFound()) { + holder = lookup.holder(); + } else { + // Find the top object. + holder = *object; + do { + holder = JSObject::cast(holder->GetPrototype()); + } while (holder->GetPrototype()->IsJSObject()); + } + // We need an extra register, push + __ push(name_reg); + Label miss_pop, done_check; + CheckPrototypes(object, receiver_reg, Handle<JSObject>(holder), name_reg, + scratch1, scratch2, name, &miss_pop); + __ jmp(&done_check); + __ bind(&miss_pop); + __ pop(name_reg); + __ jmp(miss_label); + __ bind(&done_check); + __ pop(name_reg); } // Stub never generated for non-global objects that require access @@ -754,11 +791,11 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, if (!transition.is_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. + __ pop(scratch1); // Return address. __ push(receiver_reg); __ Push(transition); __ push(rax); - __ push(scratch); + __ push(scratch1); __ TailCallExternalReference( ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage), masm->isolate()), @@ -768,9 +805,19 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, } if (!transition.is_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), transition); + // Update the map of the object. + __ Move(scratch1, transition); + __ movq(FieldOperand(receiver_reg, HeapObject::kMapOffset), scratch1); + + // Update the write barrier for the map field and pass the now unused + // name_reg as scratch register. + __ RecordWriteField(receiver_reg, + HeapObject::kMapOffset, + scratch1, + name_reg, + kDontSaveFPRegs, + OMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); } // Adjust for the number of properties stored in the object. Even in the @@ -787,19 +834,19 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, // Pass the value being stored in the now unused name_reg. __ movq(name_reg, rax); __ RecordWriteField( - receiver_reg, offset, name_reg, scratch, kDontSaveFPRegs); + receiver_reg, offset, name_reg, scratch1, kDontSaveFPRegs); } 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); + __ movq(scratch1, FieldOperand(receiver_reg, JSObject::kPropertiesOffset)); + __ movq(FieldOperand(scratch1, 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); __ RecordWriteField( - scratch, offset, name_reg, receiver_reg, kDontSaveFPRegs); + scratch1, offset, name_reg, receiver_reg, kDontSaveFPRegs); } // Return the value (register rax). @@ -982,6 +1029,49 @@ void StubCompiler::GenerateLoadField(Handle<JSObject> object, } +void StubCompiler::GenerateDictionaryLoadCallback(Register receiver, + Register name_reg, + Register scratch1, + Register scratch2, + Register scratch3, + Handle<AccessorInfo> callback, + Handle<String> name, + Label* miss) { + ASSERT(!receiver.is(scratch1)); + ASSERT(!receiver.is(scratch2)); + ASSERT(!receiver.is(scratch3)); + + // Load the properties dictionary. + Register dictionary = scratch1; + __ movq(dictionary, FieldOperand(receiver, JSObject::kPropertiesOffset)); + + // Probe the dictionary. + Label probe_done; + StringDictionaryLookupStub::GeneratePositiveLookup(masm(), + miss, + &probe_done, + dictionary, + name_reg, + scratch2, + scratch3); + __ bind(&probe_done); + + // If probing finds an entry in the dictionary, scratch3 contains the + // index into the dictionary. Check that the value is the callback. + Register index = scratch3; + const int kElementsStartOffset = + StringDictionary::kHeaderSize + + StringDictionary::kElementsStartIndex * kPointerSize; + const int kValueOffset = kElementsStartOffset + kPointerSize; + __ movq(scratch2, + Operand(dictionary, index, times_pointer_size, + kValueOffset - kHeapObjectTag)); + __ movq(scratch3, callback, RelocInfo::EMBEDDED_OBJECT); + __ cmpq(scratch2, scratch3); + __ j(not_equal, miss); +} + + void StubCompiler::GenerateLoadCallback(Handle<JSObject> object, Handle<JSObject> holder, Register receiver, @@ -989,6 +1079,7 @@ void StubCompiler::GenerateLoadCallback(Handle<JSObject> object, Register scratch1, Register scratch2, Register scratch3, + Register scratch4, Handle<AccessorInfo> callback, Handle<String> name, Label* miss) { @@ -999,6 +1090,11 @@ void StubCompiler::GenerateLoadCallback(Handle<JSObject> object, Register reg = CheckPrototypes(object, receiver, holder, scratch1, scratch2, scratch3, name, miss); + if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) { + GenerateDictionaryLoadCallback( + reg, name_reg, scratch2, scratch3, scratch4, callback, name, miss); + } + // Insert additional parameters into the stack frame above return address. ASSERT(!scratch2.is(reg)); __ pop(scratch2); // Get return address to place it below. @@ -1096,12 +1192,13 @@ void StubCompiler::GenerateLoadInterceptor(Handle<JSObject> object, // later. bool compile_followup_inline = false; if (lookup->IsFound() && lookup->IsCacheable()) { - if (lookup->type() == FIELD) { + if (lookup->IsField()) { compile_followup_inline = true; } else if (lookup->type() == CALLBACKS && lookup->GetCallbackObject()->IsAccessorInfo()) { - compile_followup_inline = - AccessorInfo::cast(lookup->GetCallbackObject())->getter() != NULL; + AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject()); + compile_followup_inline = callback->getter() != NULL && + callback->IsCompatibleReceiver(*object); } } @@ -1173,7 +1270,7 @@ void StubCompiler::GenerateLoadInterceptor(Handle<JSObject> object, miss); } - if (lookup->type() == FIELD) { + if (lookup->IsField()) { // We found FIELD property in prototype chain of interceptor's holder. // Retrieve a field from field's holder. GenerateFastPropertyLoad(masm(), rax, holder_reg, @@ -1343,7 +1440,7 @@ Handle<Code> CallStubCompiler::CompileCallField(Handle<JSObject> object, GenerateMissBranch(); // Return the generated code. - return GetCode(FIELD, name); + return GetCode(Code::FIELD, name); } @@ -1434,17 +1531,32 @@ Handle<Code> CallStubCompiler::CompileArrayPushCall( __ jmp(&fast_object); // In case of fast smi-only, convert to fast object, otherwise bail out. __ bind(¬_fast_object); - __ CheckFastSmiOnlyElements(rbx, &call_builtin); + __ CheckFastSmiElements(rbx, &call_builtin); // rdx: receiver // rbx: map - __ movq(r9, rdi); // Backup rdi as it is going to be trashed. - __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, + + Label try_holey_map; + __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS, rbx, rdi, + &try_holey_map); + + ElementsTransitionGenerator:: + GenerateMapChangeElementsTransition(masm()); + // Restore edi. + __ movq(rdi, FieldOperand(rdx, JSArray::kElementsOffset)); + __ jmp(&fast_object); + + __ bind(&try_holey_map); + __ LoadTransitionedArrayMapConditional(FAST_HOLEY_SMI_ELEMENTS, + FAST_HOLEY_ELEMENTS, + rbx, + rdi, &call_builtin); - ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm()); - __ movq(rdi, r9); + ElementsTransitionGenerator:: + GenerateMapChangeElementsTransition(masm()); + __ movq(rdi, FieldOperand(rdx, JSArray::kElementsOffset)); __ bind(&fast_object); } else { __ CheckFastObjectElements(rbx, &call_builtin); @@ -1852,7 +1964,7 @@ Handle<Code> CallStubCompiler::CompileStringFromCharCodeCall( GenerateMissBranch(); // Return the generated code. - return cell.is_null() ? GetCode(function) : GetCode(NORMAL, name); + return cell.is_null() ? GetCode(function) : GetCode(Code::NORMAL, name); } @@ -1967,7 +2079,7 @@ Handle<Code> CallStubCompiler::CompileMathAbsCall( GenerateMissBranch(); // Return the generated code. - return cell.is_null() ? GetCode(function) : GetCode(NORMAL, name); + return cell.is_null() ? GetCode(function) : GetCode(Code::NORMAL, name); } @@ -2216,7 +2328,7 @@ Handle<Code> CallStubCompiler::CompileCallInterceptor(Handle<JSObject> object, GenerateMissBranch(); // Return the generated code. - return GetCode(INTERCEPTOR, name); + return GetCode(Code::INTERCEPTOR, name); } @@ -2279,7 +2391,7 @@ Handle<Code> CallStubCompiler::CompileCallGlobal( GenerateMissBranch(); // Return the generated code. - return GetCode(NORMAL, name); + return GetCode(Code::NORMAL, name); } @@ -2296,7 +2408,13 @@ Handle<Code> StoreStubCompiler::CompileStoreField(Handle<JSObject> object, Label miss; // Generate store field code. Preserves receiver and name on jump to miss. - GenerateStoreField(masm(), object, index, transition, rdx, rcx, rbx, &miss); + GenerateStoreField(masm(), + object, + index, + transition, + name, + rdx, rcx, rbx, rdi, + &miss); // Handle store cache miss. __ bind(&miss); @@ -2304,14 +2422,17 @@ Handle<Code> StoreStubCompiler::CompileStoreField(Handle<JSObject> object, __ Jump(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(transition.is_null() ? FIELD : MAP_TRANSITION, name); + return GetCode(transition.is_null() + ? Code::FIELD + : Code::MAP_TRANSITION, name); } Handle<Code> StoreStubCompiler::CompileStoreCallback( - Handle<JSObject> object, - Handle<AccessorInfo> callback, - Handle<String> name) { + Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<AccessorInfo> callback) { // ----------- S t a t e ------------- // -- rax : value // -- rcx : name @@ -2319,19 +2440,12 @@ Handle<Code> StoreStubCompiler::CompileStoreCallback( // -- rsp[0] : return address // ----------------------------------- Label miss; + // Check that the maps haven't changed. + __ JumpIfSmi(rdx, &miss); + CheckPrototypes(receiver, rdx, holder, rbx, r8, rdi, name, &miss); - // Check that the map of the object hasn't changed. - __ CheckMap(rdx, Handle<Map>(object->map()), &miss, - DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS); - - // 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()); + // Stub never generated for non-global objects that require access checks. + ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded()); __ pop(rbx); // remove the return address __ push(rdx); // receiver @@ -2351,7 +2465,81 @@ Handle<Code> StoreStubCompiler::CompileStoreCallback( __ Jump(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(CALLBACKS, name); + return GetCode(Code::CALLBACKS, name); +} + + +#undef __ +#define __ ACCESS_MASM(masm) + + +void StoreStubCompiler::GenerateStoreViaSetter( + MacroAssembler* masm, + Handle<JSFunction> setter) { + // ----------- S t a t e ------------- + // -- rax : value + // -- rcx : name + // -- rdx : receiver + // -- rsp[0] : return address + // ----------------------------------- + { + FrameScope scope(masm, StackFrame::INTERNAL); + + // Save value register, so we can restore it later. + __ push(rax); + + if (!setter.is_null()) { + // Call the JavaScript setter with receiver and value on the stack. + __ push(rdx); + __ push(rax); + ParameterCount actual(1); + __ InvokeFunction(setter, actual, CALL_FUNCTION, NullCallWrapper(), + CALL_AS_METHOD); + } else { + // If we generate a global code snippet for deoptimization only, remember + // the place to continue after deoptimization. + masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset()); + } + + // We have to return the passed value, not the return value of the setter. + __ pop(rax); + + // Restore context register. + __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); + } + __ ret(0); +} + + +#undef __ +#define __ ACCESS_MASM(masm()) + + +Handle<Code> StoreStubCompiler::CompileStoreViaSetter( + Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<JSFunction> setter) { + // ----------- S t a t e ------------- + // -- rax : value + // -- rcx : name + // -- rdx : receiver + // -- rsp[0] : return address + // ----------------------------------- + Label miss; + + // Check that the maps haven't changed. + __ JumpIfSmi(rdx, &miss); + CheckPrototypes(receiver, rdx, holder, rbx, r8, rdi, name, &miss); + + GenerateStoreViaSetter(masm(), setter); + + __ bind(&miss); + Handle<Code> ic = isolate()->builtins()->StoreIC_Miss(); + __ Jump(ic, RelocInfo::CODE_TARGET); + + // Return the generated code. + return GetCode(Code::CALLBACKS, name); } @@ -2397,7 +2585,7 @@ Handle<Code> StoreStubCompiler::CompileStoreInterceptor( __ Jump(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(INTERCEPTOR, name); + return GetCode(Code::INTERCEPTOR, name); } @@ -2445,7 +2633,7 @@ Handle<Code> StoreStubCompiler::CompileStoreGlobal( __ Jump(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(NORMAL, name); + return GetCode(Code::NORMAL, name); } @@ -2469,7 +2657,13 @@ Handle<Code> KeyedStoreStubCompiler::CompileStoreField(Handle<JSObject> object, __ 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); + GenerateStoreField(masm(), + object, + index, + transition, + name, + rdx, rcx, rbx, rdi, + &miss); // Handle store cache miss. __ bind(&miss); @@ -2478,7 +2672,9 @@ Handle<Code> KeyedStoreStubCompiler::CompileStoreField(Handle<JSObject> object, __ Jump(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(transition.is_null() ? FIELD : MAP_TRANSITION, name); + return GetCode(transition.is_null() + ? Code::FIELD + : Code::MAP_TRANSITION, name); } @@ -2502,7 +2698,7 @@ Handle<Code> KeyedStoreStubCompiler::CompileStoreElement( __ jmp(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(NORMAL, factory()->empty_string()); + return GetCode(Code::NORMAL, factory()->empty_string()); } @@ -2540,7 +2736,7 @@ Handle<Code> KeyedStoreStubCompiler::CompileStorePolymorphic( __ jmp(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(NORMAL, factory()->empty_string(), MEGAMORPHIC); + return GetCode(Code::NORMAL, factory()->empty_string(), MEGAMORPHIC); } @@ -2578,7 +2774,7 @@ Handle<Code> LoadStubCompiler::CompileLoadNonexistent(Handle<String> name, GenerateLoadMiss(masm(), Code::LOAD_IC); // Return the generated code. - return GetCode(NONEXISTENT, factory()->empty_string()); + return GetCode(Code::NONEXISTENT, factory()->empty_string()); } @@ -2598,7 +2794,7 @@ Handle<Code> LoadStubCompiler::CompileLoadField(Handle<JSObject> object, GenerateLoadMiss(masm(), Code::LOAD_IC); // Return the generated code. - return GetCode(FIELD, name); + return GetCode(Code::FIELD, name); } @@ -2613,13 +2809,76 @@ Handle<Code> LoadStubCompiler::CompileLoadCallback( // -- rsp[0] : return address // ----------------------------------- Label miss; - GenerateLoadCallback(object, holder, rax, rcx, rdx, rbx, rdi, callback, + GenerateLoadCallback(object, holder, rax, rcx, rdx, rbx, rdi, r8, callback, name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); // Return the generated code. - return GetCode(CALLBACKS, name); + return GetCode(Code::CALLBACKS, name); +} + + +#undef __ +#define __ ACCESS_MASM(masm) + + +void LoadStubCompiler::GenerateLoadViaGetter(MacroAssembler* masm, + Handle<JSFunction> getter) { + // ----------- S t a t e ------------- + // -- rax : receiver + // -- rcx : name + // -- rsp[0] : return address + // ----------------------------------- + { + FrameScope scope(masm, StackFrame::INTERNAL); + + if (!getter.is_null()) { + // Call the JavaScript getter with the receiver on the stack. + __ push(rax); + ParameterCount actual(0); + __ InvokeFunction(getter, actual, CALL_FUNCTION, NullCallWrapper(), + CALL_AS_METHOD); + } else { + // If we generate a global code snippet for deoptimization only, remember + // the place to continue after deoptimization. + masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset()); + } + + // Restore context register. + __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); + } + __ ret(0); +} + + +#undef __ +#define __ ACCESS_MASM(masm()) + + +Handle<Code> LoadStubCompiler::CompileLoadViaGetter( + Handle<String> name, + Handle<JSObject> receiver, + Handle<JSObject> holder, + Handle<JSFunction> getter) { + // ----------- S t a t e ------------- + // -- rax : receiver + // -- rcx : name + // -- rsp[0] : return address + // ----------------------------------- + Label miss; + + // Check that the maps haven't changed. + __ JumpIfSmi(rax, &miss); + CheckPrototypes(receiver, rax, holder, rbx, rdx, rdi, name, &miss); + + GenerateLoadViaGetter(masm(), getter), + + __ bind(&miss); + GenerateLoadMiss(masm(), Code::LOAD_IC); + + // Return the generated code. + return GetCode(Code::CALLBACKS, name); } @@ -2639,7 +2898,7 @@ Handle<Code> LoadStubCompiler::CompileLoadConstant(Handle<JSObject> object, GenerateLoadMiss(masm(), Code::LOAD_IC); // Return the generated code. - return GetCode(CONSTANT_FUNCTION, name); + return GetCode(Code::CONSTANT_FUNCTION, name); } @@ -2663,7 +2922,7 @@ Handle<Code> LoadStubCompiler::CompileLoadInterceptor(Handle<JSObject> receiver, GenerateLoadMiss(masm(), Code::LOAD_IC); // Return the generated code. - return GetCode(INTERCEPTOR, name); + return GetCode(Code::INTERCEPTOR, name); } @@ -2707,7 +2966,7 @@ Handle<Code> LoadStubCompiler::CompileLoadGlobal( GenerateLoadMiss(masm(), Code::LOAD_IC); // Return the generated code. - return GetCode(NORMAL, name); + return GetCode(Code::NORMAL, name); } @@ -2736,7 +2995,7 @@ Handle<Code> KeyedLoadStubCompiler::CompileLoadField(Handle<String> name, GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); // Return the generated code. - return GetCode(FIELD, name); + return GetCode(Code::FIELD, name); } @@ -2758,14 +3017,14 @@ Handle<Code> KeyedLoadStubCompiler::CompileLoadCallback( __ Cmp(rax, name); __ j(not_equal, &miss); - GenerateLoadCallback(receiver, holder, rdx, rax, rbx, rcx, rdi, callback, + GenerateLoadCallback(receiver, holder, rdx, rax, rbx, rcx, rdi, r8, callback, name, &miss); __ bind(&miss); __ DecrementCounter(counters->keyed_load_callback(), 1); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); // Return the generated code. - return GetCode(CALLBACKS, name); + return GetCode(Code::CALLBACKS, name); } @@ -2795,7 +3054,7 @@ Handle<Code> KeyedLoadStubCompiler::CompileLoadConstant( GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); // Return the generated code. - return GetCode(CONSTANT_FUNCTION, name); + return GetCode(Code::CONSTANT_FUNCTION, name); } @@ -2825,7 +3084,7 @@ Handle<Code> KeyedLoadStubCompiler::CompileLoadInterceptor( GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); // Return the generated code. - return GetCode(INTERCEPTOR, name); + return GetCode(Code::INTERCEPTOR, name); } @@ -2851,7 +3110,7 @@ Handle<Code> KeyedLoadStubCompiler::CompileLoadArrayLength( GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); // Return the generated code. - return GetCode(CALLBACKS, name); + return GetCode(Code::CALLBACKS, name); } @@ -2877,7 +3136,7 @@ Handle<Code> KeyedLoadStubCompiler::CompileLoadStringLength( GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); // Return the generated code. - return GetCode(CALLBACKS, name); + return GetCode(Code::CALLBACKS, name); } @@ -2903,7 +3162,7 @@ Handle<Code> KeyedLoadStubCompiler::CompileLoadFunctionPrototype( GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); // Return the generated code. - return GetCode(CALLBACKS, name); + return GetCode(Code::CALLBACKS, name); } @@ -2923,7 +3182,7 @@ Handle<Code> KeyedLoadStubCompiler::CompileLoadElement( __ jmp(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(NORMAL, factory()->empty_string()); + return GetCode(Code::NORMAL, factory()->empty_string()); } @@ -2951,7 +3210,7 @@ Handle<Code> KeyedLoadStubCompiler::CompileLoadPolymorphic( GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); // Return the generated code. - return GetCode(NORMAL, factory()->empty_string(), MEGAMORPHIC); + return GetCode(Code::NORMAL, factory()->empty_string(), MEGAMORPHIC); } @@ -2981,6 +3240,7 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub( #endif // Load the initial map and verify that it is in fact a map. + // rdi: constructor __ movq(rbx, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset)); // Will both indicate a NULL and a Smi. STATIC_ASSERT(kSmiTag == 0); @@ -2990,18 +3250,22 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub( #ifdef DEBUG // Cannot construct functions this way. - // rdi: constructor // rbx: initial map __ CmpInstanceType(rbx, JS_FUNCTION_TYPE); - __ Assert(not_equal, "Function constructed by construct stub."); + __ Check(not_equal, "Function constructed by construct stub."); #endif // Now allocate the JSObject in new space. - // rdi: constructor // rbx: initial map + ASSERT(function->has_initial_map()); + int instance_size = function->initial_map()->instance_size(); +#ifdef DEBUG __ movzxbq(rcx, FieldOperand(rbx, Map::kInstanceSizeOffset)); __ shl(rcx, Immediate(kPointerSizeLog2)); - __ AllocateInNewSpace(rcx, rdx, rcx, no_reg, + __ cmpq(rcx, Immediate(instance_size)); + __ Check(equal, "Instance size of initial map changed."); +#endif + __ AllocateInNewSpace(instance_size, rdx, rcx, no_reg, &generic_stub_call, NO_ALLOCATION_FLAGS); // Allocated the JSObject, now initialize the fields and add the heap tag. @@ -3047,7 +3311,6 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub( } // Fill the unused in-object property fields with undefined. - ASSERT(function->has_initial_map()); for (int i = shared->this_property_assignments_count(); i < function->initial_map()->inobject_properties(); i++) { @@ -3369,8 +3632,11 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ movsd(Operand(rbx, rdi, times_8, 0), xmm0); break; case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3435,8 +3701,11 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: case FAST_ELEMENTS: - case FAST_SMI_ONLY_ELEMENTS: + case FAST_SMI_ELEMENTS: case FAST_DOUBLE_ELEMENTS: + case FAST_HOLEY_ELEMENTS: + case FAST_HOLEY_SMI_ELEMENTS: + case FAST_HOLEY_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); @@ -3587,7 +3856,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement( // Check that the key is a smi or a heap number convertible to a smi. GenerateSmiKeyCheck(masm, rcx, rbx, xmm0, xmm1, &miss_force_generic); - if (elements_kind == FAST_SMI_ONLY_ELEMENTS) { + if (IsFastSmiElementsKind(elements_kind)) { __ JumpIfNotSmi(rax, &transition_elements_kind); } @@ -3611,13 +3880,13 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement( __ j(not_equal, &miss_force_generic); __ bind(&finish_store); - if (elements_kind == FAST_SMI_ONLY_ELEMENTS) { + if (IsFastSmiElementsKind(elements_kind)) { __ SmiToInteger32(rcx, rcx); __ movq(FieldOperand(rdi, rcx, times_pointer_size, FixedArray::kHeaderSize), rax); } else { // Do the store and update the write barrier. - ASSERT(elements_kind == FAST_ELEMENTS); + ASSERT(IsFastObjectElementsKind(elements_kind)); __ SmiToInteger32(rcx, rcx); __ lea(rcx, FieldOperand(rdi, rcx, times_pointer_size, FixedArray::kHeaderSize)); |