From: mstarzinger@chromium.org Date: Tue, 28 Feb 2012 09:05:55 +0000 (+0000) Subject: Implement inlining of constructor calls. X-Git-Tag: upstream/4.7.83~17250 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=fb8eb04bfd36445663d4095bb5f65180954ebfce;p=platform%2Fupstream%2Fv8.git Implement inlining of constructor calls. R=vegorov@chromium.org,kmillikin@chromium.org Review URL: https://chromiumcodereview.appspot.com/9304001 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10849 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index 993addc..c99e778 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -978,6 +978,11 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm, NullCallWrapper(), CALL_AS_METHOD); } + // Store offset of return address for deoptimizer. + if (!is_api_function && !count_constructions) { + masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset()); + } + // Restore context from the frame. // r0: result // sp[0]: receiver @@ -1740,7 +1745,9 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ bind(&invoke); __ Call(r3); + // Store offset of return address for deoptimizer. masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset()); + // Exit frame and return. LeaveArgumentsAdaptorFrame(masm); __ Jump(lr); diff --git a/src/arm/deoptimizer-arm.cc b/src/arm/deoptimizer-arm.cc index 76d8954..d9a4d4b 100644 --- a/src/arm/deoptimizer-arm.cc +++ b/src/arm/deoptimizer-arm.cc @@ -351,7 +351,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, } unsigned fixed_frame_size = ArgumentsAdaptorFrameConstants::kFrameSize; - unsigned input_frame_size = input_->GetFrameSize(); unsigned output_frame_size = height_in_bytes + fixed_frame_size; // Allocate and store the output frame description. @@ -373,16 +372,13 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, // Compute the incoming parameter translation. int parameter_count = height; unsigned output_offset = output_frame_size; - unsigned input_offset = input_frame_size; for (int i = 0; i < parameter_count; ++i) { output_offset -= kPointerSize; DoTranslateCommand(iterator, frame_index, output_offset); } - input_offset -= (parameter_count * kPointerSize); // Read caller's PC from the previous frame. output_offset -= kPointerSize; - input_offset -= kPointerSize; intptr_t callers_pc = output_[frame_index - 1]->GetPc(); output_frame->SetFrameSlot(output_offset, callers_pc); if (FLAG_trace_deopt) { @@ -392,7 +388,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, // Read caller's FP from the previous frame, and set this frame's FP. output_offset -= kPointerSize; - input_offset -= kPointerSize; intptr_t value = output_[frame_index - 1]->GetFp(); output_frame->SetFrameSlot(output_offset, value); intptr_t fp_value = top_address + output_offset; @@ -404,7 +399,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, // A marker value is used in place of the context. output_offset -= kPointerSize; - input_offset -= kPointerSize; intptr_t context = reinterpret_cast( Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); output_frame->SetFrameSlot(output_offset, context); @@ -415,7 +409,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, // The function was mentioned explicitly in the ARGUMENTS_ADAPTOR_FRAME. output_offset -= kPointerSize; - input_offset -= kPointerSize; value = reinterpret_cast(function); output_frame->SetFrameSlot(output_offset, value); if (FLAG_trace_deopt) { @@ -425,7 +418,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, // Number of incoming arguments. output_offset -= kPointerSize; - input_offset -= kPointerSize; value = reinterpret_cast(Smi::FromInt(height - 1)); output_frame->SetFrameSlot(output_offset, value); if (FLAG_trace_deopt) { @@ -445,6 +437,119 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, } +void Deoptimizer::DoComputeConstructStubFrame(TranslationIterator* iterator, + int frame_index) { + JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next())); + unsigned height = iterator->Next(); + unsigned height_in_bytes = height * kPointerSize; + if (FLAG_trace_deopt) { + PrintF(" translating construct stub => height=%d\n", height_in_bytes); + } + + unsigned fixed_frame_size = 7 * 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, function); + output_frame->SetFrameType(StackFrame::CONSTRUCT); + + // Construct stub can not be topmost or bottommost. + 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. + uint32_t top_address; + top_address = output_[frame_index - 1]->GetTop() - output_frame_size; + output_frame->SetTop(top_address); + + // Compute the incoming parameter translation. + int parameter_count = height; + unsigned output_offset = output_frame_size; + for (int i = 0; i < parameter_count; ++i) { + output_offset -= kPointerSize; + DoTranslateCommand(iterator, frame_index, output_offset); + } + + // 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%08x: [top + %d] <- 0x%08x ; 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%08x: [top + %d] <- 0x%08x ; 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%08x: [top + %d] <- 0x%08x ; 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(Smi::FromInt(StackFrame::CONSTRUCT)); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; function (construct sentinel)\n", + top_address + output_offset, output_offset, value); + } + + // Number of incoming arguments. + output_offset -= kPointerSize; + value = reinterpret_cast(Smi::FromInt(height - 1)); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; argc (%d)\n", + top_address + output_offset, output_offset, value, height - 1); + } + + // Constructor function being invoked by the stub. + output_offset -= kPointerSize; + value = reinterpret_cast(function); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; constructor function\n", + top_address + output_offset, output_offset, value); + } + + // The newly allocated object was passed as receiver in the artificial + // constructor stub environment created by HEnvironment::CopyForInlining(). + output_offset -= kPointerSize; + value = output_frame->GetFrameSlot(output_frame_size - kPointerSize); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; allocated receiver\n", + top_address + output_offset, output_offset, value); + } + + ASSERT(0 == output_offset); + + Builtins* builtins = isolate_->builtins(); + Code* construct_stub = builtins->builtin(Builtins::kJSConstructStubGeneric); + uint32_t pc = reinterpret_cast( + construct_stub->instruction_start() + + isolate_->heap()->construct_stub_deopt_pc_offset()->value()); + output_frame->SetPc(pc); +} + + // This code is very similar to ia32 code, but relies on register names (fp, sp) // and how the frame is laid out. void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator, @@ -557,9 +662,8 @@ void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator, value = reinterpret_cast(function->context()); } output_frame->SetFrameSlot(output_offset, value); - if (is_topmost) { - output_frame->SetRegister(cp.code(), value); - } + output_frame->SetContext(value); + if (is_topmost) output_frame->SetRegister(cp.code(), value); if (FLAG_trace_deopt) { PrintF(" 0x%08x: [top + %d] <- 0x%08x ; context\n", top_address + output_offset, output_offset, value); diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 51e9208..418e0c5 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -2382,6 +2382,7 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) { CallConstructStub stub(flags); __ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL); + PrepareForBailoutForId(expr->ReturnId(), TOS_REG); context()->Plug(r0); } diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index a934aac..4e20404 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -995,10 +995,11 @@ LEnvironment* LChunkBuilder::CreateEnvironment( LEnvironment* outer = CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator); int ast_id = hydrogen_env->ast_id(); - ASSERT(ast_id != AstNode::kNoNumber || hydrogen_env->is_arguments_adaptor()); + ASSERT(ast_id != AstNode::kNoNumber || + hydrogen_env->frame_type() != JS_FUNCTION); int value_count = hydrogen_env->length(); LEnvironment* result = new LEnvironment(hydrogen_env->closure(), - hydrogen_env->is_arguments_adaptor(), + hydrogen_env->frame_type(), ast_id, hydrogen_env->parameter_count(), argument_count_, @@ -1020,7 +1021,7 @@ LEnvironment* LChunkBuilder::CreateEnvironment( result->AddValue(op, value->representation()); } - if (!hydrogen_env->is_arguments_adaptor()) { + if (hydrogen_env->frame_type() == JS_FUNCTION) { *argument_index_accumulator = argument_index; } @@ -2093,6 +2094,12 @@ LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) { } +LInstruction* LChunkBuilder::DoAllocateObject(HAllocateObject* instr) { + LAllocateObject* result = new LAllocateObject(); + return AssignPointerMap(DefineAsRegister(result)); +} + + LInstruction* LChunkBuilder::DoFastLiteral(HFastLiteral* instr) { return MarkAsCall(DefineFixed(new LFastLiteral, r0), instr); } @@ -2245,7 +2252,8 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) { instr->arguments_count(), instr->function(), undefined, - instr->call_kind()); + instr->call_kind(), + instr->is_construct()); current_block_->UpdateEnvironment(inner); chunk_->AddInlinedClosure(instr->closure()); return NULL; diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h index 1846922..81566c4 100644 --- a/src/arm/lithium-arm.h +++ b/src/arm/lithium-arm.h @@ -49,6 +49,7 @@ class LCodeGen; #define LITHIUM_CONCRETE_INSTRUCTION_LIST(V) \ V(AccessArgumentsAt) \ V(AddI) \ + V(AllocateObject) \ V(ApplyArguments) \ V(ArgumentsElements) \ V(ArgumentsLength) \ @@ -1922,6 +1923,13 @@ class LClampTToUint8: public LTemplateInstruction<1, 1, 1> { }; +class LAllocateObject: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(AllocateObject, "allocate-object") + DECLARE_HYDROGEN_ACCESSOR(AllocateObject) +}; + + class LFastLiteral: public LTemplateInstruction<1, 0, 0> { public: DECLARE_CONCRETE_INSTRUCTION(FastLiteral, "fast-literal") diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 8045556..630ae8d 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -479,10 +479,18 @@ void LCodeGen::WriteTranslation(LEnvironment* environment, WriteTranslation(environment->outer(), translation); int closure_id = DefineDeoptimizationLiteral(environment->closure()); - if (environment->is_arguments_adaptor()) { - translation->BeginArgumentsAdaptorFrame(closure_id, translation_size); - } else { - translation->BeginJSFrame(environment->ast_id(), closure_id, height); + switch (environment->frame_type()) { + case JS_FUNCTION: + translation->BeginJSFrame(environment->ast_id(), closure_id, height); + break; + case JS_CONSTRUCT: + translation->BeginConstructStubFrame(closure_id, translation_size); + break; + case ARGUMENTS_ADAPTOR: + translation->BeginArgumentsAdaptorFrame(closure_id, translation_size); + break; + default: + UNREACHABLE(); } for (int i = 0; i < translation_size; ++i) { LOperand* value = environment->values()->at(i); @@ -619,7 +627,7 @@ void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment, int jsframe_count = 0; for (LEnvironment* e = environment; e != NULL; e = e->outer()) { ++frame_count; - if (!e->is_arguments_adaptor()) { + if (e->frame_type() == JS_FUNCTION) { ++jsframe_count; } } @@ -4322,6 +4330,44 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) { } +void LCodeGen::DoAllocateObject(LAllocateObject* instr) { + class DeferredAllocateObject: public LDeferredCode { + public: + DeferredAllocateObject(LCodeGen* codegen, LAllocateObject* instr) + : LDeferredCode(codegen), instr_(instr) { } + virtual void Generate() { codegen()->DoDeferredAllocateObject(instr_); } + virtual LInstruction* instr() { return instr_; } + private: + LAllocateObject* instr_; + }; + + DeferredAllocateObject* deferred = new DeferredAllocateObject(this, instr); + + // TODO(mstarzinger): Implement inlined version instead of jumping to + // deferred runtime call. + __ jmp(deferred->entry()); + + __ bind(deferred->exit()); +} + + +void LCodeGen::DoDeferredAllocateObject(LAllocateObject* instr) { + Register result = ToRegister(instr->result()); + Handle constructor = instr->hydrogen()->constructor(); + + // TODO(3095996): Get rid of this. For now, we need to make the + // result register contain a valid pointer because it is already + // contained in the register pointer map. + __ mov(result, Operand(0)); + + PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); + __ LoadHeapObject(r0, constructor); + __ push(r0); + CallRuntimeFromDeferred(Runtime::kNewObject, 1, instr); + __ StoreToSafepointRegisterSlot(r0, result); +} + + void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) { Heap* heap = isolate()->heap(); ElementsKind boilerplate_elements_kind = diff --git a/src/arm/lithium-codegen-arm.h b/src/arm/lithium-codegen-arm.h index 00823e1..e23dc29 100644 --- a/src/arm/lithium-codegen-arm.h +++ b/src/arm/lithium-codegen-arm.h @@ -116,6 +116,7 @@ class LCodeGen BASE_EMBEDDED { void DoDeferredStackCheck(LStackCheck* instr); void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr); void DoDeferredStringCharFromCode(LStringCharFromCode* instr); + void DoDeferredAllocateObject(LAllocateObject* instr); void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, Label* map_check); diff --git a/src/ast.cc b/src/ast.cc index ca3ab78..61923bb 100644 --- a/src/ast.cc +++ b/src/ast.cc @@ -596,6 +596,14 @@ void Call::RecordTypeFeedback(TypeFeedbackOracle* oracle, } +void CallNew::RecordTypeFeedback(TypeFeedbackOracle* oracle) { + is_monomorphic_ = oracle->CallNewIsMonomorphic(this); + if (is_monomorphic_) { + target_ = oracle->GetCallNewTarget(this); + } +} + + void CompareOperation::RecordTypeFeedback(TypeFeedbackOracle* oracle) { TypeInfo info = oracle->CompareType(this); if (info.IsSmi()) { diff --git a/src/ast.h b/src/ast.h index 3acd121..74c4c61 100644 --- a/src/ast.h +++ b/src/ast.h @@ -1528,6 +1528,13 @@ class CallNew: public Expression { ZoneList* arguments() const { return arguments_; } virtual int position() const { return pos_; } + void RecordTypeFeedback(TypeFeedbackOracle* oracle); + virtual bool IsMonomorphic() { return is_monomorphic_; } + Handle target() { return target_; } + + // Bailout support. + int ReturnId() const { return return_id_; } + protected: template friend class AstNodeFactory; @@ -1538,12 +1545,19 @@ class CallNew: public Expression { : Expression(isolate), expression_(expression), arguments_(arguments), - pos_(pos) { } + pos_(pos), + is_monomorphic_(false), + return_id_(GetNextId(isolate)) { } private: Expression* expression_; ZoneList* arguments_; int pos_; + + bool is_monomorphic_; + Handle target_; + + int return_id_; }; diff --git a/src/deoptimizer.cc b/src/deoptimizer.cc index 55ecc71..d069a45 100644 --- a/src/deoptimizer.cc +++ b/src/deoptimizer.cc @@ -170,8 +170,16 @@ DeoptimizedFrameInfo* Deoptimizer::DebuggerInspectableFrame( deoptimizer->output_[frame_index - 1]->GetFrameType() == StackFrame::ARGUMENTS_ADAPTOR; - DeoptimizedFrameInfo* info = - new DeoptimizedFrameInfo(deoptimizer, frame_index, has_arguments_adaptor); + int construct_offset = has_arguments_adaptor ? 2 : 1; + bool has_construct_stub = + frame_index >= construct_offset && + deoptimizer->output_[frame_index - construct_offset]->GetFrameType() == + StackFrame::CONSTRUCT; + + DeoptimizedFrameInfo* info = new DeoptimizedFrameInfo(deoptimizer, + frame_index, + has_arguments_adaptor, + has_construct_stub); isolate->deoptimizer_data()->deoptimized_frame_info_ = info; // Get the "simulated" top and size for the requested frame. @@ -570,6 +578,9 @@ void Deoptimizer::DoComputeOutputFrames() { case Translation::ARGUMENTS_ADAPTOR_FRAME: DoComputeArgumentsAdaptorFrame(&iterator, i); break; + case Translation::CONSTRUCT_STUB_FRAME: + DoComputeConstructStubFrame(&iterator, i); + break; default: UNREACHABLE(); break; @@ -686,6 +697,7 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator, case Translation::BEGIN: case Translation::JS_FRAME: case Translation::ARGUMENTS_ADAPTOR_FRAME: + case Translation::CONSTRUCT_STUB_FRAME: case Translation::DUPLICATE: UNREACHABLE(); return; @@ -873,6 +885,7 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator, case Translation::BEGIN: case Translation::JS_FRAME: case Translation::ARGUMENTS_ADAPTOR_FRAME: + case Translation::CONSTRUCT_STUB_FRAME: case Translation::DUPLICATE: UNREACHABLE(); // Malformed input. return false; @@ -1206,7 +1219,8 @@ FrameDescription::FrameDescription(uint32_t frame_size, function_(function), top_(kZapUint32), pc_(kZapUint32), - fp_(kZapUint32) { + fp_(kZapUint32), + context_(kZapUint32) { // Zap all the registers. for (int r = 0; r < Register::kNumRegisters; r++) { SetRegister(r, kZapUint32); @@ -1320,6 +1334,13 @@ Handle TranslationBuffer::CreateByteArray() { } +void Translation::BeginConstructStubFrame(int literal_id, unsigned height) { + buffer_->Add(CONSTRUCT_STUB_FRAME); + buffer_->Add(literal_id); + buffer_->Add(height); +} + + void Translation::BeginArgumentsAdaptorFrame(int literal_id, unsigned height) { buffer_->Add(ARGUMENTS_ADAPTOR_FRAME); buffer_->Add(literal_id); @@ -1402,6 +1423,7 @@ int Translation::NumberOfOperandsFor(Opcode opcode) { return 1; case BEGIN: case ARGUMENTS_ADAPTOR_FRAME: + case CONSTRUCT_STUB_FRAME: return 2; case JS_FRAME: return 3; @@ -1421,6 +1443,8 @@ const char* Translation::StringFor(Opcode opcode) { return "JS_FRAME"; case ARGUMENTS_ADAPTOR_FRAME: return "ARGUMENTS_ADAPTOR_FRAME"; + case CONSTRUCT_STUB_FRAME: + return "CONSTRUCT_STUB_FRAME"; case REGISTER: return "REGISTER"; case INT32_REGISTER: @@ -1476,6 +1500,7 @@ SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator, case Translation::BEGIN: case Translation::JS_FRAME: case Translation::ARGUMENTS_ADAPTOR_FRAME: + case Translation::CONSTRUCT_STUB_FRAME: // Peeled off before getting here. break; @@ -1598,10 +1623,13 @@ Vector SlotRef::ComputeSlotMappingForArguments( #ifdef ENABLE_DEBUGGER_SUPPORT -DeoptimizedFrameInfo::DeoptimizedFrameInfo( - Deoptimizer* deoptimizer, int frame_index, bool has_arguments_adaptor) { +DeoptimizedFrameInfo::DeoptimizedFrameInfo(Deoptimizer* deoptimizer, + int frame_index, + bool has_arguments_adaptor, + bool has_construct_stub) { FrameDescription* output_frame = deoptimizer->output_[frame_index]; - SetFunction(output_frame->GetFunction()); + function_ = output_frame->GetFunction(); + has_construct_stub_ = has_construct_stub; expression_count_ = output_frame->GetExpressionCount(); expression_stack_ = new Object*[expression_count_]; // Get the source position using the unoptimized code. diff --git a/src/deoptimizer.h b/src/deoptimizer.h index 68bc48d..7699222 100644 --- a/src/deoptimizer.h +++ b/src/deoptimizer.h @@ -283,6 +283,8 @@ class Deoptimizer : public Malloced { void DoComputeJSFrame(TranslationIterator* iterator, int frame_index); void DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, int frame_index); + void DoComputeConstructStubFrame(TranslationIterator* iterator, + int frame_index); void DoTranslateCommand(TranslationIterator* iterator, int frame_index, unsigned output_offset); @@ -431,6 +433,9 @@ class FrameDescription { intptr_t GetFp() const { return fp_; } void SetFp(intptr_t fp) { fp_ = fp; } + intptr_t GetContext() const { return context_; } + void SetContext(intptr_t context) { context_ = context; } + Smi* GetState() const { return state_; } void SetState(Smi* state) { state_ = state; } @@ -492,6 +497,7 @@ class FrameDescription { intptr_t top_; intptr_t pc_; intptr_t fp_; + intptr_t context_; StackFrame::Type type_; Smi* state_; #ifdef DEBUG @@ -556,6 +562,7 @@ class Translation BASE_EMBEDDED { enum Opcode { BEGIN, JS_FRAME, + CONSTRUCT_STUB_FRAME, ARGUMENTS_ADAPTOR_FRAME, REGISTER, INT32_REGISTER, @@ -584,6 +591,7 @@ class Translation BASE_EMBEDDED { // Commands. void BeginJSFrame(int node_id, int literal_id, unsigned height); void BeginArgumentsAdaptorFrame(int literal_id, unsigned height); + void BeginConstructStubFrame(int literal_id, unsigned height); void StoreRegister(Register reg); void StoreInt32Register(Register reg); void StoreDoubleRegister(DoubleRegister reg); @@ -716,7 +724,8 @@ class DeoptimizedFrameInfo : public Malloced { public: DeoptimizedFrameInfo(Deoptimizer* deoptimizer, int frame_index, - bool has_arguments_adaptor); + bool has_arguments_adaptor, + bool has_construct_stub); virtual ~DeoptimizedFrameInfo(); // GC support. @@ -733,6 +742,12 @@ class DeoptimizedFrameInfo : public Malloced { return function_; } + // Check if this frame is preceded by construct stub frame. The bottom-most + // inlined frame might still be called by an uninlined construct stub. + bool HasConstructStub() { + return has_construct_stub_; + } + // Get an incoming argument. Object* GetParameter(int index) { ASSERT(0 <= index && index < parameters_count()); @@ -750,11 +765,6 @@ class DeoptimizedFrameInfo : public Malloced { } private: - // Set the frame function. - void SetFunction(JSFunction* function) { - function_ = function; - } - // Set an incoming argument. void SetParameter(int index, Object* obj) { ASSERT(0 <= index && index < parameters_count()); @@ -768,6 +778,7 @@ class DeoptimizedFrameInfo : public Malloced { } JSFunction* function_; + bool has_construct_stub_; int parameters_count_; int expression_count_; Object** parameters_; diff --git a/src/flag-definitions.h b/src/flag-definitions.h index b8593e7..71af9ae 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -167,6 +167,7 @@ DEFINE_bool(use_osr, true, "use on-stack replacement") DEFINE_bool(trace_osr, false, "trace on-stack replacement") DEFINE_int(stress_runs, 0, "number of stress runs") DEFINE_bool(optimize_closures, true, "optimize closures") +DEFINE_bool(inline_construct, false, "inline constructor calls") DEFINE_int(loop_weight, 1, "loop weight for representation inference") DEFINE_bool(optimize_for_in, true, diff --git a/src/frames-inl.h b/src/frames-inl.h index 010233a..27a526c 100644 --- a/src/frames-inl.h +++ b/src/frames-inl.h @@ -191,7 +191,7 @@ inline bool StandardFrame::IsArgumentsAdaptorFrame(Address fp) { inline bool StandardFrame::IsConstructFrame(Address fp) { Object* marker = Memory::Object_at(fp + StandardFrameConstants::kMarkerOffset); - return marker == Smi::FromInt(CONSTRUCT); + return marker == Smi::FromInt(StackFrame::CONSTRUCT); } diff --git a/src/frames.cc b/src/frames.cc index ecca7b6..be537c9 100644 --- a/src/frames.cc +++ b/src/frames.cc @@ -843,14 +843,11 @@ void OptimizedFrame::Summarize(List* frames) { // We create the summary in reverse order because the frames // in the deoptimization translation are ordered bottom-to-top. + bool is_constructor = IsConstructor(); int i = jsframe_count; while (i > 0) { opcode = static_cast(it.Next()); if (opcode == Translation::JS_FRAME) { - // We don't inline constructor calls, so only the first, outermost - // frame can be a constructor frame in case of inlining. - bool is_constructor = (i == jsframe_count) && IsConstructor(); - i--; int ast_id = it.Next(); int function_id = it.Next(); @@ -900,11 +897,18 @@ void OptimizedFrame::Summarize(List* frames) { FrameSummary summary(receiver, function, code, pc_offset, is_constructor); frames->Add(summary); + is_constructor = false; + } else if (opcode == Translation::CONSTRUCT_STUB_FRAME) { + // The next encountered JS_FRAME will be marked as a constructor call. + it.Skip(Translation::NumberOfOperandsFor(opcode)); + ASSERT(!is_constructor); + is_constructor = true; } else { // Skip over operands to advance to the next opcode. it.Skip(Translation::NumberOfOperandsFor(opcode)); } } + ASSERT(!is_constructor); } diff --git a/src/heap.h b/src/heap.h index 70c3146..d205b9c 100644 --- a/src/heap.h +++ b/src/heap.h @@ -150,7 +150,8 @@ namespace internal { V(Script, empty_script, EmptyScript) \ V(Smi, real_stack_limit, RealStackLimit) \ V(StringDictionary, intrinsic_function_names, IntrinsicFunctionNames) \ - V(Smi, arguments_adaptor_deopt_pc_offset, ArgumentsAdaptorDeoptPCOffset) + V(Smi, arguments_adaptor_deopt_pc_offset, ArgumentsAdaptorDeoptPCOffset) \ + V(Smi, construct_stub_deopt_pc_offset, ConstructStubDeoptPCOffset) #define ROOT_LIST(V) \ STRONG_ROOT_LIST(V) \ @@ -1567,6 +1568,11 @@ class Heap { set_arguments_adaptor_deopt_pc_offset(Smi::FromInt(pc_offset)); } + void SetConstructStubDeoptPCOffset(int pc_offset) { + ASSERT(construct_stub_deopt_pc_offset() == Smi::FromInt(0)); + set_construct_stub_deopt_pc_offset(Smi::FromInt(pc_offset)); + } + private: Heap(); diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc index 6cd9998..d09e3a3 100644 --- a/src/hydrogen-instructions.cc +++ b/src/hydrogen-instructions.cc @@ -1923,6 +1923,11 @@ HType HStringCharFromCode::CalculateInferredType() { } +HType HAllocateObject::CalculateInferredType() { + return HType::JSObject(); +} + + HType HFastLiteral::CalculateInferredType() { // TODO(mstarzinger): Be smarter, could also be JSArray here. return HType::JSObject(); diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index 92645a2..a0df009 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -62,6 +62,7 @@ class LChunkBuilder; V(AbnormalExit) \ V(AccessArgumentsAt) \ V(Add) \ + V(AllocateObject) \ V(ApplyArguments) \ V(ArgumentsElements) \ V(ArgumentsLength) \ @@ -1376,11 +1377,13 @@ class HEnterInlined: public HTemplateInstruction<0> { HEnterInlined(Handle closure, int arguments_count, FunctionLiteral* function, - CallKind call_kind) + CallKind call_kind, + bool is_construct) : closure_(closure), arguments_count_(arguments_count), function_(function), - call_kind_(call_kind) { + call_kind_(call_kind), + is_construct_(is_construct) { } virtual void PrintDataTo(StringStream* stream); @@ -1389,6 +1392,7 @@ class HEnterInlined: public HTemplateInstruction<0> { int arguments_count() const { return arguments_count_; } FunctionLiteral* function() const { return function_; } CallKind call_kind() const { return call_kind_; } + bool is_construct() const { return is_construct_; } virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); @@ -1401,6 +1405,7 @@ class HEnterInlined: public HTemplateInstruction<0> { int arguments_count_; FunctionLiteral* function_; CallKind call_kind_; + bool is_construct_; }; @@ -4358,6 +4363,29 @@ class HStringLength: public HUnaryOperation { }; +class HAllocateObject: public HTemplateInstruction<1> { + public: + HAllocateObject(HValue* context, Handle constructor) + : constructor_(constructor) { + SetOperandAt(0, context); + set_representation(Representation::Tagged()); + } + + HValue* context() { return OperandAt(0); } + Handle constructor() { return constructor_; } + + virtual Representation RequiredInputRepresentation(int index) { + return Representation::Tagged(); + } + virtual HType CalculateInferredType(); + + DECLARE_CONCRETE_INSTRUCTION(AllocateObject) + + private: + Handle constructor_; +}; + + template class HMaterializedLiteral: public HTemplateInstruction { public: diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 7267773..4307a0d 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -600,7 +600,7 @@ HConstant* HGraph::GetConstantHole() { HGraphBuilder::HGraphBuilder(CompilationInfo* info, TypeFeedbackOracle* oracle) : function_state_(NULL), - initial_function_state_(this, info, oracle, false), + initial_function_state_(this, info, oracle, NORMAL_RETURN), ast_context_(NULL), break_scope_(NULL), graph_(NULL), @@ -2156,12 +2156,12 @@ void HGraph::ComputeMinusZeroChecks() { FunctionState::FunctionState(HGraphBuilder* owner, CompilationInfo* info, TypeFeedbackOracle* oracle, - bool drop_extra) + ReturnHandlingFlag return_handling) : owner_(owner), compilation_info_(info), oracle_(oracle), call_context_(NULL), - drop_extra_(drop_extra), + return_handling_(return_handling), function_return_(NULL), test_context_(NULL), outer_(owner->function_state()) { @@ -2204,7 +2204,7 @@ AstContext::AstContext(HGraphBuilder* owner, Expression::Context kind) for_typeof_(false) { owner->set_ast_context(this); // Push. #ifdef DEBUG - ASSERT(!owner->environment()->is_arguments_adaptor()); + ASSERT(owner->environment()->frame_type() == JS_FUNCTION); original_length_ = owner->environment()->length(); #endif } @@ -2219,7 +2219,7 @@ EffectContext::~EffectContext() { ASSERT(owner()->HasStackOverflow() || owner()->current_block() == NULL || (owner()->environment()->length() == original_length_ && - !owner()->environment()->is_arguments_adaptor())); + owner()->environment()->frame_type() == JS_FUNCTION)); } @@ -2227,7 +2227,7 @@ ValueContext::~ValueContext() { ASSERT(owner()->HasStackOverflow() || owner()->current_block() == NULL || (owner()->environment()->length() == original_length_ + 1 && - !owner()->environment()->is_arguments_adaptor())); + owner()->environment()->frame_type() == JS_FUNCTION)); } @@ -2590,8 +2590,8 @@ void HGraphBuilder::PushAndAdd(HInstruction* instr) { } -template -HInstruction* HGraphBuilder::PreProcessCall(HCall* call) { +template +HInstruction* HGraphBuilder::PreProcessCall(Instruction* call) { int count = call->argument_count(); ZoneList arguments(count); for (int i = 0; i < count; ++i) { @@ -2819,7 +2819,38 @@ void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) { CHECK_ALIVE(VisitForValue(stmt->expression())); HValue* result = environment()->Pop(); current_block()->FinishExit(new(zone()) HReturn(result)); - set_current_block(NULL); + } else if (function_state()->is_construct()) { + // Return from an inlined construct call. In a test context the return + // value will always evaluate to true, in a value context the return value + // needs to be a JSObject. + if (context->IsTest()) { + TestContext* test = TestContext::cast(context); + CHECK_ALIVE(VisitForEffect(stmt->expression())); + current_block()->Goto(test->if_true(), function_state()->drop_extra()); + } else if (context->IsEffect()) { + CHECK_ALIVE(VisitForEffect(stmt->expression())); + current_block()->Goto(function_return(), function_state()->drop_extra()); + } else { + ASSERT(context->IsValue()); + CHECK_ALIVE(VisitForValue(stmt->expression())); + HValue* return_value = Pop(); + HValue* receiver = environment()->Lookup(0); + HHasInstanceTypeAndBranch* typecheck = + new(zone()) HHasInstanceTypeAndBranch(return_value, + FIRST_SPEC_OBJECT_TYPE, + LAST_SPEC_OBJECT_TYPE); + HBasicBlock* if_spec_object = graph()->CreateBasicBlock(); + HBasicBlock* not_spec_object = graph()->CreateBasicBlock(); + typecheck->SetSuccessorAt(0, if_spec_object); + typecheck->SetSuccessorAt(1, not_spec_object); + current_block()->Finish(typecheck); + if_spec_object->AddLeaveInlined(return_value, + function_return(), + function_state()->drop_extra()); + not_spec_object->AddLeaveInlined(receiver, + function_return(), + function_state()->drop_extra()); + } } else { // Return from an inlined function, visit the subexpression in the // expression context of the call. @@ -2834,13 +2865,13 @@ void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) { } else { ASSERT(context->IsValue()); CHECK_ALIVE(VisitForValue(stmt->expression())); - HValue* return_value = environment()->Pop(); + HValue* return_value = Pop(); current_block()->AddLeaveInlined(return_value, function_return(), function_state()->drop_extra()); } - set_current_block(NULL); } + set_current_block(NULL); } @@ -5001,7 +5032,7 @@ void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr, PrintF("Trying to inline the polymorphic call to %s\n", *name->ToCString()); } - if (FLAG_polymorphic_inlining && TryInline(expr)) { + if (FLAG_polymorphic_inlining && TryInlineCall(expr)) { // Trying to inline will signal that we should bailout from the // entire compilation by setting stack overflow on the visitor. if (HasStackOverflow()) return; @@ -5071,19 +5102,18 @@ void HGraphBuilder::TraceInline(Handle target, } -bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) { +bool HGraphBuilder::TryInline(CallKind call_kind, + Handle target, + ZoneList* arguments, + HValue* receiver, + int ast_id, + int return_id, + ReturnHandlingFlag return_handling) { if (!FLAG_use_inlining) return false; - // The function call we are inlining is a method call if the call - // is a property call. - CallKind call_kind = (expr->expression()->AsProperty() == NULL) - ? CALL_AS_FUNCTION - : CALL_AS_METHOD; - // Precondition: call is monomorphic and we have found a target with the // appropriate arity. Handle caller = info()->closure(); - Handle target = expr->target(); Handle target_shared(target->shared()); // Do a quick check on source code length to avoid parsing large @@ -5131,7 +5161,7 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) { TraceInline(target, caller, "inline depth limit reached"); return false; } - if (!env->outer()->is_arguments_adaptor()) { + if (env->outer()->frame_type() == JS_FUNCTION) { current_level++; } env = env->outer(); @@ -5239,16 +5269,17 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) { isolate()); // The function state is new-allocated because we need to delete it // in two different places. - FunctionState* target_state = - new FunctionState(this, &target_info, &target_oracle, drop_extra); + FunctionState* target_state = new FunctionState( + this, &target_info, &target_oracle, return_handling); HConstant* undefined = graph()->GetConstantUndefined(); HEnvironment* inner_env = environment()->CopyForInlining(target, - expr->arguments()->length(), + arguments->length(), function, undefined, - call_kind); + call_kind, + function_state()->is_construct()); #ifdef V8_TARGET_ARCH_IA32 // IA32 only, overwrite the caller's context in the deoptimization // environment with the correct one. @@ -5262,12 +5293,13 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) { #endif HBasicBlock* body_entry = CreateBasicBlock(inner_env); current_block()->Goto(body_entry); - body_entry->SetJoinId(expr->ReturnId()); + body_entry->SetJoinId(return_id); set_current_block(body_entry); AddInstruction(new(zone()) HEnterInlined(target, - expr->arguments()->length(), + arguments->length(), function, - call_kind)); + call_kind, + function_state()->is_construct())); VisitDeclarations(target_info.scope()->declarations()); VisitStatements(function->body()); if (HasStackOverflow()) { @@ -5286,32 +5318,27 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) { TraceInline(target, caller, NULL); if (current_block() != NULL) { - // Add a return of undefined if control can fall off the body. In a - // test context, undefined is false. - if (inlined_test_context() == NULL) { + // Add default return value (i.e. undefined for normals calls or the newly + // allocated receiver for construct calls) if control can fall off the + // body. In a test context, undefined is false and any JSObject is true. + if (call_context()->IsValue()) { ASSERT(function_return() != NULL); - ASSERT(call_context()->IsEffect() || call_context()->IsValue()); - if (call_context()->IsEffect()) { - current_block()->Goto(function_return(), drop_extra); - } else { - current_block()->AddLeaveInlined(undefined, - function_return(), - drop_extra); - } + HValue* return_value = function_state()->is_construct() + ? receiver + : undefined; + current_block()->AddLeaveInlined(return_value, + function_return(), + function_state()->drop_extra()); + } else if (call_context()->IsEffect()) { + ASSERT(function_return() != NULL); + current_block()->Goto(function_return(), function_state()->drop_extra()); } else { - // The graph builder assumes control can reach both branches of a - // test, so we materialize the undefined value and test it rather than - // simply jumping to the false target. - // - // TODO(3168478): refactor to avoid this. ASSERT(call_context()->IsTest()); - HBasicBlock* empty_true = graph()->CreateBasicBlock(); - HBasicBlock* empty_false = graph()->CreateBasicBlock(); - HBranch* test = new(zone()) HBranch(undefined, empty_true, empty_false); - current_block()->Finish(test); - - empty_true->Goto(inlined_test_context()->if_true(), drop_extra); - empty_false->Goto(inlined_test_context()->if_false(), drop_extra); + ASSERT(inlined_test_context() != NULL); + HBasicBlock* target = function_state()->is_construct() + ? inlined_test_context()->if_true() + : inlined_test_context()->if_false(); + current_block()->Goto(target, function_state()->drop_extra()); } } @@ -5327,12 +5354,12 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) { // Forward to the real test context. if (if_true->HasPredecessor()) { - if_true->SetJoinId(expr->id()); + if_true->SetJoinId(ast_id); HBasicBlock* true_target = TestContext::cast(ast_context())->if_true(); if_true->Goto(true_target, function_state()->drop_extra()); } if (if_false->HasPredecessor()) { - if_false->SetJoinId(expr->id()); + if_false->SetJoinId(ast_id); HBasicBlock* false_target = TestContext::cast(ast_context())->if_false(); if_false->Goto(false_target, function_state()->drop_extra()); } @@ -5340,7 +5367,7 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) { return true; } else if (function_return()->HasPredecessor()) { - function_return()->SetJoinId(expr->id()); + function_return()->SetJoinId(ast_id); set_current_block(function_return()); } else { set_current_block(NULL); @@ -5350,6 +5377,34 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) { } +bool HGraphBuilder::TryInlineCall(Call* expr, bool drop_extra) { + // The function call we are inlining is a method call if the call + // is a property call. + CallKind call_kind = (expr->expression()->AsProperty() == NULL) + ? CALL_AS_FUNCTION + : CALL_AS_METHOD; + + return TryInline(call_kind, + expr->target(), + expr->arguments(), + NULL, + expr->id(), + expr->ReturnId(), + drop_extra ? DROP_EXTRA_ON_RETURN : NORMAL_RETURN); +} + + +bool HGraphBuilder::TryInlineConstruct(CallNew* expr, HValue* receiver) { + return TryInline(CALL_AS_FUNCTION, + expr->target(), + expr->arguments(), + receiver, + expr->id(), + expr->ReturnId(), + CONSTRUCT_CALL_RETURN); +} + + bool HGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr, bool drop_extra) { if (!expr->target()->shared()->HasBuiltinFunctionId()) return false; BuiltinFunctionId id = expr->target()->shared()->builtin_function_id(); @@ -5679,7 +5734,7 @@ void HGraphBuilder::VisitCall(Call* expr) { } else { AddCheckConstantFunction(expr, receiver, receiver_map, true); - if (TryInline(expr)) return; + if (TryInlineCall(expr)) return; call = PreProcessCall( new(zone()) HCallConstantFunction(expr->target(), argument_count)); @@ -5743,7 +5798,7 @@ void HGraphBuilder::VisitCall(Call* expr) { } return; } - if (TryInline(expr)) return; + if (TryInlineCall(expr)) return; call = PreProcessCall(new(zone()) HCallKnownGlobal(expr->target(), argument_count)); } else { @@ -5779,7 +5834,7 @@ void HGraphBuilder::VisitCall(Call* expr) { return; } - if (TryInline(expr, true)) { // Drop function from environment. + if (TryInlineCall(expr, true)) { // Drop function from environment. return; } else { call = PreProcessCall(new(zone()) HInvokeFunction(context, @@ -5809,25 +5864,64 @@ void HGraphBuilder::VisitCall(Call* expr) { } +// Checks whether allocation using the given constructor can be inlined. +static bool IsAllocationInlineable(Handle constructor) { + return constructor->has_initial_map() && + constructor->initial_map()->instance_type() == JS_OBJECT_TYPE; +} + + void HGraphBuilder::VisitCallNew(CallNew* expr) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); - // The constructor function is also used as the receiver argument to the - // JS construct call builtin. - HValue* constructor = NULL; - CHECK_ALIVE(constructor = VisitArgument(expr->expression())); - CHECK_ALIVE(VisitArgumentList(expr->arguments())); - + expr->RecordTypeFeedback(oracle()); + int argument_count = expr->arguments()->length() + 1; // Plus constructor. HValue* context = environment()->LookupContext(); - // The constructor is both an operand to the instruction and an argument - // to the construct call. - int arg_count = expr->arguments()->length() + 1; // Plus constructor. - HCallNew* call = new(zone()) HCallNew(context, constructor, arg_count); - call->set_position(expr->position()); - Drop(arg_count); - return ast_context()->ReturnInstruction(call, expr->id()); + if (FLAG_inline_construct && + expr->IsMonomorphic() && + IsAllocationInlineable(expr->target())) { + // The constructor function is on the stack in the unoptimized code + // during evaluation of the arguments. + CHECK_ALIVE(VisitForValue(expr->expression())); + HValue* function = Top(); + CHECK_ALIVE(VisitExpressions(expr->arguments())); + Handle constructor = expr->target(); + AddInstruction(new(zone()) HCheckFunction(function, constructor)); + + // Replace the constructor function with a newly allocated receiver. + HInstruction* receiver = new(zone()) HAllocateObject(context, constructor); + // Index of the receiver from the top of the expression stack. + const int receiver_index = argument_count - 1; + AddInstruction(receiver); + ASSERT(environment()->ExpressionStackAt(receiver_index) == function); + environment()->SetExpressionStackAt(receiver_index, receiver); + + if (TryInlineConstruct(expr, receiver)) return; + + // TODO(mstarzinger): For now we remove the previous HAllocateObject and + // add HPushArgument for the arguments in case inlining failed. What we + // actually should do is emit HInvokeFunction on the constructor instead + // of using HCallNew as a fallback. + receiver->DeleteAndReplaceWith(NULL); + environment()->SetExpressionStackAt(receiver_index, function); + HInstruction* call = PreProcessCall( + new(zone()) HCallNew(context, function, argument_count)); + call->set_position(expr->position()); + return ast_context()->ReturnInstruction(call, expr->id()); + } else { + // The constructor function is both an operand to the instruction and an + // argument to the construct call. + HValue* constructor = NULL; + CHECK_ALIVE(constructor = VisitArgument(expr->expression())); + CHECK_ALIVE(VisitArgumentList(expr->arguments())); + HInstruction* call = + new(zone()) HCallNew(context, constructor, argument_count); + Drop(argument_count); + call->set_position(expr->position()); + return ast_context()->ReturnInstruction(call, expr->id()); + } } @@ -6952,10 +7046,11 @@ void HGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf( void HGraphBuilder::GenerateIsConstructCall(CallRuntime* call) { ASSERT(call->arguments()->length() == 0); if (function_state()->outer() != NULL) { - // We are generating graph for inlined function. Currently - // constructor inlining is not supported and we can just return - // false from %_IsConstructCall(). - return ast_context()->ReturnValue(graph()->GetConstantFalse()); + // We are generating graph for inlined function. + HValue* value = function_state()->is_construct() + ? graph()->GetConstantTrue() + : graph()->GetConstantFalse(); + return ast_context()->ReturnValue(value); } else { return ast_context()->ReturnControl(new(zone()) HIsConstructCallAndBranch, call->id()); @@ -7343,14 +7438,14 @@ HEnvironment::HEnvironment(HEnvironment* outer, : closure_(closure), values_(0), assigned_variables_(4), + frame_type_(JS_FUNCTION), parameter_count_(0), specials_count_(1), local_count_(0), outer_(outer), pop_count_(0), push_count_(0), - ast_id_(AstNode::kNoNumber), - arguments_adaptor_(false) { + ast_id_(AstNode::kNoNumber) { Initialize(scope->num_parameters() + 1, scope->num_stack_slots(), 0); } @@ -7358,31 +7453,32 @@ HEnvironment::HEnvironment(HEnvironment* outer, HEnvironment::HEnvironment(const HEnvironment* other) : values_(0), assigned_variables_(0), + frame_type_(JS_FUNCTION), parameter_count_(0), specials_count_(1), local_count_(0), outer_(NULL), pop_count_(0), push_count_(0), - ast_id_(other->ast_id()), - arguments_adaptor_(false) { + ast_id_(other->ast_id()) { Initialize(other); } HEnvironment::HEnvironment(HEnvironment* outer, Handle closure, + FrameType frame_type, int arguments) : closure_(closure), values_(arguments), assigned_variables_(0), + frame_type_(frame_type), parameter_count_(arguments), local_count_(0), outer_(outer), pop_count_(0), push_count_(0), - ast_id_(AstNode::kNoNumber), - arguments_adaptor_(true) { + ast_id_(AstNode::kNoNumber) { } @@ -7403,13 +7499,13 @@ void HEnvironment::Initialize(const HEnvironment* other) { closure_ = other->closure(); values_.AddAll(other->values_); assigned_variables_.AddAll(other->assigned_variables_); + frame_type_ = other->frame_type_; parameter_count_ = other->parameter_count_; local_count_ = other->local_count_; if (other->outer_ != NULL) outer_ = other->outer_->Copy(); // Deep copy. pop_count_ = other->pop_count_; push_count_ = other->push_count_; ast_id_ = other->ast_id_; - arguments_adaptor_ = other->arguments_adaptor_; } @@ -7510,13 +7606,28 @@ HEnvironment* HEnvironment::CopyAsLoopHeader(HBasicBlock* loop_header) const { } +HEnvironment* HEnvironment::CreateStubEnvironment(HEnvironment* outer, + Handle target, + FrameType frame_type, + int arguments) const { + HEnvironment* new_env = new(closure()->GetIsolate()->zone()) + HEnvironment(outer, target, frame_type, arguments + 1); + for (int i = 0; i <= arguments; ++i) { // Include receiver. + new_env->Push(ExpressionStackAt(arguments - i)); + } + new_env->ClearHistory(); + return new_env; +} + + HEnvironment* HEnvironment::CopyForInlining( Handle target, int arguments, FunctionLiteral* function, HConstant* undefined, - CallKind call_kind) const { - ASSERT(!is_arguments_adaptor()); + CallKind call_kind, + bool is_construct) const { + ASSERT(frame_type() == JS_FUNCTION); Zone* zone = closure()->GetIsolate()->zone(); @@ -7527,13 +7638,16 @@ HEnvironment* HEnvironment::CopyForInlining( outer->Drop(arguments + 1); // Including receiver. outer->ClearHistory(); + if (is_construct) { + // Create artificial constructor stub environment. The receiver should + // actually be the constructor function, but we pass the newly allocated + // object instead, DoComputeConstructStubFrame() relies on that. + outer = CreateStubEnvironment(outer, target, JS_CONSTRUCT, arguments); + } + if (arity != arguments) { // Create artificial arguments adaptation environment. - outer = new(zone) HEnvironment(outer, target, arguments + 1); - for (int i = 0; i <= arguments; ++i) { // Include receiver. - outer->Push(ExpressionStackAt(arguments - i)); - } - outer->ClearHistory(); + outer = CreateStubEnvironment(outer, target, ARGUMENTS_ADAPTOR, arguments); } HEnvironment* inner = diff --git a/src/hydrogen.h b/src/hydrogen.h index 65aa346..05eb121 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -361,19 +361,19 @@ class HGraph: public ZoneObject { Zone* HBasicBlock::zone() { return graph_->zone(); } +// Type of stack frame an environment might refer to. +enum FrameType { JS_FUNCTION, JS_CONSTRUCT, ARGUMENTS_ADAPTOR }; + + class HEnvironment: public ZoneObject { public: HEnvironment(HEnvironment* outer, Scope* scope, Handle closure); - bool is_arguments_adaptor() const { - return arguments_adaptor_; - } - HEnvironment* DiscardInlined(bool drop_extra) { - HEnvironment* outer = outer_->is_arguments_adaptor() ? - outer_->outer_ : outer_; + HEnvironment* outer = outer_; + while (outer->frame_type() != JS_FUNCTION) outer = outer->outer_; if (drop_extra) outer->Drop(1); return outer; } @@ -384,6 +384,7 @@ class HEnvironment: public ZoneObject { const ZoneList* assigned_variables() const { return &assigned_variables_; } + FrameType frame_type() const { return frame_type_; } int parameter_count() const { return parameter_count_; } int specials_count() const { return specials_count_; } int local_count() const { return local_count_; } @@ -469,7 +470,8 @@ class HEnvironment: public ZoneObject { int arguments, FunctionLiteral* function, HConstant* undefined, - CallKind call_kind) const; + CallKind call_kind, + bool is_construct) const; void AddIncomingEdge(HBasicBlock* block, HEnvironment* other); @@ -490,9 +492,17 @@ class HEnvironment: public ZoneObject { private: explicit HEnvironment(const HEnvironment* other); - // Create an argument adaptor environment. - HEnvironment(HEnvironment* outer, Handle closure, int arguments); + HEnvironment(HEnvironment* outer, + Handle closure, + FrameType frame_type, + int arguments); + // Create an artificial stub environment (e.g. for argument adaptor or + // constructor stub). + HEnvironment* CreateStubEnvironment(HEnvironment* outer, + Handle target, + FrameType frame_type, + int arguments) const; // True if index is included in the expression stack part of the environment. bool HasExpressionAt(int index) const; @@ -515,6 +525,7 @@ class HEnvironment: public ZoneObject { // Value array [parameters] [specials] [locals] [temporaries]. ZoneList values_; ZoneList assigned_variables_; + FrameType frame_type_; int parameter_count_; int specials_count_; int local_count_; @@ -522,7 +533,6 @@ class HEnvironment: public ZoneObject { int pop_count_; int push_count_; int ast_id_; - bool arguments_adaptor_; }; @@ -650,18 +660,27 @@ class TestContext: public AstContext { }; +enum ReturnHandlingFlag { + NORMAL_RETURN, + DROP_EXTRA_ON_RETURN, + CONSTRUCT_CALL_RETURN +}; + + class FunctionState { public: + FunctionState(HGraphBuilder* owner, CompilationInfo* info, TypeFeedbackOracle* oracle, - bool drop_extra); + ReturnHandlingFlag return_handling); ~FunctionState(); CompilationInfo* compilation_info() { return compilation_info_; } TypeFeedbackOracle* oracle() { return oracle_; } AstContext* call_context() { return call_context_; } - bool drop_extra() { return drop_extra_; } + bool drop_extra() { return return_handling_ == DROP_EXTRA_ON_RETURN; } + bool is_construct() { return return_handling_ == CONSTRUCT_CALL_RETURN; } HBasicBlock* function_return() { return function_return_; } TestContext* test_context() { return test_context_; } void ClearInlinedTestContext() { @@ -681,11 +700,13 @@ class FunctionState { // inlined. NULL when not inlining. AstContext* call_context_; - // Indicate if we have to drop an extra value from the environment on - // return from inlined functions. - bool drop_extra_; + // Indicate whether we have to perform special handling on return from + // inlined functions. + // - DROP_EXTRA_ON_RETURN: Drop an extra value from the environment. + // - CONSTRUCT_CALL_RETURN: Either use allocated receiver or return value. + ReturnHandlingFlag return_handling_; - // When inlining in an effect of value context, this is the return block. + // When inlining in an effect or value context, this is the return block. // It is NULL otherwise. When inlining in a test context, there are a // pair of return blocks in the context. When not inlining, there is no // local return point. @@ -825,7 +846,6 @@ class HGraphBuilder: public AstVisitor { CompilationInfo* info() const { return function_state()->compilation_info(); } - AstContext* call_context() const { return function_state()->call_context(); } @@ -922,7 +942,7 @@ class HGraphBuilder: public AstVisitor { // Remove the arguments from the bailout environment and emit instructions // to push them as outgoing parameters. - template HInstruction* PreProcessCall(HCall* call); + template HInstruction* PreProcessCall(Instruction* call); void TraceRepresentation(Token::Value op, TypeInfo info, @@ -954,11 +974,20 @@ class HGraphBuilder: public AstVisitor { // Try to optimize fun.apply(receiver, arguments) pattern. bool TryCallApply(Call* expr); - bool TryInline(Call* expr, bool drop_extra = false); + bool TryInline(CallKind call_kind, + Handle target, + ZoneList* arguments, + HValue* receiver, + int ast_id, + int return_id, + ReturnHandlingFlag return_handling); + + bool TryInlineCall(Call* expr, bool drop_extra = false); + bool TryInlineConstruct(CallNew* expr, HValue* receiver); bool TryInlineBuiltinMethodCall(Call* expr, - HValue* receiver, - Handle receiver_map, - CheckType check_type); + HValue* receiver, + Handle receiver_map, + CheckType check_type); bool TryInlineBuiltinFunctionCall(Call* expr, bool drop_extra); // If --trace-inlining, print a line of the inlining trace. Inlining diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index fdf21e5..a5d42cf 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -324,6 +324,11 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm, NullCallWrapper(), CALL_AS_METHOD); } + // Store offset of return address for deoptimizer. + if (!is_api_function && !count_constructions) { + masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset()); + } + // Restore context from the frame. __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); @@ -1639,7 +1644,9 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); __ call(edx); + // Store offset of return address for deoptimizer. masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset()); + // Leave frame and return. LeaveArgumentsAdaptorFrame(masm); __ ret(0); diff --git a/src/ia32/deoptimizer-ia32.cc b/src/ia32/deoptimizer-ia32.cc index 0c552d7..67291c3 100644 --- a/src/ia32/deoptimizer-ia32.cc +++ b/src/ia32/deoptimizer-ia32.cc @@ -467,7 +467,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, } unsigned fixed_frame_size = ArgumentsAdaptorFrameConstants::kFrameSize; - unsigned input_frame_size = input_->GetFrameSize(); unsigned output_frame_size = height_in_bytes + fixed_frame_size; // Allocate and store the output frame description. @@ -489,16 +488,13 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, // Compute the incoming parameter translation. int parameter_count = height; unsigned output_offset = output_frame_size; - unsigned input_offset = input_frame_size; for (int i = 0; i < parameter_count; ++i) { output_offset -= kPointerSize; DoTranslateCommand(iterator, frame_index, output_offset); } - input_offset -= (parameter_count * kPointerSize); // Read caller's PC from the previous frame. output_offset -= kPointerSize; - input_offset -= kPointerSize; intptr_t callers_pc = output_[frame_index - 1]->GetPc(); output_frame->SetFrameSlot(output_offset, callers_pc); if (FLAG_trace_deopt) { @@ -508,7 +504,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, // Read caller's FP from the previous frame, and set this frame's FP. output_offset -= kPointerSize; - input_offset -= kPointerSize; intptr_t value = output_[frame_index - 1]->GetFp(); output_frame->SetFrameSlot(output_offset, value); intptr_t fp_value = top_address + output_offset; @@ -520,7 +515,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, // A marker value is used in place of the context. output_offset -= kPointerSize; - input_offset -= kPointerSize; intptr_t context = reinterpret_cast( Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); output_frame->SetFrameSlot(output_offset, context); @@ -531,7 +525,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, // The function was mentioned explicitly in the ARGUMENTS_ADAPTOR_FRAME. output_offset -= kPointerSize; - input_offset -= kPointerSize; value = reinterpret_cast(function); output_frame->SetFrameSlot(output_offset, value); if (FLAG_trace_deopt) { @@ -541,7 +534,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, // Number of incoming arguments. output_offset -= kPointerSize; - input_offset -= kPointerSize; value = reinterpret_cast(Smi::FromInt(height - 1)); output_frame->SetFrameSlot(output_offset, value); if (FLAG_trace_deopt) { @@ -561,6 +553,110 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, } +void Deoptimizer::DoComputeConstructStubFrame(TranslationIterator* iterator, + int frame_index) { + JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next())); + unsigned height = iterator->Next(); + unsigned height_in_bytes = height * kPointerSize; + if (FLAG_trace_deopt) { + PrintF(" translating construct stub => height=%d\n", height_in_bytes); + } + + unsigned fixed_frame_size = 6 * 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, function); + output_frame->SetFrameType(StackFrame::CONSTRUCT); + + // Construct stub can not be topmost or bottommost. + 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. + uint32_t top_address; + top_address = output_[frame_index - 1]->GetTop() - output_frame_size; + output_frame->SetTop(top_address); + + // Compute the incoming parameter translation. + int parameter_count = height; + unsigned output_offset = output_frame_size; + for (int i = 0; i < parameter_count; ++i) { + output_offset -= kPointerSize; + DoTranslateCommand(iterator, frame_index, output_offset); + } + + // 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%08x: [top + %d] <- 0x%08x ; 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%08x: [top + %d] <- 0x%08x ; 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%08x: [top + %d] <- 0x%08x ; 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(Smi::FromInt(StackFrame::CONSTRUCT)); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; function (construct sentinel)\n", + top_address + output_offset, output_offset, value); + } + + // Number of incoming arguments. + output_offset -= kPointerSize; + value = reinterpret_cast(Smi::FromInt(height - 1)); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; argc (%d)\n", + top_address + output_offset, output_offset, value, height - 1); + } + + // The newly allocated object was passed as receiver in the artificial + // constructor stub environment created by HEnvironment::CopyForInlining(). + output_offset -= kPointerSize; + value = output_frame->GetFrameSlot(output_frame_size - kPointerSize); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08x: [top + %d] <- 0x%08x ; allocated receiver\n", + top_address + output_offset, output_offset, value); + } + + ASSERT(0 == output_offset); + + Builtins* builtins = isolate_->builtins(); + Code* construct_stub = builtins->builtin(Builtins::kJSConstructStubGeneric); + uint32_t pc = reinterpret_cast( + construct_stub->instruction_start() + + isolate_->heap()->construct_stub_deopt_pc_offset()->value()); + output_frame->SetPc(pc); +} + + void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator, int frame_index) { int node_id = iterator->Next(); @@ -672,6 +768,7 @@ void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator, value = reinterpret_cast(function->context()); } output_frame->SetFrameSlot(output_offset, value); + output_frame->SetContext(value); if (is_topmost) output_frame->SetRegister(esi.code(), value); if (FLAG_trace_deopt) { PrintF(" 0x%08x: [top + %d] <- 0x%08x ; context\n", diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index ee14625..0b7c7fd 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -2400,6 +2400,7 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) { CallConstructStub stub(flags); __ call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL); + PrepareForBailoutForId(expr->ReturnId(), TOS_REG); context()->Plug(eax); } diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index fec3308..d8c3972 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -394,10 +394,18 @@ void LCodeGen::WriteTranslation(LEnvironment* environment, WriteTranslation(environment->outer(), translation); int closure_id = DefineDeoptimizationLiteral(environment->closure()); - if (environment->is_arguments_adaptor()) { - translation->BeginArgumentsAdaptorFrame(closure_id, translation_size); - } else { - translation->BeginJSFrame(environment->ast_id(), closure_id, height); + switch (environment->frame_type()) { + case JS_FUNCTION: + translation->BeginJSFrame(environment->ast_id(), closure_id, height); + break; + case JS_CONSTRUCT: + translation->BeginConstructStubFrame(closure_id, translation_size); + break; + case ARGUMENTS_ADAPTOR: + translation->BeginArgumentsAdaptorFrame(closure_id, translation_size); + break; + default: + UNREACHABLE(); } for (int i = 0; i < translation_size; ++i) { LOperand* value = environment->values()->at(i); @@ -550,7 +558,7 @@ void LCodeGen::RegisterEnvironmentForDeoptimization( int jsframe_count = 0; for (LEnvironment* e = environment; e != NULL; e = e->outer()) { ++frame_count; - if (!e->is_arguments_adaptor()) { + if (e->frame_type() == JS_FUNCTION) { ++jsframe_count; } } @@ -4194,6 +4202,43 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) { } +void LCodeGen::DoAllocateObject(LAllocateObject* instr) { + class DeferredAllocateObject: public LDeferredCode { + public: + DeferredAllocateObject(LCodeGen* codegen, LAllocateObject* instr) + : LDeferredCode(codegen), instr_(instr) { } + virtual void Generate() { codegen()->DoDeferredAllocateObject(instr_); } + virtual LInstruction* instr() { return instr_; } + private: + LAllocateObject* instr_; + }; + + DeferredAllocateObject* deferred = new DeferredAllocateObject(this, instr); + + // TODO(mstarzinger): Implement inlined version instead of jumping to + // deferred runtime call. + __ jmp(deferred->entry()); + + __ bind(deferred->exit()); +} + + +void LCodeGen::DoDeferredAllocateObject(LAllocateObject* instr) { + Register result = ToRegister(instr->result()); + Handle constructor = instr->hydrogen()->constructor(); + + // TODO(3095996): Get rid of this. For now, we need to make the + // result register contain a valid pointer because it is already + // contained in the register pointer map. + __ Set(result, Immediate(0)); + + PushSafepointRegistersScope scope(this); + __ PushHeapObject(constructor); + CallRuntimeFromDeferred(Runtime::kNewObject, 1, instr, instr->context()); + __ StoreToSafepointRegisterSlot(result, eax); +} + + void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) { ASSERT(ToRegister(instr->context()).is(esi)); Heap* heap = isolate()->heap(); diff --git a/src/ia32/lithium-codegen-ia32.h b/src/ia32/lithium-codegen-ia32.h index d86d48c..baf2036 100644 --- a/src/ia32/lithium-codegen-ia32.h +++ b/src/ia32/lithium-codegen-ia32.h @@ -107,6 +107,7 @@ class LCodeGen BASE_EMBEDDED { void DoDeferredStackCheck(LStackCheck* instr); void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr); void DoDeferredStringCharFromCode(LStringCharFromCode* instr); + void DoDeferredAllocateObject(LAllocateObject* instr); void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, Label* map_check); diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index 32704b0..16dc261 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -1005,11 +1005,12 @@ LEnvironment* LChunkBuilder::CreateEnvironment( LEnvironment* outer = CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator); int ast_id = hydrogen_env->ast_id(); - ASSERT(ast_id != AstNode::kNoNumber || hydrogen_env->is_arguments_adaptor()); + ASSERT(ast_id != AstNode::kNoNumber || + hydrogen_env->frame_type() != JS_FUNCTION); int value_count = hydrogen_env->length(); LEnvironment* result = new(zone()) LEnvironment(hydrogen_env->closure(), - hydrogen_env->is_arguments_adaptor(), + hydrogen_env->frame_type(), ast_id, hydrogen_env->parameter_count(), argument_count_, @@ -1031,7 +1032,7 @@ LEnvironment* LChunkBuilder::CreateEnvironment( result->AddValue(op, value->representation()); } - if (!hydrogen_env->is_arguments_adaptor()) { + if (hydrogen_env->frame_type() == JS_FUNCTION) { *argument_index_accumulator = argument_index; } @@ -2213,6 +2214,13 @@ LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) { } +LInstruction* LChunkBuilder::DoAllocateObject(HAllocateObject* instr) { + LOperand* context = UseFixed(instr->context(), esi); + LAllocateObject* result = new(zone()) LAllocateObject(context); + return AssignPointerMap(DefineAsRegister(result)); +} + + LInstruction* LChunkBuilder::DoFastLiteral(HFastLiteral* instr) { LOperand* context = UseFixed(instr->context(), esi); return MarkAsCall( @@ -2385,7 +2393,8 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) { instr->arguments_count(), instr->function(), undefined, - instr->call_kind()); + instr->call_kind(), + instr->is_construct()); current_block_->UpdateEnvironment(inner); chunk_->AddInlinedClosure(instr->closure()); return NULL; diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h index b879fc1..8450974 100644 --- a/src/ia32/lithium-ia32.h +++ b/src/ia32/lithium-ia32.h @@ -43,6 +43,7 @@ class LCodeGen; #define LITHIUM_CONCRETE_INSTRUCTION_LIST(V) \ V(AccessArgumentsAt) \ V(AddI) \ + V(AllocateObject) \ V(ApplyArguments) \ V(ArgumentsElements) \ V(ArgumentsLength) \ @@ -1995,6 +1996,19 @@ class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> { }; +class LAllocateObject: public LTemplateInstruction<1, 1, 0> { + public: + LAllocateObject(LOperand* context) { + inputs_[0] = context; + } + + DECLARE_CONCRETE_INSTRUCTION(AllocateObject, "allocate-object") + DECLARE_HYDROGEN_ACCESSOR(AllocateObject) + + LOperand* context() { return inputs_[0]; } +}; + + class LFastLiteral: public LTemplateInstruction<1, 1, 0> { public: explicit LFastLiteral(LOperand* context) { diff --git a/src/lithium.h b/src/lithium.h index 474e555..ec72695 100644 --- a/src/lithium.h +++ b/src/lithium.h @@ -438,14 +438,14 @@ class LPointerMap: public ZoneObject { class LEnvironment: public ZoneObject { public: LEnvironment(Handle closure, - bool is_arguments_adaptor, + FrameType frame_type, int ast_id, int parameter_count, int argument_count, int value_count, LEnvironment* outer) : closure_(closure), - is_arguments_adaptor_(is_arguments_adaptor), + frame_type_(frame_type), arguments_stack_height_(argument_count), deoptimization_index_(Safepoint::kNoDeoptimizationIndex), translation_index_(-1), @@ -459,6 +459,7 @@ class LEnvironment: public ZoneObject { outer_(outer) { } Handle closure() const { return closure_; } + FrameType frame_type() const { return frame_type_; } int arguments_stack_height() const { return arguments_stack_height_; } int deoptimization_index() const { return deoptimization_index_; } int translation_index() const { return translation_index_; } @@ -503,11 +504,9 @@ class LEnvironment: public ZoneObject { void PrintTo(StringStream* stream); - bool is_arguments_adaptor() const { return is_arguments_adaptor_; } - private: Handle closure_; - bool is_arguments_adaptor_; + FrameType frame_type_; int arguments_stack_height_; int deoptimization_index_; int translation_index_; diff --git a/src/mips/full-codegen-mips.cc b/src/mips/full-codegen-mips.cc index 312f49b..d6e7a44 100644 --- a/src/mips/full-codegen-mips.cc +++ b/src/mips/full-codegen-mips.cc @@ -2444,6 +2444,7 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) { CallConstructStub stub(flags); __ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL); + PrepareForBailoutForId(expr->ReturnId(), TOS_REG); context()->Plug(v0); } diff --git a/src/mips/lithium-codegen-mips.cc b/src/mips/lithium-codegen-mips.cc index 252e8f4..f600904 100644 --- a/src/mips/lithium-codegen-mips.cc +++ b/src/mips/lithium-codegen-mips.cc @@ -4222,6 +4222,44 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) { } +void LCodeGen::DoAllocateObject(LAllocateObject* instr) { + class DeferredAllocateObject: public LDeferredCode { + public: + DeferredAllocateObject(LCodeGen* codegen, LAllocateObject* instr) + : LDeferredCode(codegen), instr_(instr) { } + virtual void Generate() { codegen()->DoDeferredAllocateObject(instr_); } + virtual LInstruction* instr() { return instr_; } + private: + LAllocateObject* instr_; + }; + + DeferredAllocateObject* deferred = new DeferredAllocateObject(this, instr); + + // TODO(mstarzinger): Implement inlined version instead of jumping to + // deferred runtime call. + __ jmp(deferred->entry()); + + __ bind(deferred->exit()); +} + + +void LCodeGen::DoDeferredAllocateObject(LAllocateObject* instr) { + Register result = ToRegister(instr->result()); + Handle constructor = instr->hydrogen()->constructor(); + + // TODO(3095996): Get rid of this. For now, we need to make the + // result register contain a valid pointer because it is already + // contained in the register pointer map. + __ mov(result, zero_reg); + + PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); + __ LoadHeapObject(a0, constructor); + __ push(a0); + CallRuntimeFromDeferred(Runtime::kNewObject, 1, instr); + __ StoreToSafepointRegisterSlot(v0, result); +} + + void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) { Heap* heap = isolate()->heap(); ElementsKind boilerplate_elements_kind = diff --git a/src/mips/lithium-codegen-mips.h b/src/mips/lithium-codegen-mips.h index 513992c..9e5b983 100644 --- a/src/mips/lithium-codegen-mips.h +++ b/src/mips/lithium-codegen-mips.h @@ -112,6 +112,7 @@ class LCodeGen BASE_EMBEDDED { void DoDeferredStackCheck(LStackCheck* instr); void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr); void DoDeferredStringCharFromCode(LStringCharFromCode* instr); + void DoDeferredAllocateObject(LAllocateObject* instr); void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, Label* map_check); diff --git a/src/mips/lithium-mips.cc b/src/mips/lithium-mips.cc index c534abc..0d78845 100644 --- a/src/mips/lithium-mips.cc +++ b/src/mips/lithium-mips.cc @@ -2098,6 +2098,12 @@ LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) { } +LInstruction* LChunkBuilder::DoAllocateObject(HAllocateObject* instr) { + LAllocateObject* result = new LAllocateObject(); + return AssignPointerMap(DefineAsRegister(result)); +} + + LInstruction* LChunkBuilder::DoFastLiteral(HFastLiteral* instr) { return MarkAsCall(DefineFixed(new LFastLiteral, v0), instr); } diff --git a/src/mips/lithium-mips.h b/src/mips/lithium-mips.h index f4c3c21..f41689e 100644 --- a/src/mips/lithium-mips.h +++ b/src/mips/lithium-mips.h @@ -49,6 +49,7 @@ class LCodeGen; #define LITHIUM_CONCRETE_INSTRUCTION_LIST(V) \ V(AccessArgumentsAt) \ V(AddI) \ + V(AllocateObject) \ V(ApplyArguments) \ V(ArgumentsElements) \ V(ArgumentsLength) \ @@ -1917,6 +1918,13 @@ class LClampTToUint8: public LTemplateInstruction<1, 1, 1> { }; +class LAllocateObject: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(AllocateObject, "allocate-object") + DECLARE_HYDROGEN_ACCESSOR(AllocateObject) +}; + + class LFastLiteral: public LTemplateInstruction<1, 0, 0> { public: DECLARE_CONCRETE_INSTRUCTION(FastLiteral, "fast-literal") diff --git a/src/objects.cc b/src/objects.cc index 8941151..538986d 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -8257,9 +8257,15 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) { break; } - case Translation::ARGUMENTS_ADAPTOR_FRAME: { + case Translation::ARGUMENTS_ADAPTOR_FRAME: + case Translation::CONSTRUCT_STUB_FRAME: { + int function_id = iterator.Next(); + JSFunction* function = + JSFunction::cast(LiteralArray()->get(function_id)); unsigned height = iterator.Next(); - PrintF(out, "{arguments adaptor, height=%d}", height); + PrintF(out, "{function="); + function->PrintName(out); + PrintF(out, ", height=%u}", height); break; } diff --git a/src/runtime.cc b/src/runtime.cc index b377e6e..fdbe1f5 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -882,14 +882,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_IsInPrototypeChain) { } -RUNTIME_FUNCTION(MaybeObject*, Runtime_IsConstructCall) { - NoHandleAllocation ha; - ASSERT(args.length() == 0); - JavaScriptFrameIterator it(isolate); - return isolate->heap()->ToBoolean(it.frame()->IsConstructor()); -} - - // Recursively traverses hidden prototypes if property is not found static void GetOwnPropertyImplementation(JSObject* obj, String* name, @@ -10640,6 +10632,7 @@ class FrameInspector { frame, inlined_jsframe_index, isolate); } has_adapted_arguments_ = frame_->has_adapted_arguments(); + is_bottommost_ = inlined_jsframe_index == 0; is_optimized_ = frame_->is_optimized(); } @@ -10677,6 +10670,11 @@ class FrameInspector { ? deoptimized_frame_->GetSourcePosition() : frame_->LookupCode()->SourcePosition(frame_->pc()); } + bool IsConstructor() { + return is_optimized_ && !is_bottommost_ + ? deoptimized_frame_->HasConstructStub() + : frame_->IsConstructor(); + } // To inspect all the provided arguments the frame might need to be // replaced with the arguments frame. @@ -10692,6 +10690,7 @@ class FrameInspector { DeoptimizedFrameInfo* deoptimized_frame_; Isolate* isolate_; bool is_optimized_; + bool is_bottommost_; bool has_adapted_arguments_; DISALLOW_COPY_AND_ASSIGN(FrameInspector); @@ -10721,6 +10720,16 @@ static SaveContext* FindSavedContextForFrame(Isolate* isolate, } +RUNTIME_FUNCTION(MaybeObject*, Runtime_IsConstructCall) { + NoHandleAllocation ha; + ASSERT(args.length() == 0); + JavaScriptFrameIterator it(isolate); + JavaScriptFrame* frame = it.frame(); + FrameInspector frame_inspector(frame, frame->GetInlineCount() - 1, isolate); + return isolate->heap()->ToBoolean(frame_inspector.IsConstructor()); +} + + // Return an array with frame details // args[0]: number: break id // args[1]: number: frame index @@ -10785,9 +10794,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) { // Find source position in unoptimized code. int position = frame_inspector.GetSourcePosition(); - // Check for constructor frame. Inlined frames cannot be construct calls. - bool inlined_frame = is_optimized && inlined_jsframe_index != 0; - bool constructor = !inlined_frame && it.frame()->IsConstructor(); + // Check for constructor frame. + bool constructor = frame_inspector.IsConstructor(); // Get scope info and read from it for local variable information. Handle function(JSFunction::cast(frame_inspector.GetFunction())); diff --git a/src/type-info.cc b/src/type-info.cc index 2fb4e2a..81cbff3 100644 --- a/src/type-info.cc +++ b/src/type-info.cc @@ -256,6 +256,11 @@ Handle TypeFeedbackOracle::GetCallTarget(Call* expr) { } +Handle TypeFeedbackOracle::GetCallNewTarget(CallNew* expr) { + return Handle::cast(GetInfo(expr->id())); +} + + bool TypeFeedbackOracle::LoadIsBuiltin(Property* expr, Builtins::Name id) { return *GetInfo(expr->id()) == isolate_->builtins()->builtin(id); diff --git a/src/type-info.h b/src/type-info.h index 9b8b431..19a309b 100644 --- a/src/type-info.h +++ b/src/type-info.h @@ -267,6 +267,7 @@ class TypeFeedbackOracle BASE_EMBEDDED { Handle GetPrototypeForPrimitiveCheck(CheckType check); Handle GetCallTarget(Call* expr); + Handle GetCallNewTarget(CallNew* expr); bool LoadIsBuiltin(Property* expr, Builtins::Name id); diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index 2ea68b3..abf9b56 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -329,6 +329,14 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm, NullCallWrapper(), CALL_AS_METHOD); } + // Store offset of return address for deoptimizer. + // TODO(849): Once Generate_StringConstructCode doesn't reuse this + // generator, we can drop the third condition below! + if (!is_api_function && !count_constructions && + masm->isolate()->heap()->construct_stub_deopt_pc_offset() == 0) { + masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset()); + } + // Restore context from the frame. __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); @@ -1538,7 +1546,9 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ bind(&invoke); __ call(rdx); + // Store offset of return address for deoptimizer. masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset()); + // Leave frame and return. LeaveArgumentsAdaptorFrame(masm); __ ret(0); diff --git a/src/x64/deoptimizer-x64.cc b/src/x64/deoptimizer-x64.cc index efa9888..2adf587 100644 --- a/src/x64/deoptimizer-x64.cc +++ b/src/x64/deoptimizer-x64.cc @@ -347,7 +347,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, } unsigned fixed_frame_size = ArgumentsAdaptorFrameConstants::kFrameSize; - unsigned input_frame_size = input_->GetFrameSize(); unsigned output_frame_size = height_in_bytes + fixed_frame_size; // Allocate and store the output frame description. @@ -369,16 +368,13 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, // Compute the incoming parameter translation. int parameter_count = height; unsigned output_offset = output_frame_size; - unsigned input_offset = input_frame_size; for (int i = 0; i < parameter_count; ++i) { output_offset -= kPointerSize; DoTranslateCommand(iterator, frame_index, output_offset); } - input_offset -= (parameter_count * kPointerSize); // Read caller's PC from the previous frame. output_offset -= kPointerSize; - input_offset -= kPointerSize; intptr_t callers_pc = output_[frame_index - 1]->GetPc(); output_frame->SetFrameSlot(output_offset, callers_pc); if (FLAG_trace_deopt) { @@ -389,7 +385,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, // Read caller's FP from the previous frame, and set this frame's FP. output_offset -= kPointerSize; - input_offset -= kPointerSize; intptr_t value = output_[frame_index - 1]->GetFp(); output_frame->SetFrameSlot(output_offset, value); intptr_t fp_value = top_address + output_offset; @@ -402,7 +397,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, // A marker value is used in place of the context. output_offset -= kPointerSize; - input_offset -= kPointerSize; intptr_t context = reinterpret_cast( Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); output_frame->SetFrameSlot(output_offset, context); @@ -414,7 +408,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, // The function was mentioned explicitly in the ARGUMENTS_ADAPTOR_FRAME. output_offset -= kPointerSize; - input_offset -= kPointerSize; value = reinterpret_cast(function); output_frame->SetFrameSlot(output_offset, value); if (FLAG_trace_deopt) { @@ -425,7 +418,6 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, // Number of incoming arguments. output_offset -= kPointerSize; - input_offset -= kPointerSize; value = reinterpret_cast(Smi::FromInt(height - 1)); output_frame->SetFrameSlot(output_offset, value); if (FLAG_trace_deopt) { @@ -446,6 +438,116 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator, } +void Deoptimizer::DoComputeConstructStubFrame(TranslationIterator* iterator, + int frame_index) { + JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next())); + unsigned height = iterator->Next(); + unsigned height_in_bytes = height * kPointerSize; + if (FLAG_trace_deopt) { + PrintF(" translating construct stub => height=%d\n", height_in_bytes); + } + + unsigned fixed_frame_size = 6 * 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, function); + output_frame->SetFrameType(StackFrame::CONSTRUCT); + + // Construct stub can not be topmost or bottommost. + 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; + top_address = output_[frame_index - 1]->GetTop() - output_frame_size; + output_frame->SetTop(top_address); + + // Compute the incoming parameter translation. + int parameter_count = height; + unsigned output_offset = output_frame_size; + for (int i = 0; i < parameter_count; ++i) { + output_offset -= kPointerSize; + DoTranslateCommand(iterator, frame_index, output_offset); + } + + // 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 + %d] <- 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 + %d] <- 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 + %d] <- 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(Smi::FromInt(StackFrame::CONSTRUCT)); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08" + V8PRIxPTR " ; function (construct sentinel)\n", + top_address + output_offset, output_offset, value); + } + + // Number of incoming arguments. + output_offset -= kPointerSize; + value = reinterpret_cast(Smi::FromInt(height - 1)); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08" + V8PRIxPTR " ; argc (%d)\n", + top_address + output_offset, output_offset, value, height - 1); + } + + // The newly allocated object was passed as receiver in the artificial + // constructor stub environment created by HEnvironment::CopyForInlining(). + output_offset -= kPointerSize; + value = output_frame->GetFrameSlot(output_frame_size - kPointerSize); + output_frame->SetFrameSlot(output_offset, value); + if (FLAG_trace_deopt) { + PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08" + V8PRIxPTR " ; allocated receiver\n", + top_address + output_offset, output_offset, value); + } + + ASSERT(0 == output_offset); + + Builtins* builtins = isolate_->builtins(); + Code* construct_stub = builtins->builtin(Builtins::kJSConstructStubGeneric); + intptr_t pc = reinterpret_cast( + construct_stub->instruction_start() + + isolate_->heap()->construct_stub_deopt_pc_offset()->value()); + output_frame->SetPc(pc); +} + + void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator, int frame_index) { int node_id = iterator->Next(); @@ -555,6 +657,7 @@ void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator, value = reinterpret_cast(function->context()); } output_frame->SetFrameSlot(output_offset, value); + output_frame->SetContext(value); if (is_topmost) output_frame->SetRegister(rsi.code(), value); if (FLAG_trace_deopt) { PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08" diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index 9c03053..a9bf9a8 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -2280,6 +2280,7 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) { CallConstructStub stub(flags); __ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL); + PrepareForBailoutForId(expr->ReturnId(), TOS_REG); context()->Plug(rax); } diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index 2151cf4..cf2c1b0 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -368,10 +368,18 @@ void LCodeGen::WriteTranslation(LEnvironment* environment, WriteTranslation(environment->outer(), translation); int closure_id = DefineDeoptimizationLiteral(environment->closure()); - if (environment->is_arguments_adaptor()) { - translation->BeginArgumentsAdaptorFrame(closure_id, translation_size); - } else { - translation->BeginJSFrame(environment->ast_id(), closure_id, height); + switch (environment->frame_type()) { + case JS_FUNCTION: + translation->BeginJSFrame(environment->ast_id(), closure_id, height); + break; + case JS_CONSTRUCT: + translation->BeginConstructStubFrame(closure_id, translation_size); + break; + case ARGUMENTS_ADAPTOR: + translation->BeginArgumentsAdaptorFrame(closure_id, translation_size); + break; + default: + UNREACHABLE(); } for (int i = 0; i < translation_size; ++i) { LOperand* value = environment->values()->at(i); @@ -511,7 +519,7 @@ void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment, int jsframe_count = 0; for (LEnvironment* e = environment; e != NULL; e = e->outer()) { ++frame_count; - if (!e->is_arguments_adaptor()) { + if (e->frame_type() == JS_FUNCTION) { ++jsframe_count; } } @@ -3925,6 +3933,43 @@ void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) { } +void LCodeGen::DoAllocateObject(LAllocateObject* instr) { + class DeferredAllocateObject: public LDeferredCode { + public: + DeferredAllocateObject(LCodeGen* codegen, LAllocateObject* instr) + : LDeferredCode(codegen), instr_(instr) { } + virtual void Generate() { codegen()->DoDeferredAllocateObject(instr_); } + virtual LInstruction* instr() { return instr_; } + private: + LAllocateObject* instr_; + }; + + DeferredAllocateObject* deferred = new DeferredAllocateObject(this, instr); + + // TODO(mstarzinger): Implement inlined version instead of jumping to + // deferred runtime call. + __ jmp(deferred->entry()); + + __ bind(deferred->exit()); +} + + +void LCodeGen::DoDeferredAllocateObject(LAllocateObject* instr) { + Register result = ToRegister(instr->result()); + Handle constructor = instr->hydrogen()->constructor(); + + // TODO(3095996): Get rid of this. For now, we need to make the + // result register contain a valid pointer because it is already + // contained in the register pointer map. + __ Set(result, 0); + + PushSafepointRegistersScope scope(this); + __ PushHeapObject(constructor); + CallRuntimeFromDeferred(Runtime::kNewObject, 1, instr); + __ StoreToSafepointRegisterSlot(result, rax); +} + + void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) { Heap* heap = isolate()->heap(); ElementsKind boilerplate_elements_kind = diff --git a/src/x64/lithium-codegen-x64.h b/src/x64/lithium-codegen-x64.h index 2890c53..1a9275b 100644 --- a/src/x64/lithium-codegen-x64.h +++ b/src/x64/lithium-codegen-x64.h @@ -99,6 +99,7 @@ class LCodeGen BASE_EMBEDDED { void DoDeferredStackCheck(LStackCheck* instr); void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr); void DoDeferredStringCharFromCode(LStringCharFromCode* instr); + void DoDeferredAllocateObject(LAllocateObject* instr); void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, Label* map_check); diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index d373e19..65d57e0 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -990,10 +990,11 @@ LEnvironment* LChunkBuilder::CreateEnvironment( LEnvironment* outer = CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator); int ast_id = hydrogen_env->ast_id(); - ASSERT(ast_id != AstNode::kNoNumber || hydrogen_env->is_arguments_adaptor()); + ASSERT(ast_id != AstNode::kNoNumber || + hydrogen_env->frame_type() != JS_FUNCTION); int value_count = hydrogen_env->length(); LEnvironment* result = new LEnvironment(hydrogen_env->closure(), - hydrogen_env->is_arguments_adaptor(), + hydrogen_env->frame_type(), ast_id, hydrogen_env->parameter_count(), argument_count_, @@ -1015,7 +1016,7 @@ LEnvironment* LChunkBuilder::CreateEnvironment( result->AddValue(op, value->representation()); } - if (!hydrogen_env->is_arguments_adaptor()) { + if (hydrogen_env->frame_type() == JS_FUNCTION) { *argument_index_accumulator = argument_index; } @@ -2094,6 +2095,12 @@ LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) { } +LInstruction* LChunkBuilder::DoAllocateObject(HAllocateObject* instr) { + LAllocateObject* result = new LAllocateObject(); + return AssignPointerMap(DefineAsRegister(result)); +} + + LInstruction* LChunkBuilder::DoFastLiteral(HFastLiteral* instr) { return MarkAsCall(DefineFixed(new LFastLiteral, rax), instr); } @@ -2246,7 +2253,8 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) { instr->arguments_count(), instr->function(), undefined, - instr->call_kind()); + instr->call_kind(), + instr->is_construct()); current_block_->UpdateEnvironment(inner); chunk_->AddInlinedClosure(instr->closure()); return NULL; diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h index b91cbc4..b8a8c8a 100644 --- a/src/x64/lithium-x64.h +++ b/src/x64/lithium-x64.h @@ -49,6 +49,7 @@ class LCodeGen; #define LITHIUM_CONCRETE_INSTRUCTION_LIST(V) \ V(AccessArgumentsAt) \ V(AddI) \ + V(AllocateObject) \ V(ApplyArguments) \ V(ArgumentsElements) \ V(ArgumentsLength) \ @@ -1910,6 +1911,13 @@ class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> { }; +class LAllocateObject: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(AllocateObject, "allocate-object") + DECLARE_HYDROGEN_ACCESSOR(AllocateObject) +}; + + class LFastLiteral: public LTemplateInstruction<1, 0, 0> { public: DECLARE_CONCRETE_INSTRUCTION(FastLiteral, "fast-literal") diff --git a/test/mjsunit/compiler/inline-construct.js b/test/mjsunit/compiler/inline-construct.js new file mode 100644 index 0000000..16e19a7 --- /dev/null +++ b/test/mjsunit/compiler/inline-construct.js @@ -0,0 +1,170 @@ +// 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: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax --inline-construct + +// Test inlining of constructor calls. + +function TestInlinedConstructor(closure) { + var result; + var counter = { value:0 }; + result = closure(11, 12, counter); + assertEquals(23, result); + assertEquals(1, counter.value); + result = closure(23, 19, counter); + assertEquals(42, result); + assertEquals(2, counter.value); + %OptimizeFunctionOnNextCall(closure); + result = closure(1, 42, counter) + assertEquals(43, result); + assertEquals(3, counter.value); + result = closure("foo", "bar", counter) + assertEquals("foobar", result) + assertEquals(4, counter.value); +} + + +// Test constructor returning nothing in all contexts. +function c1(a, b, counter) { + this.x = a + b; + counter.value++; +} +function c1_value_context(a, b, counter) { + var obj = new c1(a, b, counter); + return obj.x; +} +function c1_test_context(a, b, counter) { + if (!new c1(a, b, counter)) { + assertUnreachable("should not happen"); + } + return a + b; +} +function c1_effect_context(a, b, counter) { + new c1(a, b, counter); + return a + b; +} +TestInlinedConstructor(c1_value_context); +TestInlinedConstructor(c1_test_context); +TestInlinedConstructor(c1_effect_context); + + +// Test constructor returning an object in all contexts. +function c2(a, b, counter) { + var obj = new Object(); + obj.x = a + b; + counter.value++; + return obj; +} +function c2_value_context(a, b, counter) { + var obj = new c2(a, b, counter); + return obj.x; +} +function c2_test_context(a, b, counter) { + if (!new c2(a, b, counter)) { + assertUnreachable("should not happen"); + } + return a + b; +} +function c2_effect_context(a, b, counter) { + new c2(a, b, counter); + return a + b; +} +TestInlinedConstructor(c2_value_context); +TestInlinedConstructor(c2_test_context); +TestInlinedConstructor(c2_effect_context); + + +// Test constructor returning a primitive value in all contexts. +function c3(a, b, counter) { + this.x = a + b; + counter.value++; + return "not an object"; +} +function c3_value_context(a, b, counter) { + var obj = new c3(a, b, counter); + return obj.x; +} +function c3_test_context(a, b, counter) { + if (!new c3(a, b, counter)) { + assertUnreachable("should not happen"); + } + return a + b; +} +function c3_effect_context(a, b, counter) { + new c3(a, b, counter); + return a + b; +} +TestInlinedConstructor(c3_value_context); +TestInlinedConstructor(c3_test_context); +TestInlinedConstructor(c3_effect_context); + + +// Test constructor called with too many arguments. +function c_too_many(a, b) { + this.x = a + b; +} +function f_too_many(a, b, c) { + var obj = new c_too_many(a, b, c); + return obj.x; +} +assertEquals(23, f_too_many(11, 12, 1)); +assertEquals(42, f_too_many(23, 19, 1)); +%OptimizeFunctionOnNextCall(f_too_many); +assertEquals(43, f_too_many(1, 42, 1)); +assertEquals("foobar", f_too_many("foo", "bar", "baz")) + + +// Test constructor called with too few arguments. +function c_too_few(a, b) { + assertSame(undefined, b); + this.x = a + 1; +} +function f_too_few(a) { + var obj = new c_too_few(a); + return obj.x; +} +assertEquals(12, f_too_few(11)); +assertEquals(24, f_too_few(23)); +%OptimizeFunctionOnNextCall(f_too_few); +assertEquals(2, f_too_few(1)); +assertEquals("foo1", f_too_few("foo")) + + +// Test constructor that cannot be inlined. +function c_unsupported_syntax(a, b, counter) { + try { + this.x = a + b; + counter.value++; + } catch(e) { + throw new Error(); + } +} +function f_unsupported_syntax(a, b, counter) { + var obj = new c_unsupported_syntax(a, b, counter); + return obj.x; +} +TestInlinedConstructor(f_unsupported_syntax); diff --git a/test/mjsunit/debug-evaluate-locals-optimized-double.js b/test/mjsunit/debug-evaluate-locals-optimized-double.js index 7178661..cf25c0c 100644 --- a/test/mjsunit/debug-evaluate-locals-optimized-double.js +++ b/test/mjsunit/debug-evaluate-locals-optimized-double.js @@ -25,7 +25,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --expose-debug-as debug --allow-natives-syntax +// Flags: --expose-debug-as debug --expose-gc --allow-natives-syntax --inline-construct // Get the Debug object exposed from the debug context global object. Debug = debug.Debug @@ -140,7 +140,13 @@ function listener(event, exec_state, event_data, data) { } // Check for construct call. - assertEquals(testingConstructCall && i == 4, frame.isConstructCall()); + if (i == 4) { + assertEquals(testingConstructCall, frame.isConstructCall()); + } else if (i == 2) { + assertTrue(frame.isConstructCall()); + } else { + assertFalse(frame.isConstructCall()); + } // When function f is optimized (1 means YES, see runtime.cc) we // expect an optimized frame for f with g1, g2 and g3 inlined. @@ -204,7 +210,7 @@ function g1(i, x3, y3, z3) { var b3 = input[i].b; a3 = a3 + a3 / 100; b3 = b3 + b3 / 100; - g2(i - 1, a3, b3); + new g2(i - 1, a3, b3); }; function f(i, x4, y4) { @@ -222,8 +228,11 @@ testingConstructCall = true; new f(input.length - 1, 11.11, 12.12); new f(input.length - 1, 11.11, 12.12, ""); -// Make sure that the debug event listener vas invoked. +// Make sure that the debug event listener was invoked. assertFalse(exception, "exception in listener " + exception) assertTrue(listenerComplete); +//Throw away type information for next run. +gc(); + Debug.setListener(null); diff --git a/test/mjsunit/debug-evaluate-locals-optimized.js b/test/mjsunit/debug-evaluate-locals-optimized.js index 485f752..c88a683 100644 --- a/test/mjsunit/debug-evaluate-locals-optimized.js +++ b/test/mjsunit/debug-evaluate-locals-optimized.js @@ -25,7 +25,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --expose-debug-as debug --allow-natives-syntax +// Flags: --expose-debug-as debug --expose-gc --allow-natives-syntax --inline-construct // Get the Debug object exposed from the debug context global object. Debug = debug.Debug @@ -130,7 +130,13 @@ function listener(event, exec_state, event_data, data) { } // Check for construct call. - assertEquals(testingConstructCall && i == 4, frame.isConstructCall()); + if (i == 4) { + assertEquals(testingConstructCall, frame.isConstructCall()); + } else if (i == 2) { + assertTrue(frame.isConstructCall()); + } else { + assertFalse(frame.isConstructCall()); + } // When function f is optimized (1 means YES, see runtime.cc) we // expect an optimized frame for f with g1, g2 and g3 inlined. @@ -185,7 +191,7 @@ function g2(i) { function g1(i, x3, y3, z3) { var a3 = expected[i].locals.a3; var b3 = expected[i].locals.b3; - g2(i - 1, a3, b3); + new g2(i - 1, a3, b3); } function f(i, x4, y4) { @@ -201,8 +207,11 @@ testingConstructCall = true; new f(expected.length - 1, 11, 12); new f(expected.length - 1, 11, 12, 0); -// Make sure that the debug event listener vas invoked. +// Make sure that the debug event listener was invoked. assertFalse(exception, "exception in listener " + exception) assertTrue(listenerComplete); +// Throw away type information for next run. +gc(); + Debug.setListener(null); diff --git a/test/mjsunit/regress/regress-1229.js b/test/mjsunit/regress/regress-1229.js index 3d166d5..a52e92a 100644 --- a/test/mjsunit/regress/regress-1229.js +++ b/test/mjsunit/regress/regress-1229.js @@ -126,19 +126,39 @@ invoke(h3, [8, 6, 4]); // Check that %_IsConstructCall returns correct value when inlined var NON_CONSTRUCT_MARKER = {}; var CONSTRUCT_MARKER = {}; -function baz(x) { +function baz1(x) { return (!%_IsConstructCall()) ? NON_CONSTRUCT_MARKER : CONSTRUCT_MARKER; } -function bar(x, y, z) { - var non_construct = baz(0); /* baz should be inlined */ +function bar1(x, y, z) { + var non_construct = baz1(0); /* baz should be inlined */ assertSame(non_construct, NON_CONSTRUCT_MARKER); - var non_construct = baz(); /* baz should be inlined */ + var non_construct = baz1(); /* baz should be inlined */ assertSame(non_construct, NON_CONSTRUCT_MARKER); - var non_construct = baz(0, 0); /* baz should be inlined */ + var non_construct = baz1(0, 0); /* baz should be inlined */ assertSame(non_construct, NON_CONSTRUCT_MARKER); - var construct = new baz(0); + var construct = new baz1(0); + assertSame(construct, CONSTRUCT_MARKER); + var construct = new baz1(0, 0); + assertSame(construct, CONSTRUCT_MARKER); +} + +function baz2(x) { + return (!%IsConstructCall()) ? NON_CONSTRUCT_MARKER : CONSTRUCT_MARKER; +} + +function bar2(x, y, z) { + var non_construct = baz2(0); /* baz should be inlined */ + assertSame(non_construct, NON_CONSTRUCT_MARKER); + var non_construct = baz2(); /* baz should be inlined */ + assertSame(non_construct, NON_CONSTRUCT_MARKER); + var non_construct = baz2(0, 0); /* baz should be inlined */ + assertSame(non_construct, NON_CONSTRUCT_MARKER); + var construct = new baz2(0); + assertSame(construct, CONSTRUCT_MARKER); + var construct = new baz2(0, 0); assertSame(construct, CONSTRUCT_MARKER); } -invoke(bar, [1, 2, 3]); +invoke(bar1, [1, 2, 3]); +invoke(bar2, [1, 2, 3]);