From 9b892b86b1958d8b2917d810553cbf1669412d7e Mon Sep 17 00:00:00 2001 From: "bmeurer@chromium.org" Date: Mon, 2 Dec 2013 13:14:07 +0000 Subject: [PATCH] Refactor BinaryOpIC to be able to use different stubs. Previously BinaryOpIC and BinaryOpStub were pretty much interdependent. However, in order to use allocation sites for string adds on-demand, we need to be able to use different stubs (with a different number of register parameters, via trampolines) depending on the BinaryOpIC state. R=hpayer@chromium.org, mvstanton@chromium.org Review URL: https://codereview.chromium.org/97543002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@18191 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/code-stubs-arm.cc | 4 +- src/arm/full-codegen-arm.cc | 7 +- src/arm/lithium-codegen-arm.cc | 2 +- src/code-stubs-hydrogen.cc | 49 ++-- src/code-stubs.cc | 488 ++------------------------------------- src/code-stubs.h | 180 +++------------ src/ia32/code-stubs-ia32.cc | 6 +- src/ia32/full-codegen-ia32.cc | 6 +- src/ia32/lithium-codegen-ia32.cc | 2 +- src/ic.cc | 468 +++++++++++++++++++++++++++++++++---- src/ic.h | 109 ++++++++- src/isolate.cc | 2 +- src/log.cc | 7 +- src/token.h | 4 + src/type-info.cc | 36 ++- src/x64/code-stubs-x64.cc | 4 +- src/x64/full-codegen-x64.cc | 6 +- src/x64/lithium-codegen-x64.cc | 2 +- 18 files changed, 645 insertions(+), 737 deletions(-) diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index d63bc65..cc2dbdc 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -193,7 +193,7 @@ void CompareNilICStub::InitializeInterfaceDescriptor( } -void BinaryOpStub::InitializeInterfaceDescriptor( +void BinaryOpICStub::InitializeInterfaceDescriptor( Isolate* isolate, CodeStubInterfaceDescriptor* descriptor) { static Register registers[] = { r1, r0 }; @@ -1666,7 +1666,7 @@ void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) { StubFailureTrampolineStub::GenerateAheadOfTime(isolate); ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate); CreateAllocationSiteStub::GenerateAheadOfTime(isolate); - BinaryOpStub::GenerateAheadOfTime(isolate); + BinaryOpICStub::GenerateAheadOfTime(isolate); } diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 88421af..5508803 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -2298,7 +2298,7 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr, patch_site.EmitJumpIfSmi(scratch1, &smi_case); __ bind(&stub_call); - BinaryOpStub stub(op, mode); + BinaryOpICStub stub(op, mode); CallIC(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, expr->BinaryOperationFeedbackId()); patch_site.EmitPatchInfo(); @@ -2307,7 +2307,6 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr, __ bind(&smi_case); // Smi case. This code works the same way as the smi-smi case in the type // recording binary operation stub, see - // BinaryOpStub::GenerateSmiSmiOperation for comments. switch (op) { case Token::SAR: __ GetLeastBitsFromSmi(scratch1, right, 5); @@ -2376,7 +2375,7 @@ void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr, Token::Value op, OverwriteMode mode) { __ pop(r1); - BinaryOpStub stub(op, mode); + BinaryOpICStub stub(op, mode); JumpPatchSite patch_site(masm_); // unbound, signals no inlined smi code. CallIC(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, expr->BinaryOperationFeedbackId()); @@ -4416,7 +4415,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { // Record position before stub call. SetSourcePosition(expr->position()); - BinaryOpStub stub(Token::ADD, NO_OVERWRITE); + BinaryOpICStub stub(Token::ADD, NO_OVERWRITE); CallIC(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, expr->CountBinOpFeedbackId()); diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index b432bc8..c842df2 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -2186,7 +2186,7 @@ void LCodeGen::DoArithmeticT(LArithmeticT* instr) { ASSERT(ToRegister(instr->right()).is(r0)); ASSERT(ToRegister(instr->result()).is(r0)); - BinaryOpStub stub(instr->op(), NO_OVERWRITE); + BinaryOpICStub stub(instr->op(), NO_OVERWRITE); // Block literal pool emission to ensure nop indicating no inlined smi code // is in the correct position. Assembler::BlockConstPoolScope block_const_pool(masm()); diff --git a/src/code-stubs-hydrogen.cc b/src/code-stubs-hydrogen.cc index acda324..e52ec65 100644 --- a/src/code-stubs-hydrogen.cc +++ b/src/code-stubs-hydrogen.cc @@ -895,20 +895,21 @@ Handle CompareNilICStub::GenerateCode(Isolate* isolate) { template <> -HValue* CodeStubGraphBuilder::BuildCodeInitializedStub() { - BinaryOpStub* stub = casted_stub(); - HValue* left = GetParameter(0); - HValue* right = GetParameter(1); +HValue* CodeStubGraphBuilder::BuildCodeInitializedStub() { + BinaryOpIC::State state = casted_stub()->state(); - Handle left_type = stub->GetLeftType(isolate()); - Handle right_type = stub->GetRightType(isolate()); - Handle result_type = stub->GetResultType(isolate()); + HValue* left = GetParameter(BinaryOpICStub::kLeft); + HValue* right = GetParameter(BinaryOpICStub::kRight); + + Handle left_type = state.GetLeftType(isolate()); + Handle right_type = state.GetRightType(isolate()); + Handle result_type = state.GetResultType(isolate()); ASSERT(!left_type->Is(Type::None()) && !right_type->Is(Type::None()) && - (stub->HasSideEffects(isolate()) || !result_type->Is(Type::None()))); + (state.HasSideEffects() || !result_type->Is(Type::None()))); HValue* result = NULL; - if (stub->operation() == Token::ADD && + if (state.op() == Token::ADD && (left_type->Maybe(Type::String()) || right_type->Maybe(Type::String())) && !left_type->Is(Type::String()) && !right_type->Is(Type::String())) { // For the generic add stub a fast case for string addition is performance @@ -919,16 +920,16 @@ HValue* CodeStubGraphBuilder::BuildCodeInitializedStub() { if_leftisstring.Then(); { Push(BuildBinaryOperation( - stub->operation(), left, right, + state.op(), left, right, handle(Type::String(), isolate()), right_type, - result_type, stub->fixed_right_arg())); + result_type, state.fixed_right_arg())); } if_leftisstring.Else(); { Push(BuildBinaryOperation( - stub->operation(), left, right, + state.op(), left, right, left_type, right_type, result_type, - stub->fixed_right_arg())); + state.fixed_right_arg())); } if_leftisstring.End(); result = Pop(); @@ -938,32 +939,32 @@ HValue* CodeStubGraphBuilder::BuildCodeInitializedStub() { if_rightisstring.Then(); { Push(BuildBinaryOperation( - stub->operation(), left, right, + state.op(), left, right, left_type, handle(Type::String(), isolate()), - result_type, stub->fixed_right_arg())); + result_type, state.fixed_right_arg())); } if_rightisstring.Else(); { Push(BuildBinaryOperation( - stub->operation(), left, right, + state.op(), left, right, left_type, right_type, result_type, - stub->fixed_right_arg())); + state.fixed_right_arg())); } if_rightisstring.End(); result = Pop(); } } else { result = BuildBinaryOperation( - stub->operation(), left, right, + state.op(), left, right, left_type, right_type, result_type, - stub->fixed_right_arg()); + state.fixed_right_arg()); } // If we encounter a generic argument, the number conversion is // observable, thus we cannot afford to bail out after the fact. - if (!stub->HasSideEffects(isolate())) { + if (!state.HasSideEffects()) { if (result_type->Is(Type::Smi())) { - if (stub->operation() == Token::SHR) { + if (state.op() == Token::SHR) { // TODO(olivf) Replace this by a SmiTagU Instruction. // 0x40000000: this number would convert to negative when interpreting // the register as signed value; @@ -981,8 +982,8 @@ HValue* CodeStubGraphBuilder::BuildCodeInitializedStub() { // Reuse the double box of one of the operands if we are allowed to (i.e. // chained binops). - if (stub->CanReuseDoubleBox()) { - HValue* operand = (stub->mode() == OVERWRITE_LEFT) ? left : right; + if (state.CanReuseDoubleBox()) { + HValue* operand = (state.mode() == OVERWRITE_LEFT) ? left : right; IfBuilder if_heap_number(this); if_heap_number.IfNot(operand); if_heap_number.Then(); @@ -998,7 +999,7 @@ HValue* CodeStubGraphBuilder::BuildCodeInitializedStub() { } -Handle BinaryOpStub::GenerateCode(Isolate* isolate) { +Handle BinaryOpICStub::GenerateCode(Isolate* isolate) { return DoGenerateCode(isolate, this); } diff --git a/src/code-stubs.cc b/src/code-stubs.cc index 3f26e3f..3fb6153 100644 --- a/src/code-stubs.cc +++ b/src/code-stubs.cc @@ -217,479 +217,32 @@ void CodeStub::PrintName(StringStream* stream) { } -void BinaryOpStub::PrintBaseName(StringStream* stream) { - const char* op_name = Token::Name(op_); - const char* ovr = ""; - if (mode_ == OVERWRITE_LEFT) ovr = "_ReuseLeft"; - if (mode_ == OVERWRITE_RIGHT) ovr = "_ReuseRight"; - stream->Add("BinaryOpStub_%s%s", op_name, ovr); -} - - -void BinaryOpStub::PrintState(StringStream* stream) { - stream->Add("("); - stream->Add(StateToName(left_state_)); - stream->Add("*"); - if (fixed_right_arg_.has_value) { - stream->Add("%d", fixed_right_arg_.value); - } else { - stream->Add(StateToName(right_state_)); - } - stream->Add("->"); - stream->Add(StateToName(result_state_)); - stream->Add(")"); -} - - -Maybe > BinaryOpStub::Result(Handle left, - Handle right, - Isolate* isolate) { - Handle builtins(isolate->js_builtins_object()); - Builtins::JavaScript func = BinaryOpIC::TokenToJSBuiltin(op_); - Object* builtin = builtins->javascript_builtin(func); - Handle builtin_function = - Handle(JSFunction::cast(builtin), isolate); - bool caught_exception; - Handle result = Execution::Call(isolate, builtin_function, left, - 1, &right, &caught_exception); - return Maybe >(!caught_exception, result); -} - - -void BinaryOpStub::Initialize() { - fixed_right_arg_.has_value = false; - left_state_ = right_state_ = result_state_ = NONE; -} - - -void BinaryOpStub::Generate(Token::Value op, - State left, - State right, - State result, - OverwriteMode mode, - Isolate* isolate) { - BinaryOpStub stub(INITIALIZED); - stub.op_ = op; - stub.left_state_ = left; - stub.right_state_ = right; - stub.result_state_ = result; - stub.mode_ = mode; - stub.GetCode(isolate); -} - - -void BinaryOpStub::Generate(Token::Value op, - State left, - int right, - State result, - OverwriteMode mode, - Isolate* isolate) { - BinaryOpStub stub(INITIALIZED); - stub.op_ = op; - stub.left_state_ = left; - stub.fixed_right_arg_.has_value = true; - stub.fixed_right_arg_.value = right; - stub.right_state_ = SMI; - stub.result_state_ = result; - stub.mode_ = mode; - stub.GetCode(isolate); -} - - -void BinaryOpStub::GenerateAheadOfTime(Isolate* isolate) { - Token::Value binop[] = {Token::SUB, Token::MOD, Token::DIV, Token::MUL, - Token::ADD, Token::SAR, Token::BIT_OR, Token::BIT_AND, - Token::BIT_XOR, Token::SHL, Token::SHR}; - for (unsigned i = 0; i < ARRAY_SIZE(binop); i++) { - BinaryOpStub stub(UNINITIALIZED); - stub.op_ = binop[i]; - stub.GetCode(isolate); - } - - // TODO(olivf) We should investigate why adding stubs to the snapshot is so - // expensive at runtime. When solved we should be able to add most binops to - // the snapshot instead of hand-picking them. - // Generated list of commonly used stubs - Generate(Token::ADD, INT32, INT32, INT32, NO_OVERWRITE, isolate); - Generate(Token::ADD, INT32, INT32, INT32, OVERWRITE_LEFT, isolate); - Generate(Token::ADD, INT32, INT32, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::ADD, INT32, INT32, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::ADD, INT32, NUMBER, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::ADD, INT32, NUMBER, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::ADD, INT32, NUMBER, NUMBER, OVERWRITE_RIGHT, isolate); - Generate(Token::ADD, INT32, SMI, INT32, NO_OVERWRITE, isolate); - Generate(Token::ADD, INT32, SMI, INT32, OVERWRITE_LEFT, isolate); - Generate(Token::ADD, INT32, SMI, INT32, OVERWRITE_RIGHT, isolate); - Generate(Token::ADD, NUMBER, INT32, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::ADD, NUMBER, INT32, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::ADD, NUMBER, INT32, NUMBER, OVERWRITE_RIGHT, isolate); - Generate(Token::ADD, NUMBER, NUMBER, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::ADD, NUMBER, NUMBER, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::ADD, NUMBER, NUMBER, NUMBER, OVERWRITE_RIGHT, isolate); - Generate(Token::ADD, NUMBER, SMI, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::ADD, NUMBER, SMI, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::ADD, NUMBER, SMI, NUMBER, OVERWRITE_RIGHT, isolate); - Generate(Token::ADD, SMI, INT32, INT32, NO_OVERWRITE, isolate); - Generate(Token::ADD, SMI, INT32, INT32, OVERWRITE_LEFT, isolate); - Generate(Token::ADD, SMI, INT32, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::ADD, SMI, NUMBER, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::ADD, SMI, NUMBER, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::ADD, SMI, NUMBER, NUMBER, OVERWRITE_RIGHT, isolate); - Generate(Token::ADD, SMI, SMI, INT32, OVERWRITE_LEFT, isolate); - Generate(Token::ADD, SMI, SMI, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::BIT_AND, INT32, INT32, INT32, NO_OVERWRITE, isolate); - Generate(Token::BIT_AND, INT32, INT32, INT32, OVERWRITE_LEFT, isolate); - Generate(Token::BIT_AND, INT32, INT32, INT32, OVERWRITE_RIGHT, isolate); - Generate(Token::BIT_AND, INT32, INT32, SMI, NO_OVERWRITE, isolate); - Generate(Token::BIT_AND, INT32, INT32, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::BIT_AND, INT32, SMI, INT32, NO_OVERWRITE, isolate); - Generate(Token::BIT_AND, INT32, SMI, INT32, OVERWRITE_RIGHT, isolate); - Generate(Token::BIT_AND, INT32, SMI, SMI, NO_OVERWRITE, isolate); - Generate(Token::BIT_AND, INT32, SMI, SMI, OVERWRITE_LEFT, isolate); - Generate(Token::BIT_AND, INT32, SMI, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::BIT_AND, NUMBER, INT32, INT32, OVERWRITE_RIGHT, isolate); - Generate(Token::BIT_AND, NUMBER, SMI, SMI, NO_OVERWRITE, isolate); - Generate(Token::BIT_AND, NUMBER, SMI, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::BIT_AND, SMI, INT32, INT32, NO_OVERWRITE, isolate); - Generate(Token::BIT_AND, SMI, INT32, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::BIT_AND, SMI, NUMBER, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::BIT_AND, SMI, SMI, SMI, NO_OVERWRITE, isolate); - Generate(Token::BIT_AND, SMI, SMI, SMI, OVERWRITE_LEFT, isolate); - Generate(Token::BIT_AND, SMI, SMI, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::BIT_OR, INT32, INT32, INT32, OVERWRITE_LEFT, isolate); - Generate(Token::BIT_OR, INT32, INT32, INT32, OVERWRITE_RIGHT, isolate); - Generate(Token::BIT_OR, INT32, INT32, SMI, OVERWRITE_LEFT, isolate); - Generate(Token::BIT_OR, INT32, SMI, INT32, NO_OVERWRITE, isolate); - Generate(Token::BIT_OR, INT32, SMI, INT32, OVERWRITE_LEFT, isolate); - Generate(Token::BIT_OR, INT32, SMI, INT32, OVERWRITE_RIGHT, isolate); - Generate(Token::BIT_OR, INT32, SMI, SMI, NO_OVERWRITE, isolate); - Generate(Token::BIT_OR, INT32, SMI, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::BIT_OR, NUMBER, SMI, INT32, NO_OVERWRITE, isolate); - Generate(Token::BIT_OR, NUMBER, SMI, INT32, OVERWRITE_LEFT, isolate); - Generate(Token::BIT_OR, NUMBER, SMI, INT32, OVERWRITE_RIGHT, isolate); - Generate(Token::BIT_OR, NUMBER, SMI, SMI, NO_OVERWRITE, isolate); - Generate(Token::BIT_OR, NUMBER, SMI, SMI, OVERWRITE_LEFT, isolate); - Generate(Token::BIT_OR, SMI, INT32, INT32, OVERWRITE_LEFT, isolate); - Generate(Token::BIT_OR, SMI, INT32, INT32, OVERWRITE_RIGHT, isolate); - Generate(Token::BIT_OR, SMI, INT32, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::BIT_OR, SMI, SMI, SMI, OVERWRITE_LEFT, isolate); - Generate(Token::BIT_OR, SMI, SMI, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::BIT_XOR, INT32, INT32, INT32, NO_OVERWRITE, isolate); - Generate(Token::BIT_XOR, INT32, INT32, INT32, OVERWRITE_LEFT, isolate); - Generate(Token::BIT_XOR, INT32, INT32, INT32, OVERWRITE_RIGHT, isolate); - Generate(Token::BIT_XOR, INT32, INT32, SMI, NO_OVERWRITE, isolate); - Generate(Token::BIT_XOR, INT32, INT32, SMI, OVERWRITE_LEFT, isolate); - Generate(Token::BIT_XOR, INT32, NUMBER, SMI, NO_OVERWRITE, isolate); - Generate(Token::BIT_XOR, INT32, SMI, INT32, NO_OVERWRITE, isolate); - Generate(Token::BIT_XOR, INT32, SMI, INT32, OVERWRITE_LEFT, isolate); - Generate(Token::BIT_XOR, INT32, SMI, INT32, OVERWRITE_RIGHT, isolate); - Generate(Token::BIT_XOR, NUMBER, INT32, INT32, NO_OVERWRITE, isolate); - Generate(Token::BIT_XOR, NUMBER, SMI, INT32, NO_OVERWRITE, isolate); - Generate(Token::BIT_XOR, NUMBER, SMI, SMI, NO_OVERWRITE, isolate); - Generate(Token::BIT_XOR, SMI, INT32, INT32, NO_OVERWRITE, isolate); - Generate(Token::BIT_XOR, SMI, INT32, INT32, OVERWRITE_LEFT, isolate); - Generate(Token::BIT_XOR, SMI, INT32, SMI, OVERWRITE_LEFT, isolate); - Generate(Token::BIT_XOR, SMI, SMI, SMI, NO_OVERWRITE, isolate); - Generate(Token::BIT_XOR, SMI, SMI, SMI, OVERWRITE_LEFT, isolate); - Generate(Token::BIT_XOR, SMI, SMI, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::DIV, INT32, INT32, INT32, NO_OVERWRITE, isolate); - Generate(Token::DIV, INT32, INT32, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::DIV, INT32, NUMBER, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::DIV, INT32, NUMBER, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::DIV, INT32, SMI, INT32, NO_OVERWRITE, isolate); - Generate(Token::DIV, INT32, SMI, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::DIV, NUMBER, INT32, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::DIV, NUMBER, INT32, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::DIV, NUMBER, NUMBER, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::DIV, NUMBER, NUMBER, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::DIV, NUMBER, NUMBER, NUMBER, OVERWRITE_RIGHT, isolate); - Generate(Token::DIV, NUMBER, SMI, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::DIV, NUMBER, SMI, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::DIV, SMI, INT32, INT32, NO_OVERWRITE, isolate); - Generate(Token::DIV, SMI, INT32, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::DIV, SMI, INT32, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::DIV, SMI, NUMBER, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::DIV, SMI, NUMBER, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::DIV, SMI, NUMBER, NUMBER, OVERWRITE_RIGHT, isolate); - Generate(Token::DIV, SMI, SMI, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::DIV, SMI, SMI, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::DIV, SMI, SMI, NUMBER, OVERWRITE_RIGHT, isolate); - Generate(Token::DIV, SMI, SMI, SMI, NO_OVERWRITE, isolate); - Generate(Token::DIV, SMI, SMI, SMI, OVERWRITE_LEFT, isolate); - Generate(Token::DIV, SMI, SMI, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::MOD, NUMBER, SMI, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::MOD, SMI, 16, SMI, OVERWRITE_LEFT, isolate); - Generate(Token::MOD, SMI, 2, SMI, NO_OVERWRITE, isolate); - Generate(Token::MOD, SMI, 2048, SMI, NO_OVERWRITE, isolate); - Generate(Token::MOD, SMI, 32, SMI, NO_OVERWRITE, isolate); - Generate(Token::MOD, SMI, 4, SMI, NO_OVERWRITE, isolate); - Generate(Token::MOD, SMI, 4, SMI, OVERWRITE_LEFT, isolate); - Generate(Token::MOD, SMI, 8, SMI, NO_OVERWRITE, isolate); - Generate(Token::MOD, SMI, SMI, SMI, NO_OVERWRITE, isolate); - Generate(Token::MOD, SMI, SMI, SMI, OVERWRITE_LEFT, isolate); - Generate(Token::MUL, INT32, INT32, INT32, NO_OVERWRITE, isolate); - Generate(Token::MUL, INT32, INT32, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::MUL, INT32, NUMBER, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::MUL, INT32, NUMBER, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::MUL, INT32, SMI, INT32, NO_OVERWRITE, isolate); - Generate(Token::MUL, INT32, SMI, INT32, OVERWRITE_LEFT, isolate); - Generate(Token::MUL, INT32, SMI, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::MUL, NUMBER, INT32, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::MUL, NUMBER, INT32, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::MUL, NUMBER, INT32, NUMBER, OVERWRITE_RIGHT, isolate); - Generate(Token::MUL, NUMBER, NUMBER, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::MUL, NUMBER, NUMBER, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::MUL, NUMBER, SMI, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::MUL, NUMBER, SMI, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::MUL, NUMBER, SMI, NUMBER, OVERWRITE_RIGHT, isolate); - Generate(Token::MUL, SMI, INT32, INT32, NO_OVERWRITE, isolate); - Generate(Token::MUL, SMI, INT32, INT32, OVERWRITE_LEFT, isolate); - Generate(Token::MUL, SMI, INT32, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::MUL, SMI, NUMBER, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::MUL, SMI, NUMBER, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::MUL, SMI, NUMBER, NUMBER, OVERWRITE_RIGHT, isolate); - Generate(Token::MUL, SMI, SMI, INT32, NO_OVERWRITE, isolate); - Generate(Token::MUL, SMI, SMI, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::MUL, SMI, SMI, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::MUL, SMI, SMI, SMI, NO_OVERWRITE, isolate); - Generate(Token::MUL, SMI, SMI, SMI, OVERWRITE_LEFT, isolate); - Generate(Token::MUL, SMI, SMI, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::SAR, INT32, SMI, INT32, OVERWRITE_RIGHT, isolate); - Generate(Token::SAR, INT32, SMI, SMI, NO_OVERWRITE, isolate); - Generate(Token::SAR, INT32, SMI, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::SAR, NUMBER, SMI, SMI, NO_OVERWRITE, isolate); - Generate(Token::SAR, NUMBER, SMI, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::SAR, SMI, SMI, SMI, OVERWRITE_LEFT, isolate); - Generate(Token::SAR, SMI, SMI, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::SHL, INT32, SMI, INT32, NO_OVERWRITE, isolate); - Generate(Token::SHL, INT32, SMI, INT32, OVERWRITE_RIGHT, isolate); - Generate(Token::SHL, INT32, SMI, SMI, NO_OVERWRITE, isolate); - Generate(Token::SHL, INT32, SMI, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::SHL, NUMBER, SMI, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::SHL, SMI, SMI, INT32, NO_OVERWRITE, isolate); - Generate(Token::SHL, SMI, SMI, INT32, OVERWRITE_LEFT, isolate); - Generate(Token::SHL, SMI, SMI, INT32, OVERWRITE_RIGHT, isolate); - Generate(Token::SHL, SMI, SMI, SMI, NO_OVERWRITE, isolate); - Generate(Token::SHL, SMI, SMI, SMI, OVERWRITE_LEFT, isolate); - Generate(Token::SHL, SMI, SMI, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::SHR, INT32, SMI, SMI, NO_OVERWRITE, isolate); - Generate(Token::SHR, INT32, SMI, SMI, OVERWRITE_LEFT, isolate); - Generate(Token::SHR, INT32, SMI, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::SHR, NUMBER, SMI, SMI, NO_OVERWRITE, isolate); - Generate(Token::SHR, NUMBER, SMI, SMI, OVERWRITE_LEFT, isolate); - Generate(Token::SHR, NUMBER, SMI, INT32, OVERWRITE_RIGHT, isolate); - Generate(Token::SHR, SMI, SMI, SMI, NO_OVERWRITE, isolate); - Generate(Token::SHR, SMI, SMI, SMI, OVERWRITE_LEFT, isolate); - Generate(Token::SHR, SMI, SMI, SMI, OVERWRITE_RIGHT, isolate); - Generate(Token::SUB, INT32, INT32, INT32, NO_OVERWRITE, isolate); - Generate(Token::SUB, INT32, INT32, INT32, OVERWRITE_LEFT, isolate); - Generate(Token::SUB, INT32, NUMBER, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::SUB, INT32, NUMBER, NUMBER, OVERWRITE_RIGHT, isolate); - Generate(Token::SUB, INT32, SMI, INT32, OVERWRITE_LEFT, isolate); - Generate(Token::SUB, INT32, SMI, INT32, OVERWRITE_RIGHT, isolate); - Generate(Token::SUB, NUMBER, INT32, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::SUB, NUMBER, INT32, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::SUB, NUMBER, NUMBER, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::SUB, NUMBER, NUMBER, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::SUB, NUMBER, NUMBER, NUMBER, OVERWRITE_RIGHT, isolate); - Generate(Token::SUB, NUMBER, SMI, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::SUB, NUMBER, SMI, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::SUB, NUMBER, SMI, NUMBER, OVERWRITE_RIGHT, isolate); - Generate(Token::SUB, SMI, INT32, INT32, NO_OVERWRITE, isolate); - Generate(Token::SUB, SMI, NUMBER, NUMBER, NO_OVERWRITE, isolate); - Generate(Token::SUB, SMI, NUMBER, NUMBER, OVERWRITE_LEFT, isolate); - Generate(Token::SUB, SMI, NUMBER, NUMBER, OVERWRITE_RIGHT, isolate); - Generate(Token::SUB, SMI, SMI, SMI, NO_OVERWRITE, isolate); - Generate(Token::SUB, SMI, SMI, SMI, OVERWRITE_LEFT, isolate); - Generate(Token::SUB, SMI, SMI, SMI, OVERWRITE_RIGHT, isolate); -} - - -bool BinaryOpStub::can_encode_arg_value(int32_t value) const { - return op_ == Token::MOD && value > 0 && IsPowerOf2(value) && - FixedRightArgValueBits::is_valid(WhichPowerOf2(value)); -} - - -int BinaryOpStub::encode_arg_value(int32_t value) const { - ASSERT(can_encode_arg_value(value)); - return WhichPowerOf2(value); -} - - -int32_t BinaryOpStub::decode_arg_value(int value) const { - return 1 << value; -} - - -int BinaryOpStub::encode_token(Token::Value op) const { - ASSERT(op >= FIRST_TOKEN && op <= LAST_TOKEN); - return op - FIRST_TOKEN; -} - - -Token::Value BinaryOpStub::decode_token(int op) const { - int res = op + FIRST_TOKEN; - ASSERT(res >= FIRST_TOKEN && res <= LAST_TOKEN); - return static_cast(res); -} - - -const char* BinaryOpStub::StateToName(State state) { - switch (state) { - case NONE: - return "None"; - case SMI: - return "Smi"; - case INT32: - return "Int32"; - case NUMBER: - return "Number"; - case STRING: - return "String"; - case GENERIC: - return "Generic"; - } - return ""; -} - - -void BinaryOpStub::UpdateStatus(Handle left, - Handle right, - Maybe > result) { - int old_state = GetExtraICState(); - - UpdateStatus(left, &left_state_); - UpdateStatus(right, &right_state_); - - int32_t value; - bool new_has_fixed_right_arg = - right->ToInt32(&value) && can_encode_arg_value(value) && - (left_state_ == SMI || left_state_ == INT32) && - (result_state_ == NONE || !fixed_right_arg_.has_value); - - fixed_right_arg_ = Maybe(new_has_fixed_right_arg, value); - - if (result.has_value) UpdateStatus(result.value, &result_state_); - - State max_input = Max(left_state_, right_state_); - - if (!has_int_result() && op_ != Token::SHR && - max_input <= NUMBER && max_input > result_state_) { - result_state_ = max_input; - } - - ASSERT(result_state_ <= (has_int_result() ? INT32 : NUMBER) || - op_ == Token::ADD); - - // Reset overwrite mode unless we can actually make use of it, or may be able - // to make use of it at some point in the future. - if ((mode_ == OVERWRITE_LEFT && left_state_ > NUMBER) || - (mode_ == OVERWRITE_RIGHT && right_state_ > NUMBER) || - result_state_ > NUMBER) { - mode_ = NO_OVERWRITE; - } - - if (old_state == GetExtraICState()) { - // Tagged operations can lead to non-truncating HChanges - if (left->IsUndefined() || left->IsBoolean()) { - left_state_ = GENERIC; - } else if (right->IsUndefined() || right->IsBoolean()) { - right_state_ = GENERIC; - } else { - // Since the fpu is to precise, we might bail out on numbers which - // actually would truncate with 64 bit precision. - ASSERT(!CpuFeatures::IsSupported(SSE2) && - result_state_ <= INT32); - result_state_ = NUMBER; +// static +void BinaryOpICStub::GenerateAheadOfTime(Isolate* isolate) { + // Generate the uninitialized versions of the stub. + for (int op = Token::BIT_OR; op <= Token::MOD; ++op) { + for (int mode = NO_OVERWRITE; mode <= OVERWRITE_RIGHT; ++mode) { + BinaryOpICStub stub(static_cast(op), + static_cast(mode)); + stub.GetCode(isolate); } } -} - - -void BinaryOpStub::UpdateStatus(Handle object, - State* state) { - bool is_truncating = (op_ == Token::BIT_AND || op_ == Token::BIT_OR || - op_ == Token::BIT_XOR || op_ == Token::SAR || - op_ == Token::SHL || op_ == Token::SHR); - v8::internal::TypeInfo type = v8::internal::TypeInfo::FromValue(object); - if (object->IsBoolean() && is_truncating) { - // Booleans are converted by truncating by HChange. - type = TypeInfo::Integer32(); - } - if (object->IsUndefined()) { - // Undefined will be automatically truncated for us by HChange. - type = is_truncating ? TypeInfo::Integer32() : TypeInfo::Double(); - } - State int_state = SmiValuesAre32Bits() ? NUMBER : INT32; - State new_state = NONE; - if (type.IsSmi()) { - new_state = SMI; - } else if (type.IsInteger32()) { - new_state = int_state; - } else if (type.IsNumber()) { - new_state = NUMBER; - } else if (object->IsString() && operation() == Token::ADD) { - new_state = STRING; - } else { - new_state = GENERIC; - } - if ((new_state <= NUMBER && *state > NUMBER) || - (new_state > NUMBER && *state <= NUMBER && *state != NONE)) { - new_state = GENERIC; - } - *state = Max(*state, new_state); -} - - -Handle BinaryOpStub::StateToType(State state, - Isolate* isolate) { - Handle t = handle(Type::None(), isolate); - switch (state) { - case NUMBER: - t = handle(Type::Union(t, handle(Type::Double(), isolate)), isolate); - // Fall through. - case INT32: - t = handle(Type::Union(t, handle(Type::Signed32(), isolate)), isolate); - // Fall through. - case SMI: - t = handle(Type::Union(t, handle(Type::Smi(), isolate)), isolate); - break; - case STRING: - t = handle(Type::Union(t, handle(Type::String(), isolate)), isolate); - break; - case GENERIC: - return handle(Type::Any(), isolate); - break; - case NONE: - break; - } - return t; + // Generate special versions of the stub. + BinaryOpIC::State::GenerateAheadOfTime(isolate, &GenerateAheadOfTime); } -Handle BinaryOpStub::GetLeftType(Isolate* isolate) const { - return StateToType(left_state_, isolate); -} - - -Handle BinaryOpStub::GetRightType(Isolate* isolate) const { - return StateToType(right_state_, isolate); +void BinaryOpICStub::PrintState(StringStream* stream) { + state_.Print(stream); } -Handle BinaryOpStub::GetResultType(Isolate* isolate) const { - if (HasSideEffects(isolate)) return StateToType(NONE, isolate); - if (result_state_ == GENERIC && op_ == Token::ADD) { - return handle(Type::Union(handle(Type::Number(), isolate), - handle(Type::String(), isolate)), isolate); - } - ASSERT(result_state_ != GENERIC); - if (result_state_ == NUMBER && op_ == Token::SHR) { - return handle(Type::Unsigned32(), isolate); - } - return StateToType(result_state_, isolate); +// static +void BinaryOpICStub::GenerateAheadOfTime(Isolate* isolate, + const BinaryOpIC::State& state) { + BinaryOpICStub stub(state); + stub.GetCode(isolate); } @@ -1166,6 +719,13 @@ void FastNewClosureStub::InstallDescriptors(Isolate* isolate) { // static +void BinaryOpICStub::InstallDescriptors(Isolate* isolate) { + BinaryOpICStub stub(Token::ADD, NO_OVERWRITE); + InstallDescriptor(isolate, &stub); +} + + +// static void NewStringAddStub::InstallDescriptors(Isolate* isolate) { NewStringAddStub stub(STRING_ADD_CHECK_NONE, NOT_TENURED); InstallDescriptor(isolate, &stub); diff --git a/src/code-stubs.h b/src/code-stubs.h index a852a5c..f2de616 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -41,7 +41,7 @@ namespace internal { #define CODE_STUB_LIST_ALL_PLATFORMS(V) \ V(CallFunction) \ V(CallConstruct) \ - V(BinaryOp) \ + V(BinaryOpIC) \ V(StringAdd) \ V(NewStringAdd) \ V(SubString) \ @@ -126,9 +126,6 @@ namespace internal { CODE_STUB_LIST_ARM(V) \ CODE_STUB_LIST_MIPS(V) -// Mode to overwrite BinaryExpression values. -enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT }; - // Stub is base classes of all stubs. class CodeStub BASE_EMBEDDED { public: @@ -1061,177 +1058,56 @@ class KeyedArrayCallStub: public HICStub { }; -class BinaryOpStub: public HydrogenCodeStub { +class BinaryOpICStub V8_FINAL : public HydrogenCodeStub { public: - BinaryOpStub(Token::Value op, OverwriteMode mode) - : HydrogenCodeStub(UNINITIALIZED), op_(op), mode_(mode) { - ASSERT(op <= LAST_TOKEN && op >= FIRST_TOKEN); - Initialize(); - } + BinaryOpICStub(Token::Value op, OverwriteMode mode) + : HydrogenCodeStub(UNINITIALIZED), state_(op, mode) {} - explicit BinaryOpStub(ExtraICState state) - : op_(decode_token(OpBits::decode(state))), - mode_(OverwriteModeField::decode(state)), - fixed_right_arg_( - Maybe(HasFixedRightArgBits::decode(state), - decode_arg_value(FixedRightArgValueBits::decode(state)))), - left_state_(LeftStateField::decode(state)), - right_state_(fixed_right_arg_.has_value - ? ((fixed_right_arg_.value <= Smi::kMaxValue) ? SMI : INT32) - : RightStateField::decode(state)), - result_state_(ResultStateField::decode(state)) { - // We don't deserialize the SSE2 Field, since this is only used to be able - // to include SSE2 as well as non-SSE2 versions in the snapshot. For code - // generation we always want it to reflect the current state. - ASSERT(!fixed_right_arg_.has_value || - can_encode_arg_value(fixed_right_arg_.value)); - } - - static const int FIRST_TOKEN = Token::BIT_OR; - static const int LAST_TOKEN = Token::MOD; + explicit BinaryOpICStub(const BinaryOpIC::State& state) : state_(state) {} static void GenerateAheadOfTime(Isolate* isolate); + virtual void InitializeInterfaceDescriptor( - Isolate* isolate, CodeStubInterfaceDescriptor* descriptor); - static void InitializeForIsolate(Isolate* isolate) { - BinaryOpStub binopStub(UNINITIALIZED); - binopStub.InitializeInterfaceDescriptor( - isolate, isolate->code_stub_interface_descriptor(CodeStub::BinaryOp)); - } + Isolate* isolate, CodeStubInterfaceDescriptor* descriptor) V8_OVERRIDE; - virtual Code::Kind GetCodeKind() const { return Code::BINARY_OP_IC; } - virtual InlineCacheState GetICState() { - if (Max(left_state_, right_state_) == NONE) { - return ::v8::internal::UNINITIALIZED; - } - if (Max(left_state_, right_state_) == GENERIC) return MEGAMORPHIC; - return MONOMORPHIC; - } + static void InstallDescriptors(Isolate* isolate); - virtual void VerifyPlatformFeatures(Isolate* isolate) V8_OVERRIDE { - ASSERT(CpuFeatures::VerifyCrossCompiling(SSE2)); + virtual Code::Kind GetCodeKind() const V8_OVERRIDE { + return Code::BINARY_OP_IC; } - virtual ExtraICState GetExtraICState() { - bool sse_field = Max(result_state_, Max(left_state_, right_state_)) > SMI && - CpuFeatures::IsSafeForSnapshot(SSE2); - - return OpBits::encode(encode_token(op_)) - | LeftStateField::encode(left_state_) - | RightStateField::encode(fixed_right_arg_.has_value - ? NONE : right_state_) - | ResultStateField::encode(result_state_) - | HasFixedRightArgBits::encode(fixed_right_arg_.has_value) - | FixedRightArgValueBits::encode(fixed_right_arg_.has_value - ? encode_arg_value( - fixed_right_arg_.value) - : 0) - | SSE2Field::encode(sse_field) - | OverwriteModeField::encode(mode_); + virtual InlineCacheState GetICState() V8_OVERRIDE { + return state_.GetICState(); } - bool CanReuseDoubleBox() { - return result_state_ <= NUMBER && result_state_ > SMI && - ((left_state_ > SMI && left_state_ <= NUMBER && - mode_ == OVERWRITE_LEFT) || - (right_state_ > SMI && right_state_ <= NUMBER && - mode_ == OVERWRITE_RIGHT)); + virtual ExtraICState GetExtraICState() V8_OVERRIDE { + return state_.GetExtraICState(); } - bool HasSideEffects(Isolate* isolate) const { - Handle left = GetLeftType(isolate); - Handle right = GetRightType(isolate); - return left->Maybe(Type::Receiver()) || right->Maybe(Type::Receiver()); + virtual void VerifyPlatformFeatures(Isolate* isolate) V8_OVERRIDE { + ASSERT(CpuFeatures::VerifyCrossCompiling(SSE2)); } - virtual Handle GenerateCode(Isolate* isolate); - - Maybe > Result(Handle left, - Handle right, - Isolate* isolate); - - Token::Value operation() const { return op_; } - OverwriteMode mode() const { return mode_; } - Maybe fixed_right_arg() const { return fixed_right_arg_; } + virtual Handle GenerateCode(Isolate* isolate) V8_OVERRIDE; - Handle GetLeftType(Isolate* isolate) const; - Handle GetRightType(Isolate* isolate) const; - Handle GetResultType(Isolate* isolate) const; + const BinaryOpIC::State& state() const { return state_; } - void UpdateStatus(Handle left, - Handle right, - Maybe > result); + virtual void PrintState(StringStream* stream) V8_OVERRIDE; - void PrintState(StringStream* stream); + // Parameters accessed via CodeStubGraphBuilder::GetParameter() + static const int kLeft = 0; + static const int kRight = 1; private: - explicit BinaryOpStub(InitializationState state) : HydrogenCodeStub(state), - op_(Token::ADD), - mode_(NO_OVERWRITE) { - Initialize(); - } - void Initialize(); - - enum State { NONE, SMI, INT32, NUMBER, STRING, GENERIC }; - - // We truncate the last bit of the token. - STATIC_ASSERT(LAST_TOKEN - FIRST_TOKEN < (1 << 5)); - class LeftStateField: public BitField {}; - // When fixed right arg is set, we don't need to store the right state. - // Thus the two fields can overlap. - class HasFixedRightArgBits: public BitField {}; - class FixedRightArgValueBits: public BitField {}; - class RightStateField: public BitField {}; - class ResultStateField: public BitField {}; - class SSE2Field: public BitField {}; - class OverwriteModeField: public BitField {}; - class OpBits: public BitField {}; - - virtual CodeStub::Major MajorKey() { return BinaryOp; } - virtual int NotMissMinorKey() { return GetExtraICState(); } - - static Handle StateToType(State state, - Isolate* isolate); - - static void Generate(Token::Value op, - State left, - int right, - State result, - OverwriteMode mode, - Isolate* isolate); - - static void Generate(Token::Value op, - State left, - State right, - State result, - OverwriteMode mode, - Isolate* isolate); + static void GenerateAheadOfTime(Isolate* isolate, + const BinaryOpIC::State& state); - void UpdateStatus(Handle object, - State* state); + virtual Major MajorKey() V8_OVERRIDE { return BinaryOpIC; } + virtual int NotMissMinorKey() V8_OVERRIDE { return GetExtraICState(); } - bool can_encode_arg_value(int32_t value) const; - int encode_arg_value(int32_t value) const; - int32_t decode_arg_value(int value) const; - int encode_token(Token::Value op) const; - Token::Value decode_token(int op) const; - - bool has_int_result() const { - return op_ == Token::BIT_XOR || op_ == Token::BIT_AND || - op_ == Token::BIT_OR || op_ == Token::SAR || op_ == Token::SHL; - } - - const char* StateToName(State state); - - void PrintBaseName(StringStream* stream); - - Token::Value op_; - OverwriteMode mode_; + BinaryOpIC::State state_; - Maybe fixed_right_arg_; - State left_state_; - State right_state_; - State result_state_; + DISALLOW_COPY_AND_ASSIGN(BinaryOpICStub); }; diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index f0dcf71..0481814 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -330,7 +330,7 @@ void ElementsTransitionAndStoreStub::InitializeInterfaceDescriptor( } -void BinaryOpStub::InitializeInterfaceDescriptor( +void BinaryOpICStub::InitializeInterfaceDescriptor( Isolate* isolate, CodeStubInterfaceDescriptor* descriptor) { static Register registers[] = { edx, eax }; @@ -2961,9 +2961,9 @@ void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) { CreateAllocationSiteStub::GenerateAheadOfTime(isolate); if (Serializer::enabled()) { PlatformFeatureScope sse2(SSE2); - BinaryOpStub::GenerateAheadOfTime(isolate); + BinaryOpICStub::GenerateAheadOfTime(isolate); } else { - BinaryOpStub::GenerateAheadOfTime(isolate); + BinaryOpICStub::GenerateAheadOfTime(isolate); } } diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index 649a58b..3c5d4aa 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -2245,7 +2245,7 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr, __ bind(&stub_call); __ mov(eax, ecx); - BinaryOpStub stub(op, mode); + BinaryOpICStub stub(op, mode); CallIC(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, expr->BinaryOperationFeedbackId()); patch_site.EmitPatchInfo(); @@ -2330,7 +2330,7 @@ void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr, Token::Value op, OverwriteMode mode) { __ pop(edx); - BinaryOpStub stub(op, mode); + BinaryOpICStub stub(op, mode); JumpPatchSite patch_site(masm_); // unbound, signals no inlined smi code. CallIC(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, expr->BinaryOperationFeedbackId()); @@ -4414,7 +4414,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { __ bind(&stub_call); __ mov(edx, eax); __ mov(eax, Immediate(Smi::FromInt(1))); - BinaryOpStub stub(expr->binary_op(), NO_OVERWRITE); + BinaryOpICStub stub(expr->binary_op(), NO_OVERWRITE); CallIC(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, expr->CountBinOpFeedbackId()); diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index d3a5ff3..f26eeac 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -2356,7 +2356,7 @@ void LCodeGen::DoArithmeticT(LArithmeticT* instr) { ASSERT(ToRegister(instr->right()).is(eax)); ASSERT(ToRegister(instr->result()).is(eax)); - BinaryOpStub stub(instr->op(), NO_OVERWRITE); + BinaryOpICStub stub(instr->op(), NO_OVERWRITE); CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); __ nop(); // Signals no inlined code. } diff --git a/src/ic.cc b/src/ic.cc index 33ce188..1ec9728 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -2317,76 +2317,464 @@ RUNTIME_FUNCTION(MaybeObject*, ElementsTransitionAndStoreIC_Miss) { } -const char* BinaryOpIC::GetName(TypeInfo type_info) { - switch (type_info) { - case UNINITIALIZED: return "Uninitialized"; +BinaryOpIC::State::State(ExtraICState extra_ic_state) { + // We don't deserialize the SSE2 Field, since this is only used to be able + // to include SSE2 as well as non-SSE2 versions in the snapshot. For code + // generation we always want it to reflect the current state. + op_ = static_cast( + FIRST_TOKEN + OpField::decode(extra_ic_state)); + mode_ = OverwriteModeField::decode(extra_ic_state); + fixed_right_arg_ = Maybe( + HasFixedRightArgField::decode(extra_ic_state), + 1 << FixedRightArgValueField::decode(extra_ic_state)); + left_kind_ = LeftKindField::decode(extra_ic_state); + if (fixed_right_arg_.has_value) { + right_kind_ = Smi::IsValid(fixed_right_arg_.value) ? SMI : INT32; + } else { + right_kind_ = RightKindField::decode(extra_ic_state); + } + result_kind_ = ResultKindField::decode(extra_ic_state); + ASSERT_LE(FIRST_TOKEN, op_); + ASSERT_LE(op_, LAST_TOKEN); +} + + +ExtraICState BinaryOpIC::State::GetExtraICState() const { + bool sse2 = (Max(result_kind_, Max(left_kind_, right_kind_)) > SMI && + CpuFeatures::IsSafeForSnapshot(SSE2)); + ExtraICState extra_ic_state = + SSE2Field::encode(sse2) | + OpField::encode(op_ - FIRST_TOKEN) | + OverwriteModeField::encode(mode_) | + LeftKindField::encode(left_kind_) | + ResultKindField::encode(result_kind_) | + HasFixedRightArgField::encode(fixed_right_arg_.has_value); + if (fixed_right_arg_.has_value) { + extra_ic_state = FixedRightArgValueField::update( + extra_ic_state, WhichPowerOf2(fixed_right_arg_.value)); + } else { + extra_ic_state = RightKindField::update(extra_ic_state, right_kind_); + } + return extra_ic_state; +} + + +// static +void BinaryOpIC::State::GenerateAheadOfTime( + Isolate* isolate, void (*Generate)(Isolate*, const State&)) { + // TODO(olivf) We should investigate why adding stubs to the snapshot is so + // expensive at runtime. When solved we should be able to add most binops to + // the snapshot instead of hand-picking them. + // Generated list of commonly used stubs +#define GENERATE(op, left_kind, right_kind, result_kind, mode) \ + do { \ + State state(op, mode); \ + state.left_kind_ = left_kind; \ + state.fixed_right_arg_.has_value = false; \ + state.right_kind_ = right_kind; \ + state.result_kind_ = result_kind; \ + Generate(isolate, state); \ + } while (false) + GENERATE(Token::ADD, INT32, INT32, INT32, NO_OVERWRITE); + GENERATE(Token::ADD, INT32, INT32, INT32, OVERWRITE_LEFT); + GENERATE(Token::ADD, INT32, INT32, NUMBER, NO_OVERWRITE); + GENERATE(Token::ADD, INT32, INT32, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::ADD, INT32, NUMBER, NUMBER, NO_OVERWRITE); + GENERATE(Token::ADD, INT32, NUMBER, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::ADD, INT32, NUMBER, NUMBER, OVERWRITE_RIGHT); + GENERATE(Token::ADD, INT32, SMI, INT32, NO_OVERWRITE); + GENERATE(Token::ADD, INT32, SMI, INT32, OVERWRITE_LEFT); + GENERATE(Token::ADD, INT32, SMI, INT32, OVERWRITE_RIGHT); + GENERATE(Token::ADD, NUMBER, INT32, NUMBER, NO_OVERWRITE); + GENERATE(Token::ADD, NUMBER, INT32, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::ADD, NUMBER, INT32, NUMBER, OVERWRITE_RIGHT); + GENERATE(Token::ADD, NUMBER, NUMBER, NUMBER, NO_OVERWRITE); + GENERATE(Token::ADD, NUMBER, NUMBER, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::ADD, NUMBER, NUMBER, NUMBER, OVERWRITE_RIGHT); + GENERATE(Token::ADD, NUMBER, SMI, NUMBER, NO_OVERWRITE); + GENERATE(Token::ADD, NUMBER, SMI, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::ADD, NUMBER, SMI, NUMBER, OVERWRITE_RIGHT); + GENERATE(Token::ADD, SMI, INT32, INT32, NO_OVERWRITE); + GENERATE(Token::ADD, SMI, INT32, INT32, OVERWRITE_LEFT); + GENERATE(Token::ADD, SMI, INT32, NUMBER, NO_OVERWRITE); + GENERATE(Token::ADD, SMI, NUMBER, NUMBER, NO_OVERWRITE); + GENERATE(Token::ADD, SMI, NUMBER, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::ADD, SMI, NUMBER, NUMBER, OVERWRITE_RIGHT); + GENERATE(Token::ADD, SMI, SMI, INT32, OVERWRITE_LEFT); + GENERATE(Token::ADD, SMI, SMI, SMI, OVERWRITE_RIGHT); + GENERATE(Token::BIT_AND, INT32, INT32, INT32, NO_OVERWRITE); + GENERATE(Token::BIT_AND, INT32, INT32, INT32, OVERWRITE_LEFT); + GENERATE(Token::BIT_AND, INT32, INT32, INT32, OVERWRITE_RIGHT); + GENERATE(Token::BIT_AND, INT32, INT32, SMI, NO_OVERWRITE); + GENERATE(Token::BIT_AND, INT32, INT32, SMI, OVERWRITE_RIGHT); + GENERATE(Token::BIT_AND, INT32, SMI, INT32, NO_OVERWRITE); + GENERATE(Token::BIT_AND, INT32, SMI, INT32, OVERWRITE_RIGHT); + GENERATE(Token::BIT_AND, INT32, SMI, SMI, NO_OVERWRITE); + GENERATE(Token::BIT_AND, INT32, SMI, SMI, OVERWRITE_LEFT); + GENERATE(Token::BIT_AND, INT32, SMI, SMI, OVERWRITE_RIGHT); + GENERATE(Token::BIT_AND, NUMBER, INT32, INT32, OVERWRITE_RIGHT); + GENERATE(Token::BIT_AND, NUMBER, SMI, SMI, NO_OVERWRITE); + GENERATE(Token::BIT_AND, NUMBER, SMI, SMI, OVERWRITE_RIGHT); + GENERATE(Token::BIT_AND, SMI, INT32, INT32, NO_OVERWRITE); + GENERATE(Token::BIT_AND, SMI, INT32, SMI, OVERWRITE_RIGHT); + GENERATE(Token::BIT_AND, SMI, NUMBER, SMI, OVERWRITE_RIGHT); + GENERATE(Token::BIT_AND, SMI, SMI, SMI, NO_OVERWRITE); + GENERATE(Token::BIT_AND, SMI, SMI, SMI, OVERWRITE_LEFT); + GENERATE(Token::BIT_AND, SMI, SMI, SMI, OVERWRITE_RIGHT); + GENERATE(Token::BIT_OR, INT32, INT32, INT32, OVERWRITE_LEFT); + GENERATE(Token::BIT_OR, INT32, INT32, INT32, OVERWRITE_RIGHT); + GENERATE(Token::BIT_OR, INT32, INT32, SMI, OVERWRITE_LEFT); + GENERATE(Token::BIT_OR, INT32, SMI, INT32, NO_OVERWRITE); + GENERATE(Token::BIT_OR, INT32, SMI, INT32, OVERWRITE_LEFT); + GENERATE(Token::BIT_OR, INT32, SMI, INT32, OVERWRITE_RIGHT); + GENERATE(Token::BIT_OR, INT32, SMI, SMI, NO_OVERWRITE); + GENERATE(Token::BIT_OR, INT32, SMI, SMI, OVERWRITE_RIGHT); + GENERATE(Token::BIT_OR, NUMBER, SMI, INT32, NO_OVERWRITE); + GENERATE(Token::BIT_OR, NUMBER, SMI, INT32, OVERWRITE_LEFT); + GENERATE(Token::BIT_OR, NUMBER, SMI, INT32, OVERWRITE_RIGHT); + GENERATE(Token::BIT_OR, NUMBER, SMI, SMI, NO_OVERWRITE); + GENERATE(Token::BIT_OR, NUMBER, SMI, SMI, OVERWRITE_LEFT); + GENERATE(Token::BIT_OR, SMI, INT32, INT32, OVERWRITE_LEFT); + GENERATE(Token::BIT_OR, SMI, INT32, INT32, OVERWRITE_RIGHT); + GENERATE(Token::BIT_OR, SMI, INT32, SMI, OVERWRITE_RIGHT); + GENERATE(Token::BIT_OR, SMI, SMI, SMI, OVERWRITE_LEFT); + GENERATE(Token::BIT_OR, SMI, SMI, SMI, OVERWRITE_RIGHT); + GENERATE(Token::BIT_XOR, INT32, INT32, INT32, NO_OVERWRITE); + GENERATE(Token::BIT_XOR, INT32, INT32, INT32, OVERWRITE_LEFT); + GENERATE(Token::BIT_XOR, INT32, INT32, INT32, OVERWRITE_RIGHT); + GENERATE(Token::BIT_XOR, INT32, INT32, SMI, NO_OVERWRITE); + GENERATE(Token::BIT_XOR, INT32, INT32, SMI, OVERWRITE_LEFT); + GENERATE(Token::BIT_XOR, INT32, NUMBER, SMI, NO_OVERWRITE); + GENERATE(Token::BIT_XOR, INT32, SMI, INT32, NO_OVERWRITE); + GENERATE(Token::BIT_XOR, INT32, SMI, INT32, OVERWRITE_LEFT); + GENERATE(Token::BIT_XOR, INT32, SMI, INT32, OVERWRITE_RIGHT); + GENERATE(Token::BIT_XOR, NUMBER, INT32, INT32, NO_OVERWRITE); + GENERATE(Token::BIT_XOR, NUMBER, SMI, INT32, NO_OVERWRITE); + GENERATE(Token::BIT_XOR, NUMBER, SMI, SMI, NO_OVERWRITE); + GENERATE(Token::BIT_XOR, SMI, INT32, INT32, NO_OVERWRITE); + GENERATE(Token::BIT_XOR, SMI, INT32, INT32, OVERWRITE_LEFT); + GENERATE(Token::BIT_XOR, SMI, INT32, SMI, OVERWRITE_LEFT); + GENERATE(Token::BIT_XOR, SMI, SMI, SMI, NO_OVERWRITE); + GENERATE(Token::BIT_XOR, SMI, SMI, SMI, OVERWRITE_LEFT); + GENERATE(Token::BIT_XOR, SMI, SMI, SMI, OVERWRITE_RIGHT); + GENERATE(Token::DIV, INT32, INT32, INT32, NO_OVERWRITE); + GENERATE(Token::DIV, INT32, INT32, NUMBER, NO_OVERWRITE); + GENERATE(Token::DIV, INT32, NUMBER, NUMBER, NO_OVERWRITE); + GENERATE(Token::DIV, INT32, NUMBER, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::DIV, INT32, SMI, INT32, NO_OVERWRITE); + GENERATE(Token::DIV, INT32, SMI, NUMBER, NO_OVERWRITE); + GENERATE(Token::DIV, NUMBER, INT32, NUMBER, NO_OVERWRITE); + GENERATE(Token::DIV, NUMBER, INT32, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::DIV, NUMBER, NUMBER, NUMBER, NO_OVERWRITE); + GENERATE(Token::DIV, NUMBER, NUMBER, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::DIV, NUMBER, NUMBER, NUMBER, OVERWRITE_RIGHT); + GENERATE(Token::DIV, NUMBER, SMI, NUMBER, NO_OVERWRITE); + GENERATE(Token::DIV, NUMBER, SMI, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::DIV, SMI, INT32, INT32, NO_OVERWRITE); + GENERATE(Token::DIV, SMI, INT32, NUMBER, NO_OVERWRITE); + GENERATE(Token::DIV, SMI, INT32, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::DIV, SMI, NUMBER, NUMBER, NO_OVERWRITE); + GENERATE(Token::DIV, SMI, NUMBER, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::DIV, SMI, NUMBER, NUMBER, OVERWRITE_RIGHT); + GENERATE(Token::DIV, SMI, SMI, NUMBER, NO_OVERWRITE); + GENERATE(Token::DIV, SMI, SMI, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::DIV, SMI, SMI, NUMBER, OVERWRITE_RIGHT); + GENERATE(Token::DIV, SMI, SMI, SMI, NO_OVERWRITE); + GENERATE(Token::DIV, SMI, SMI, SMI, OVERWRITE_LEFT); + GENERATE(Token::DIV, SMI, SMI, SMI, OVERWRITE_RIGHT); + GENERATE(Token::MOD, NUMBER, SMI, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::MOD, SMI, SMI, SMI, NO_OVERWRITE); + GENERATE(Token::MOD, SMI, SMI, SMI, OVERWRITE_LEFT); + GENERATE(Token::MUL, INT32, INT32, INT32, NO_OVERWRITE); + GENERATE(Token::MUL, INT32, INT32, NUMBER, NO_OVERWRITE); + GENERATE(Token::MUL, INT32, NUMBER, NUMBER, NO_OVERWRITE); + GENERATE(Token::MUL, INT32, NUMBER, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::MUL, INT32, SMI, INT32, NO_OVERWRITE); + GENERATE(Token::MUL, INT32, SMI, INT32, OVERWRITE_LEFT); + GENERATE(Token::MUL, INT32, SMI, NUMBER, NO_OVERWRITE); + GENERATE(Token::MUL, NUMBER, INT32, NUMBER, NO_OVERWRITE); + GENERATE(Token::MUL, NUMBER, INT32, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::MUL, NUMBER, INT32, NUMBER, OVERWRITE_RIGHT); + GENERATE(Token::MUL, NUMBER, NUMBER, NUMBER, NO_OVERWRITE); + GENERATE(Token::MUL, NUMBER, NUMBER, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::MUL, NUMBER, SMI, NUMBER, NO_OVERWRITE); + GENERATE(Token::MUL, NUMBER, SMI, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::MUL, NUMBER, SMI, NUMBER, OVERWRITE_RIGHT); + GENERATE(Token::MUL, SMI, INT32, INT32, NO_OVERWRITE); + GENERATE(Token::MUL, SMI, INT32, INT32, OVERWRITE_LEFT); + GENERATE(Token::MUL, SMI, INT32, NUMBER, NO_OVERWRITE); + GENERATE(Token::MUL, SMI, NUMBER, NUMBER, NO_OVERWRITE); + GENERATE(Token::MUL, SMI, NUMBER, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::MUL, SMI, NUMBER, NUMBER, OVERWRITE_RIGHT); + GENERATE(Token::MUL, SMI, SMI, INT32, NO_OVERWRITE); + GENERATE(Token::MUL, SMI, SMI, NUMBER, NO_OVERWRITE); + GENERATE(Token::MUL, SMI, SMI, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::MUL, SMI, SMI, SMI, NO_OVERWRITE); + GENERATE(Token::MUL, SMI, SMI, SMI, OVERWRITE_LEFT); + GENERATE(Token::MUL, SMI, SMI, SMI, OVERWRITE_RIGHT); + GENERATE(Token::SAR, INT32, SMI, INT32, OVERWRITE_RIGHT); + GENERATE(Token::SAR, INT32, SMI, SMI, NO_OVERWRITE); + GENERATE(Token::SAR, INT32, SMI, SMI, OVERWRITE_RIGHT); + GENERATE(Token::SAR, NUMBER, SMI, SMI, NO_OVERWRITE); + GENERATE(Token::SAR, NUMBER, SMI, SMI, OVERWRITE_RIGHT); + GENERATE(Token::SAR, SMI, SMI, SMI, OVERWRITE_LEFT); + GENERATE(Token::SAR, SMI, SMI, SMI, OVERWRITE_RIGHT); + GENERATE(Token::SHL, INT32, SMI, INT32, NO_OVERWRITE); + GENERATE(Token::SHL, INT32, SMI, INT32, OVERWRITE_RIGHT); + GENERATE(Token::SHL, INT32, SMI, SMI, NO_OVERWRITE); + GENERATE(Token::SHL, INT32, SMI, SMI, OVERWRITE_RIGHT); + GENERATE(Token::SHL, NUMBER, SMI, SMI, OVERWRITE_RIGHT); + GENERATE(Token::SHL, SMI, SMI, INT32, NO_OVERWRITE); + GENERATE(Token::SHL, SMI, SMI, INT32, OVERWRITE_LEFT); + GENERATE(Token::SHL, SMI, SMI, INT32, OVERWRITE_RIGHT); + GENERATE(Token::SHL, SMI, SMI, SMI, NO_OVERWRITE); + GENERATE(Token::SHL, SMI, SMI, SMI, OVERWRITE_LEFT); + GENERATE(Token::SHL, SMI, SMI, SMI, OVERWRITE_RIGHT); + GENERATE(Token::SHR, INT32, SMI, SMI, NO_OVERWRITE); + GENERATE(Token::SHR, INT32, SMI, SMI, OVERWRITE_LEFT); + GENERATE(Token::SHR, INT32, SMI, SMI, OVERWRITE_RIGHT); + GENERATE(Token::SHR, NUMBER, SMI, SMI, NO_OVERWRITE); + GENERATE(Token::SHR, NUMBER, SMI, SMI, OVERWRITE_LEFT); + GENERATE(Token::SHR, NUMBER, SMI, INT32, OVERWRITE_RIGHT); + GENERATE(Token::SHR, SMI, SMI, SMI, NO_OVERWRITE); + GENERATE(Token::SHR, SMI, SMI, SMI, OVERWRITE_LEFT); + GENERATE(Token::SHR, SMI, SMI, SMI, OVERWRITE_RIGHT); + GENERATE(Token::SUB, INT32, INT32, INT32, NO_OVERWRITE); + GENERATE(Token::SUB, INT32, INT32, INT32, OVERWRITE_LEFT); + GENERATE(Token::SUB, INT32, NUMBER, NUMBER, NO_OVERWRITE); + GENERATE(Token::SUB, INT32, NUMBER, NUMBER, OVERWRITE_RIGHT); + GENERATE(Token::SUB, INT32, SMI, INT32, OVERWRITE_LEFT); + GENERATE(Token::SUB, INT32, SMI, INT32, OVERWRITE_RIGHT); + GENERATE(Token::SUB, NUMBER, INT32, NUMBER, NO_OVERWRITE); + GENERATE(Token::SUB, NUMBER, INT32, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::SUB, NUMBER, NUMBER, NUMBER, NO_OVERWRITE); + GENERATE(Token::SUB, NUMBER, NUMBER, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::SUB, NUMBER, NUMBER, NUMBER, OVERWRITE_RIGHT); + GENERATE(Token::SUB, NUMBER, SMI, NUMBER, NO_OVERWRITE); + GENERATE(Token::SUB, NUMBER, SMI, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::SUB, NUMBER, SMI, NUMBER, OVERWRITE_RIGHT); + GENERATE(Token::SUB, SMI, INT32, INT32, NO_OVERWRITE); + GENERATE(Token::SUB, SMI, NUMBER, NUMBER, NO_OVERWRITE); + GENERATE(Token::SUB, SMI, NUMBER, NUMBER, OVERWRITE_LEFT); + GENERATE(Token::SUB, SMI, NUMBER, NUMBER, OVERWRITE_RIGHT); + GENERATE(Token::SUB, SMI, SMI, SMI, NO_OVERWRITE); + GENERATE(Token::SUB, SMI, SMI, SMI, OVERWRITE_LEFT); + GENERATE(Token::SUB, SMI, SMI, SMI, OVERWRITE_RIGHT); +#undef GENERATE +#define GENERATE(op, left_kind, fixed_right_arg_value, result_kind, mode) \ + do { \ + State state(op, mode); \ + state.left_kind_ = left_kind; \ + state.fixed_right_arg_.has_value = true; \ + state.fixed_right_arg_.value = fixed_right_arg_value; \ + state.right_kind_ = SMI; \ + state.result_kind_ = result_kind; \ + Generate(isolate, state); \ + } while (false) + GENERATE(Token::MOD, SMI, 2, SMI, NO_OVERWRITE); + GENERATE(Token::MOD, SMI, 4, SMI, NO_OVERWRITE); + GENERATE(Token::MOD, SMI, 4, SMI, OVERWRITE_LEFT); + GENERATE(Token::MOD, SMI, 8, SMI, NO_OVERWRITE); + GENERATE(Token::MOD, SMI, 16, SMI, OVERWRITE_LEFT); + GENERATE(Token::MOD, SMI, 32, SMI, NO_OVERWRITE); + GENERATE(Token::MOD, SMI, 2048, SMI, NO_OVERWRITE); +#undef GENERATE +} + + +Handle BinaryOpIC::State::GetResultType(Isolate* isolate) const { + Kind result_kind = result_kind_; + if (HasSideEffects()) { + result_kind = NONE; + } else if (result_kind == GENERIC && op_ == Token::ADD) { + return handle(Type::Union(handle(Type::Number(), isolate), + handle(Type::String(), isolate)), isolate); + } else if (result_kind == NUMBER && op_ == Token::SHR) { + return handle(Type::Unsigned32(), isolate); + } + ASSERT_NE(GENERIC, result_kind); + return KindToType(result_kind, isolate); +} + + +void BinaryOpIC::State::Print(StringStream* stream) const { + stream->Add("(%s", Token::Name(op_)); + if (mode_ == OVERWRITE_LEFT) stream->Add("_ReuseLeft"); + else if (mode_ == OVERWRITE_RIGHT) stream->Add("_ReuseRight"); + stream->Add(":%s*", KindToString(left_kind_)); + if (fixed_right_arg_.has_value) { + stream->Add("%d", fixed_right_arg_.value); + } else { + stream->Add("%s", KindToString(right_kind_)); + } + stream->Add("->%s)", KindToString(result_kind_)); +} + + +void BinaryOpIC::State::Update(Handle left, + Handle right, + Handle result) { + ExtraICState old_extra_ic_state = GetExtraICState(); + + left_kind_ = UpdateKind(left, left_kind_); + right_kind_ = UpdateKind(right, right_kind_); + + int32_t fixed_right_arg_value; + bool has_fixed_right_arg = + op_ == Token::MOD && + right->ToInt32(&fixed_right_arg_value) && + fixed_right_arg_value > 0 && + IsPowerOf2(fixed_right_arg_value) && + FixedRightArgValueField::is_valid(fixed_right_arg_value) && + (left_kind_ == SMI || left_kind_ == INT32) && + (result_kind_ == NONE || !fixed_right_arg_.has_value); + fixed_right_arg_ = Maybe(has_fixed_right_arg, + fixed_right_arg_value); + + result_kind_ = UpdateKind(result, result_kind_); + + if (!Token::IsTruncatingBinaryOp(op_)) { + Kind input_kind = Max(left_kind_, right_kind_); + if (result_kind_ < input_kind && input_kind <= NUMBER) { + result_kind_ = input_kind; + } + } + + // Reset overwrite mode unless we can actually make use of it, or may be able + // to make use of it at some point in the future. + if ((mode_ == OVERWRITE_LEFT && left_kind_ > NUMBER) || + (mode_ == OVERWRITE_RIGHT && right_kind_ > NUMBER) || + result_kind_ > NUMBER) { + mode_ = NO_OVERWRITE; + } + + if (old_extra_ic_state == GetExtraICState()) { + // Tagged operations can lead to non-truncating HChanges + if (left->IsUndefined() || left->IsBoolean()) { + left_kind_ = GENERIC; + } else if (right->IsUndefined() || right->IsBoolean()) { + right_kind_ = GENERIC; + } else { + // Since the X87 is too precise, we might bail out on numbers which + // actually would truncate with 64 bit precision. + ASSERT(!CpuFeatures::IsSupported(SSE2)); + ASSERT(result_kind_ < NUMBER); + result_kind_ = NUMBER; + } + } +} + + +BinaryOpIC::State::Kind BinaryOpIC::State::UpdateKind(Handle object, + Kind kind) const { + Kind new_kind = GENERIC; + bool is_truncating = Token::IsTruncatingBinaryOp(op()); + if (object->IsBoolean() && is_truncating) { + // Booleans will be automatically truncated by HChange. + new_kind = INT32; + } else if (object->IsUndefined()) { + // Undefined will be automatically truncated by HChange. + new_kind = is_truncating ? INT32 : NUMBER; + } else if (object->IsSmi()) { + new_kind = SMI; + } else if (object->IsHeapNumber()) { + double value = Handle::cast(object)->value(); + new_kind = TypeInfo::IsInt32Double(value) ? INT32 : NUMBER; + } else if (object->IsString() && op() == Token::ADD) { + new_kind = STRING; + } + if (new_kind == INT32 && SmiValuesAre32Bits()) { + new_kind = NUMBER; + } + if (kind != NONE && + ((new_kind <= NUMBER && kind > NUMBER) || + (new_kind > NUMBER && kind <= NUMBER))) { + new_kind = GENERIC; + } + return Max(kind, new_kind); +} + + +// static +const char* BinaryOpIC::State::KindToString(Kind kind) { + switch (kind) { + case NONE: return "None"; case SMI: return "Smi"; case INT32: return "Int32"; case NUMBER: return "Number"; - case ODDBALL: return "Oddball"; case STRING: return "String"; case GENERIC: return "Generic"; - default: return "Invalid"; } + UNREACHABLE(); + return NULL; +} + + +// static +Handle BinaryOpIC::State::KindToType(Kind kind, Isolate* isolate) { + Type* type = NULL; + switch (kind) { + case NONE: type = Type::None(); break; + case SMI: type = Type::Smi(); break; + case INT32: type = Type::Signed32(); break; + case NUMBER: type = Type::Number(); break; + case STRING: type = Type::String(); break; + case GENERIC: type = Type::Any(); break; + } + return handle(type, isolate); } MaybeObject* BinaryOpIC::Transition(Handle left, Handle right) { - ExtraICState extra_ic_state = target()->extended_extra_ic_state(); - BinaryOpStub stub(extra_ic_state); + State state(target()->extended_extra_ic_state()); - Handle left_type = stub.GetLeftType(isolate()); - Handle right_type = stub.GetRightType(isolate()); - bool smi_was_enabled = left_type->Maybe(Type::Smi()) && - right_type->Maybe(Type::Smi()); + // Compute the actual result using the builtin for the binary operation. + Object* builtin = isolate()->js_builtins_object()->javascript_builtin( + TokenToJSBuiltin(state.op())); + Handle function = handle(JSFunction::cast(builtin), isolate()); + bool caught_exception; + Handle result = Execution::Call( + isolate(), function, left, 1, &right, &caught_exception); + if (caught_exception) return Failure::Exception(); - Maybe > result = stub.Result(left, right, isolate()); - if (!result.has_value) return Failure::Exception(); + // Compute the new state. + State old_state = state; + state.Update(left, right, result); + + // Install the new stub. + BinaryOpICStub stub(state); + set_target(*stub.GetCode(isolate())); if (FLAG_trace_ic) { - char buffer[100]; - NoAllocationStringAllocator allocator(buffer, - static_cast(sizeof(buffer))); + char buffer[150]; + NoAllocationStringAllocator allocator( + buffer, static_cast(sizeof(buffer))); StringStream stream(&allocator); - stream.Add("["); - stub.PrintName(&stream); - - stub.UpdateStatus(left, right, result); - + stream.Add("[BinaryOpIC"); + old_state.Print(&stream); stream.Add(" => "); - stub.PrintState(&stream); - stream.Add(" "); + state.Print(&stream); + stream.Add(" @ %p <- ", static_cast(*target())); stream.OutputToStdOut(); - PrintF(" @ %p <- ", static_cast(*stub.GetCode(isolate()))); JavaScriptFrame::PrintTop(isolate(), stdout, false, true); PrintF("]\n"); - } else { - stub.UpdateStatus(left, right, result); } - Handle code = stub.GetCode(isolate()); - set_target(*code); - - left_type = stub.GetLeftType(isolate()); - right_type = stub.GetRightType(isolate()); - bool enable_smi = left_type->Maybe(Type::Smi()) && - right_type->Maybe(Type::Smi()); - - if (!smi_was_enabled && enable_smi) { + // Patch the inlined smi code as necessary. + if (!old_state.UseInlinedSmiCode() && state.UseInlinedSmiCode()) { PatchInlinedSmiCode(address(), ENABLE_INLINED_SMI_CHECK); - } else if (smi_was_enabled && !enable_smi) { + } else if (old_state.UseInlinedSmiCode() && !state.UseInlinedSmiCode()) { PatchInlinedSmiCode(address(), DISABLE_INLINED_SMI_CHECK); } - ASSERT(result.has_value); - return static_cast(*result.value); + return *result; } RUNTIME_FUNCTION(MaybeObject*, BinaryOpIC_Miss) { HandleScope scope(isolate); - Handle left = args.at(0); - Handle right = args.at(1); + Handle left = args.at(BinaryOpICStub::kLeft); + Handle right = args.at(BinaryOpICStub::kRight); BinaryOpIC ic(isolate); return ic.Transition(left, right); } diff --git a/src/ic.h b/src/ic.h index b1a47e2..e018d40 100644 --- a/src/ic.h +++ b/src/ic.h @@ -810,25 +810,114 @@ class KeyedStoreIC: public StoreIC { }; +// Mode to overwrite BinaryExpression values. +enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT }; + // Type Recording BinaryOpIC, that records the types of the inputs and outputs. class BinaryOpIC: public IC { public: - enum TypeInfo { - UNINITIALIZED, - SMI, - INT32, - NUMBER, - ODDBALL, - STRING, // Only used for addition operation. - GENERIC + class State V8_FINAL BASE_EMBEDDED { + public: + explicit State(ExtraICState extra_ic_state); + + State(Token::Value op, OverwriteMode mode) + : op_(op), mode_(mode), left_kind_(NONE), right_kind_(NONE), + result_kind_(NONE) { + ASSERT_LE(FIRST_TOKEN, op); + ASSERT_LE(op, LAST_TOKEN); + } + + InlineCacheState GetICState() const { + if (Max(left_kind_, right_kind_) == NONE) { + return ::v8::internal::UNINITIALIZED; + } + if (Max(left_kind_, right_kind_) == GENERIC) { + return ::v8::internal::MEGAMORPHIC; + } + if (Min(left_kind_, right_kind_) == GENERIC) { + return ::v8::internal::GENERIC; + } + return ::v8::internal::MONOMORPHIC; + } + + ExtraICState GetExtraICState() const; + + static void GenerateAheadOfTime( + Isolate*, void (*Generate)(Isolate*, const State&)); + + bool CanReuseDoubleBox() const { + return (result_kind_ > SMI && result_kind_ <= NUMBER) && + ((mode_ == OVERWRITE_LEFT && + left_kind_ > SMI && left_kind_ <= NUMBER) || + (mode_ == OVERWRITE_RIGHT && + right_kind_ > SMI && right_kind_ <= NUMBER)); + } + + bool HasSideEffects() const { + return Max(left_kind_, right_kind_) == GENERIC; + } + + bool UseInlinedSmiCode() const { + return KindMaybeSmi(left_kind_) || KindMaybeSmi(right_kind_); + } + + static const int FIRST_TOKEN = Token::BIT_OR; + static const int LAST_TOKEN = Token::MOD; + + Token::Value op() const { return op_; } + OverwriteMode mode() const { return mode_; } + Maybe fixed_right_arg() const { return fixed_right_arg_; } + + Handle GetLeftType(Isolate* isolate) const { + return KindToType(left_kind_, isolate); + } + Handle GetRightType(Isolate* isolate) const { + return KindToType(right_kind_, isolate); + } + Handle GetResultType(Isolate* isolate) const; + + void Print(StringStream* stream) const; + + void Update(Handle left, + Handle right, + Handle result); + + private: + enum Kind { NONE, SMI, INT32, NUMBER, STRING, GENERIC }; + + Kind UpdateKind(Handle object, Kind kind) const; + + static const char* KindToString(Kind kind); + static Handle KindToType(Kind kind, Isolate* isolate); + static bool KindMaybeSmi(Kind kind) { + return (kind >= SMI && kind <= NUMBER) || kind == GENERIC; + } + + // We truncate the last bit of the token. + STATIC_ASSERT(LAST_TOKEN - FIRST_TOKEN < (1 << 4)); + class OpField: public BitField {}; + class OverwriteModeField: public BitField {}; + class SSE2Field: public BitField {}; + class ResultKindField: public BitField {}; + class LeftKindField: public BitField {}; + // When fixed right arg is set, we don't need to store the right kind. + // Thus the two fields can overlap. + class HasFixedRightArgField: public BitField {}; + class FixedRightArgValueField: public BitField {}; + class RightKindField: public BitField {}; + + Token::Value op_; + OverwriteMode mode_; + Kind left_kind_; + Kind right_kind_; + Kind result_kind_; + Maybe fixed_right_arg_; }; explicit BinaryOpIC(Isolate* isolate) : IC(EXTRA_CALL_FRAME, isolate) { } static Builtins::JavaScript TokenToJSBuiltin(Token::Value op); - static const char* GetName(TypeInfo type_info); - MUST_USE_RESULT MaybeObject* Transition(Handle left, Handle right); }; diff --git a/src/isolate.cc b/src/isolate.cc index 6495d25..25bc546 100644 --- a/src/isolate.cc +++ b/src/isolate.cc @@ -2104,7 +2104,7 @@ bool Isolate::Init(Deserializer* des) { DONT_TRACK_ALLOCATION_SITE, 0); stub.InitializeInterfaceDescriptor( this, code_stub_interface_descriptor(CodeStub::FastCloneShallowArray)); - BinaryOpStub::InitializeForIsolate(this); + BinaryOpICStub::InstallDescriptors(this); CompareNilICStub::InitializeForIsolate(this); ToBooleanStub::InitializeForIsolate(this); ArrayConstructorStubBase::InstallDescriptors(this); diff --git a/src/log.cc b/src/log.cc index e123231..a508e87 100644 --- a/src/log.cc +++ b/src/log.cc @@ -1838,12 +1838,7 @@ void Logger::LogCodeObject(Object* object) { case Code::FUNCTION: case Code::OPTIMIZED_FUNCTION: return; // We log this later using LogCompiledFunctions. - case Code::BINARY_OP_IC: { - BinaryOpStub stub(code_object->extended_extra_ic_state()); - description = stub.GetName().Detach(); - tag = Logger::STUB_TAG; - break; - } + case Code::BINARY_OP_IC: case Code::COMPARE_IC: // fall through case Code::COMPARE_NIL_IC: // fall through case Code::TO_BOOLEAN_IC: // fall through diff --git a/src/token.h b/src/token.h index 992adaa..39bcc24 100644 --- a/src/token.h +++ b/src/token.h @@ -213,6 +213,10 @@ class Token { return COMMA <= op && op <= MOD; } + static bool IsTruncatingBinaryOp(Value op) { + return BIT_OR <= op && op <= ROR; + } + static bool IsCompareOp(Value op) { return EQ <= op && op <= IN; } diff --git a/src/type-info.cc b/src/type-info.cc index e4eb7db..3cb5655 100644 --- a/src/type-info.cc +++ b/src/type-info.cc @@ -410,28 +410,26 @@ void TypeFeedbackOracle::BinaryType(TypeFeedbackId id, Handle* right, Handle* result, Maybe* fixed_right_arg, - Token::Value operation) { + Token::Value op) { Handle object = GetInfo(id); if (!object->IsCode()) { // For some binary ops we don't have ICs, e.g. Token::COMMA, but for the - // operations covered by the BinaryOpStub we should always have them. - ASSERT(!(operation >= BinaryOpStub::FIRST_TOKEN && - operation <= BinaryOpStub::LAST_TOKEN)); + // operations covered by the BinaryOpIC we should always have them. + ASSERT(op < BinaryOpIC::State::FIRST_TOKEN || + op > BinaryOpIC::State::LAST_TOKEN); *left = *right = *result = handle(Type::None(), isolate_); + *fixed_right_arg = Maybe(); return; } Handle code = Handle::cast(object); - ASSERT(code->is_binary_op_stub()); + ASSERT_EQ(Code::BINARY_OP_IC, code->kind()); + BinaryOpIC::State state(code->extended_extra_ic_state()); + ASSERT_EQ(op, state.op()); - BinaryOpStub stub(code->extended_extra_ic_state()); - - // Sanity check. - ASSERT(stub.operation() == operation); - - *left = stub.GetLeftType(isolate()); - *right = stub.GetRightType(isolate()); - *result = stub.GetResultType(isolate()); - *fixed_right_arg = stub.fixed_right_arg(); + *left = state.GetLeftType(isolate()); + *right = state.GetRightType(isolate()); + *result = state.GetResultType(isolate()); + *fixed_right_arg = state.fixed_right_arg(); } @@ -449,13 +447,11 @@ Handle TypeFeedbackOracle::ClauseType(TypeFeedbackId id) { Handle TypeFeedbackOracle::CountType(TypeFeedbackId id) { Handle object = GetInfo(id); - Handle unknown(Type::None(), isolate_); - if (!object->IsCode()) return unknown; + if (!object->IsCode()) return handle(Type::None(), isolate_); Handle code = Handle::cast(object); - if (!code->is_binary_op_stub()) return unknown; - - BinaryOpStub stub(code->extended_extra_ic_state()); - return stub.GetLeftType(isolate()); + ASSERT_EQ(Code::BINARY_OP_IC, code->kind()); + BinaryOpIC::State state(code->extended_extra_ic_state()); + return state.GetLeftType(isolate()); } diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index 6f35a21..0c9a0f2 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -180,7 +180,7 @@ void TransitionElementsKindStub::InitializeInterfaceDescriptor( } -void BinaryOpStub::InitializeInterfaceDescriptor( +void BinaryOpICStub::InitializeInterfaceDescriptor( Isolate* isolate, CodeStubInterfaceDescriptor* descriptor) { static Register registers[] = { rdx, rax }; @@ -2788,7 +2788,7 @@ void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) { // It is important that the store buffer overflow stubs are generated first. ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate); CreateAllocationSiteStub::GenerateAheadOfTime(isolate); - BinaryOpStub::GenerateAheadOfTime(isolate); + BinaryOpICStub::GenerateAheadOfTime(isolate); } diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index 8a3f965..e479368 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -2268,7 +2268,7 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr, __ bind(&stub_call); __ movq(rax, rcx); - BinaryOpStub stub(op, mode); + BinaryOpICStub stub(op, mode); CallIC(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, expr->BinaryOperationFeedbackId()); patch_site.EmitPatchInfo(); @@ -2317,7 +2317,7 @@ void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr, Token::Value op, OverwriteMode mode) { __ pop(rdx); - BinaryOpStub stub(op, mode); + BinaryOpICStub stub(op, mode); JumpPatchSite patch_site(masm_); // unbound, signals no inlined smi code. CallIC(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, expr->BinaryOperationFeedbackId()); @@ -4405,7 +4405,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { __ bind(&stub_call); __ movq(rdx, rax); __ Move(rax, Smi::FromInt(1)); - BinaryOpStub stub(expr->binary_op(), NO_OVERWRITE); + BinaryOpICStub stub(expr->binary_op(), NO_OVERWRITE); CallIC(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, expr->CountBinOpFeedbackId()); diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index b196254..9d0dc94 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -1951,7 +1951,7 @@ void LCodeGen::DoArithmeticT(LArithmeticT* instr) { ASSERT(ToRegister(instr->right()).is(rax)); ASSERT(ToRegister(instr->result()).is(rax)); - BinaryOpStub stub(instr->op(), NO_OVERWRITE); + BinaryOpICStub stub(instr->op(), NO_OVERWRITE); CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); __ nop(); // Signals no inlined code. } -- 2.7.4