From 607a175cbc9042db07c0d0d76ca01b330c420a56 Mon Sep 17 00:00:00 2001 From: "verwaest@chromium.org" Date: Thu, 14 Nov 2013 13:46:18 +0000 Subject: [PATCH] Add support for keyed-call on arrays of fast elements R=danno@chromium.org Review URL: https://chromiumcodereview.appspot.com/23537067 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@17746 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/code-stubs-arm.cc | 31 +++++++++++ src/arm/lithium-arm.cc | 6 ++- src/arm/lithium-codegen-arm.cc | 30 +++++++++-- src/ast.cc | 6 ++- src/ast.h | 3 ++ src/code-stubs-hydrogen.cc | 26 +++++++++ src/code-stubs.cc | 12 +++++ src/code-stubs.h | 92 +++++++++++++++++++++++++++++--- src/compiler.cc | 20 ++++--- src/compiler.h | 7 +++ src/deoptimizer.cc | 16 ++++-- src/frames.cc | 5 ++ src/heap.cc | 6 +++ src/heap.h | 1 + src/hydrogen-instructions.h | 27 ++++++++-- src/hydrogen.cc | 24 ++++++--- src/hydrogen.h | 4 +- src/ia32/code-stubs-ia32.cc | 31 +++++++++++ src/ia32/lithium-codegen-ia32.cc | 30 +++++++++-- src/ia32/lithium-ia32.cc | 6 ++- src/ic.cc | 56 ++++++++++++++++--- src/ic.h | 1 + src/isolate.cc | 3 ++ src/lithium.cc | 3 +- src/objects-inl.h | 2 + src/type-info.cc | 12 ++++- src/type-info.h | 1 + src/typing.cc | 3 +- src/x64/code-stubs-x64.cc | 31 +++++++++++ src/x64/lithium-codegen-x64.cc | 21 +++++++- src/x64/lithium-x64.cc | 6 ++- test/mjsunit/keyed-array-call.js | 56 +++++++++++++++++++ 32 files changed, 522 insertions(+), 56 deletions(-) create mode 100644 test/mjsunit/keyed-array-call.js diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index 8db65bcfd4..0fe6fa8825 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -133,6 +133,19 @@ void KeyedLoadFieldStub::InitializeInterfaceDescriptor( } +void KeyedArrayCallStub::InitializeInterfaceDescriptor( + Isolate* isolate, + CodeStubInterfaceDescriptor* descriptor) { + static Register registers[] = { r2 }; + descriptor->register_param_count_ = 1; + descriptor->register_params_ = registers; + descriptor->continuation_type_ = TAIL_CALL_CONTINUATION; + descriptor->handler_arguments_mode_ = PASS_ARGUMENTS; + descriptor->deoptimization_handler_ = + FUNCTION_ADDR(KeyedCallIC_MissFromStubFailure); +} + + void KeyedStoreFastElementStub::InitializeInterfaceDescriptor( Isolate* isolate, CodeStubInterfaceDescriptor* descriptor) { @@ -5693,6 +5706,24 @@ void StubFailureTrampolineStub::Generate(MacroAssembler* masm) { } +void StubFailureTailCallTrampolineStub::Generate(MacroAssembler* masm) { + CEntryStub ces(1, fp_registers_ ? kSaveFPRegs : kDontSaveFPRegs); + __ Call(ces.GetCode(masm->isolate()), RelocInfo::CODE_TARGET); + __ mov(r1, r0); + int parameter_count_offset = + StubFailureTrampolineFrame::kCallerStackParameterCountFrameOffset; + __ ldr(r0, MemOperand(fp, parameter_count_offset)); + // The parameter count above includes the receiver for the arguments passed to + // the deoptimization handler. Subtract the receiver for the parameter count + // for the call. + __ sub(r0, r0, Operand(1)); + masm->LeaveFrame(StackFrame::STUB_FAILURE_TRAMPOLINE); + ParameterCount argument_count(r0); + __ InvokeFunction( + r1, argument_count, JUMP_FUNCTION, NullCallWrapper(), CALL_AS_METHOD); +} + + void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) { if (masm->isolate()->function_entry_hook() != NULL) { PredictableCodeSizeScope predictable(masm, 4 * Assembler::kInstrSize); diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index 787fc81aa8..53ba309e61 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -1328,8 +1328,10 @@ LInstruction* LChunkBuilder::DoCallNewArray(HCallNewArray* instr) { LInstruction* LChunkBuilder::DoCallFunction(HCallFunction* instr) { LOperand* context = UseFixed(instr->context(), cp); LOperand* function = UseFixed(instr->function(), r1); - return MarkAsCall( - DefineFixed(new(zone()) LCallFunction(context, function), r0), instr); + LCallFunction* call = new(zone()) LCallFunction(context, function); + LInstruction* result = DefineFixed(call, r0); + if (instr->IsTailCall()) return result; + return MarkAsCall(result, instr); } diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 207dd8cdaa..de9123d5c4 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -509,17 +509,36 @@ Operand LCodeGen::ToOperand(LOperand* op) { } +static int ArgumentsOffsetWithoutFrame(int index) { + ASSERT(index < 0); + return -(index + 1) * kPointerSize; +} + + MemOperand LCodeGen::ToMemOperand(LOperand* op) const { ASSERT(!op->IsRegister()); ASSERT(!op->IsDoubleRegister()); ASSERT(op->IsStackSlot() || op->IsDoubleStackSlot()); - return MemOperand(fp, StackSlotOffset(op->index())); + if (NeedsEagerFrame()) { + return MemOperand(fp, StackSlotOffset(op->index())); + } else { + // Retrieve parameter without eager stack-frame relative to the + // stack-pointer. + return MemOperand(sp, ArgumentsOffsetWithoutFrame(op->index())); + } } MemOperand LCodeGen::ToHighMemOperand(LOperand* op) const { ASSERT(op->IsDoubleStackSlot()); - return MemOperand(fp, StackSlotOffset(op->index()) + kPointerSize); + if (NeedsEagerFrame()) { + return MemOperand(fp, StackSlotOffset(op->index()) + kPointerSize); + } else { + // Retrieve parameter without eager stack-frame relative to the + // stack-pointer. + return MemOperand( + sp, ArgumentsOffsetWithoutFrame(op->index()) + kPointerSize); + } } @@ -4107,7 +4126,12 @@ void LCodeGen::DoCallFunction(LCallFunction* instr) { int arity = instr->arity(); CallFunctionStub stub(arity, NO_CALL_FUNCTION_FLAGS); - CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); + if (instr->hydrogen()->IsTailCall()) { + if (NeedsEagerFrame()) __ mov(sp, fp); + __ Jump(stub.GetCode(isolate()), RelocInfo::CODE_TARGET); + } else { + CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); + } } diff --git a/src/ast.cc b/src/ast.cc index 9deb71d0af..adf0fb8703 100644 --- a/src/ast.cc +++ b/src/ast.cc @@ -776,7 +776,7 @@ void Call::RecordTypeFeedback(TypeFeedbackOracle* oracle, if (property == NULL) { // Function call. Specialize for monomorphic calls. if (is_monomorphic_) target_ = oracle->GetCallTarget(this); - } else { + } else if (property->key()->IsPropertyName()) { // Method call. Specialize for the receiver types seen at runtime. Literal* key = property->key()->AsLiteral(); ASSERT(key != NULL && key->value()->IsString()); @@ -803,6 +803,10 @@ void Call::RecordTypeFeedback(TypeFeedbackOracle* oracle, Handle map = receiver_types_.first(); is_monomorphic_ = ComputeTarget(map, name); } + } else { + if (is_monomorphic_) { + keyed_array_call_is_holey_ = oracle->KeyedArrayCallIsHoley(this); + } } } diff --git a/src/ast.h b/src/ast.h index 42f6c8bd93..2a8669690d 100644 --- a/src/ast.h +++ b/src/ast.h @@ -1728,6 +1728,7 @@ class Call V8_FINAL : public Expression { return &receiver_types_; } virtual bool IsMonomorphic() V8_OVERRIDE { return is_monomorphic_; } + bool KeyedArrayCallIsHoley() { return keyed_array_call_is_holey_; } CheckType check_type() const { return check_type_; } void set_string_check(Handle holder) { @@ -1778,6 +1779,7 @@ class Call V8_FINAL : public Expression { expression_(expression), arguments_(arguments), is_monomorphic_(false), + keyed_array_call_is_holey_(true), check_type_(RECEIVER_MAP_CHECK), return_id_(GetNextId(isolate)) { } @@ -1786,6 +1788,7 @@ class Call V8_FINAL : public Expression { ZoneList* arguments_; bool is_monomorphic_; + bool keyed_array_call_is_holey_; CheckType check_type_; SmallMapList receiver_types_; Handle target_; diff --git a/src/code-stubs-hydrogen.cc b/src/code-stubs-hydrogen.cc index eb3bac8fbf..d65026b843 100644 --- a/src/code-stubs-hydrogen.cc +++ b/src/code-stubs-hydrogen.cc @@ -594,6 +594,32 @@ Handle KeyedLoadFieldStub::GenerateCode(Isolate* isolate) { } +template<> +HValue* CodeStubGraphBuilder::BuildCodeStub() { + int argc = casted_stub()->argc() + 1; + info()->set_parameter_count(argc); + + HValue* receiver = Add(1); + + // Load the expected initial array map from the context. + JSArrayBuilder array_builder(this, casted_stub()->elements_kind()); + HValue* map = array_builder.EmitMapCode(); + + HValue* checked_receiver = Add(receiver, map); + + HValue* function = BuildUncheckedMonomorphicElementAccess( + checked_receiver, GetParameter(0), + NULL, true, casted_stub()->elements_kind(), + false, NEVER_RETURN_HOLE, STANDARD_STORE); + return Add(function, argc, TAIL_CALL); +} + + +Handle KeyedArrayCallStub::GenerateCode(Isolate* isolate) { + return DoGenerateCode(isolate, this); +} + + template <> HValue* CodeStubGraphBuilder::BuildCodeStub() { BuildUncheckedMonomorphicElementAccess( diff --git a/src/code-stubs.cc b/src/code-stubs.cc index f6e880f610..c37c56e4f7 100644 --- a/src/code-stubs.cc +++ b/src/code-stubs.cc @@ -43,6 +43,7 @@ CodeStubInterfaceDescriptor::CodeStubInterfaceDescriptor() : register_param_count_(-1), stack_parameter_count_(no_reg), hint_stack_parameter_count_(-1), + continuation_type_(NORMAL_CONTINUATION), function_mode_(NOT_JS_FUNCTION_STUB_MODE), register_params_(NULL), deoptimization_handler_(NULL), @@ -51,6 +52,11 @@ CodeStubInterfaceDescriptor::CodeStubInterfaceDescriptor() has_miss_handler_(false) { } +void CodeStub::GenerateStubsRequiringBuiltinsAheadOfTime(Isolate* isolate) { + StubFailureTailCallTrampolineStub::GenerateAheadOfTime(isolate); +} + + bool CodeStub::FindCodeInCache(Code** code_out, Isolate* isolate) { UnseededNumberDictionary* stubs = isolate->heap()->code_stubs(); int index = stubs->FindEntry(GetKey()); @@ -1109,6 +1115,12 @@ void StubFailureTrampolineStub::GenerateAheadOfTime(Isolate* isolate) { } +void StubFailureTailCallTrampolineStub::GenerateAheadOfTime(Isolate* isolate) { + StubFailureTailCallTrampolineStub stub; + stub.GetCode(isolate)->set_is_pregenerated(true); +} + + void ProfileEntryHookStub::EntryHookTrampoline(intptr_t function, intptr_t stack_pointer, Isolate* isolate) { diff --git a/src/code-stubs.h b/src/code-stubs.h index 8f1b686743..cd14047abd 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -90,13 +90,15 @@ namespace internal { V(TransitionElementsKind) \ V(StoreArrayLiteralElement) \ V(StubFailureTrampoline) \ + V(StubFailureTailCallTrampoline) \ V(ArrayConstructor) \ V(InternalArrayConstructor) \ V(ProfileEntryHook) \ V(StoreGlobal) \ /* IC Handler stubs */ \ V(LoadField) \ - V(KeyedLoadField) + V(KeyedLoadField) \ + V(KeyedArrayCall) // List of code stubs only used on ARM platforms. #if V8_TARGET_ARCH_ARM @@ -170,6 +172,7 @@ class CodeStub BASE_EMBEDDED { virtual bool IsPregenerated(Isolate* isolate) { return false; } static void GenerateStubsAheadOfTime(Isolate* isolate); + static void GenerateStubsRequiringBuiltinsAheadOfTime(Isolate* isolate); static void GenerateFPStubs(Isolate* isolate); // Some stubs put untagged junk on the stack that cannot be scanned by the @@ -279,6 +282,9 @@ class PlatformCodeStub : public CodeStub { enum StubFunctionMode { NOT_JS_FUNCTION_STUB_MODE, JS_FUNCTION_STUB_MODE }; enum HandlerArgumentsMode { DONT_PASS_ARGUMENTS, PASS_ARGUMENTS }; +enum ContinuationType { NORMAL_CONTINUATION, TAIL_CALL_CONTINUATION }; + + struct CodeStubInterfaceDescriptor { CodeStubInterfaceDescriptor(); int register_param_count_; @@ -287,18 +293,23 @@ struct CodeStubInterfaceDescriptor { // if hint_stack_parameter_count_ > 0, the code stub can optimize the // return sequence. Default value is -1, which means it is ignored. int hint_stack_parameter_count_; + ContinuationType continuation_type_; StubFunctionMode function_mode_; Register* register_params_; Address deoptimization_handler_; HandlerArgumentsMode handler_arguments_mode_; + bool initialized() const { return register_param_count_ >= 0; } + + bool HasTailCallContinuation() const { + return continuation_type_ == TAIL_CALL_CONTINUATION; + } + int environment_length() const { return register_param_count_; } - bool initialized() const { return register_param_count_ >= 0; } - void SetMissHandler(ExternalReference handler) { miss_handler_ = handler; has_miss_handler_ = true; @@ -876,6 +887,11 @@ class HandlerStub: public HICStub { public: virtual Code::Kind GetCodeKind() const { return Code::HANDLER; } virtual int GetStubFlags() { return kind(); } + + protected: + HandlerStub() : HICStub() { } + virtual int NotMissMinorKey() { return bit_field_; } + int bit_field_; }; @@ -937,9 +953,6 @@ class LoadFieldStub: public HandlerStub { class IndexBits: public BitField {}; class UnboxedDoubleBits: public BitField {}; virtual CodeStub::Major MajorKey() { return LoadField; } - virtual int NotMissMinorKey() { return bit_field_; } - - int bit_field_; }; @@ -1018,6 +1031,52 @@ class KeyedLoadFieldStub: public LoadFieldStub { }; +class KeyedArrayCallStub: public HICStub { + public: + KeyedArrayCallStub(bool holey, int argc) : HICStub(), argc_(argc) { + bit_field_ = KindBits::encode(Code::KEYED_CALL_IC) + | HoleyBits::encode(holey); + } + + virtual Code::Kind kind() const { + return KindBits::decode(bit_field_); + } + + virtual Code::ExtraICState GetExtraICState() { return bit_field_; } + + ElementsKind elements_kind() { + return HoleyBits::decode(bit_field_) ? FAST_HOLEY_ELEMENTS : FAST_ELEMENTS; + } + + int argc() { return argc_; } + virtual int GetStubFlags() { return argc(); } + + static bool IsHoley(Handle code) { + Code::ExtraICState state = code->extra_ic_state(); + return HoleyBits::decode(state); + } + + virtual void InitializeInterfaceDescriptor( + Isolate* isolate, + CodeStubInterfaceDescriptor* descriptor); + + virtual Handle GenerateCode(Isolate* isolate); + + private: + virtual int NotMissMinorKey() { + return GetExtraICState() | ArgcBits::encode(argc_); + } + + STATIC_ASSERT(KindBits::kSize == 4); + class HoleyBits: public BitField {}; + STATIC_ASSERT(Code::kArgumentsBits <= kStubMinorKeyBits - 5); + class ArgcBits: public BitField {}; + virtual CodeStub::Major MajorKey() { return KeyedArrayCall; } + int bit_field_; + int argc_; +}; + + class BinaryOpStub: public HydrogenCodeStub { public: BinaryOpStub(Token::Value op, OverwriteMode mode) @@ -2399,6 +2458,27 @@ class StubFailureTrampolineStub : public PlatformCodeStub { }; +class StubFailureTailCallTrampolineStub : public PlatformCodeStub { + public: + StubFailureTailCallTrampolineStub() : fp_registers_(CanUseFPRegisters()) {} + + virtual bool IsPregenerated(Isolate* isolate) V8_OVERRIDE { return true; } + + static void GenerateAheadOfTime(Isolate* isolate); + + private: + class FPRegisters: public BitField {}; + Major MajorKey() { return StubFailureTailCallTrampoline; } + int MinorKey() { return FPRegisters::encode(fp_registers_); } + + void Generate(MacroAssembler* masm); + + bool fp_registers_; + + DISALLOW_COPY_AND_ASSIGN(StubFailureTailCallTrampolineStub); +}; + + class ProfileEntryHookStub : public PlatformCodeStub { public: explicit ProfileEntryHookStub() {} diff --git a/src/compiler.cc b/src/compiler.cc index e86baa02aa..1ec2626da9 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -59,7 +59,8 @@ CompilationInfo::CompilationInfo(Handle