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
This commit is contained in:
bmeurer@chromium.org 2013-12-02 13:14:07 +00:00
parent aa83f2900a
commit 9b892b86b1
18 changed files with 653 additions and 745 deletions

View File

@ -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);
}

View File

@ -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());

View File

@ -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());

View File

@ -895,20 +895,21 @@ Handle<Code> CompareNilICStub::GenerateCode(Isolate* isolate) {
template <>
HValue* CodeStubGraphBuilder<BinaryOpStub>::BuildCodeInitializedStub() {
BinaryOpStub* stub = casted_stub();
HValue* left = GetParameter(0);
HValue* right = GetParameter(1);
HValue* CodeStubGraphBuilder<BinaryOpICStub>::BuildCodeInitializedStub() {
BinaryOpIC::State state = casted_stub()->state();
Handle<Type> left_type = stub->GetLeftType(isolate());
Handle<Type> right_type = stub->GetRightType(isolate());
Handle<Type> result_type = stub->GetResultType(isolate());
HValue* left = GetParameter(BinaryOpICStub::kLeft);
HValue* right = GetParameter(BinaryOpICStub::kRight);
Handle<Type> left_type = state.GetLeftType(isolate());
Handle<Type> right_type = state.GetRightType(isolate());
Handle<Type> 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<BinaryOpStub>::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<BinaryOpStub>::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<BinaryOpStub>::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<HIsSmiAndBranch>(operand);
if_heap_number.Then();
@ -998,7 +999,7 @@ HValue* CodeStubGraphBuilder<BinaryOpStub>::BuildCodeInitializedStub() {
}
Handle<Code> BinaryOpStub::GenerateCode(Isolate* isolate) {
Handle<Code> BinaryOpICStub::GenerateCode(Isolate* isolate) {
return DoGenerateCode(isolate, this);
}

View File

@ -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<Handle<Object> > BinaryOpStub::Result(Handle<Object> left,
Handle<Object> right,
Isolate* isolate) {
Handle<JSBuiltinsObject> builtins(isolate->js_builtins_object());
Builtins::JavaScript func = BinaryOpIC::TokenToJSBuiltin(op_);
Object* builtin = builtins->javascript_builtin(func);
Handle<JSFunction> builtin_function =
Handle<JSFunction>(JSFunction::cast(builtin), isolate);
bool caught_exception;
Handle<Object> result = Execution::Call(isolate, builtin_function, left,
1, &right, &caught_exception);
return Maybe<Handle<Object> >(!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<Token::Value>(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<Object> left,
Handle<Object> right,
Maybe<Handle<Object> > 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<int32_t>(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<Token::Value>(op),
static_cast<OverwriteMode>(mode));
stub.GetCode(isolate);
}
}
// Generate special versions of the stub.
BinaryOpIC::State::GenerateAheadOfTime(isolate, &GenerateAheadOfTime);
}
void BinaryOpStub::UpdateStatus(Handle<Object> 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);
void BinaryOpICStub::PrintState(StringStream* stream) {
state_.Print(stream);
}
Handle<Type> BinaryOpStub::StateToType(State state,
Isolate* isolate) {
Handle<Type> 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;
}
Handle<Type> BinaryOpStub::GetLeftType(Isolate* isolate) const {
return StateToType(left_state_, isolate);
}
Handle<Type> BinaryOpStub::GetRightType(Isolate* isolate) const {
return StateToType(right_state_, isolate);
}
Handle<Type> 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);
}
@ -1165,6 +718,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);

View File

@ -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<int>(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;
static void InstallDescriptors(Isolate* isolate);
virtual Code::Kind GetCodeKind() const V8_OVERRIDE {
return Code::BINARY_OP_IC;
}
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;
virtual InlineCacheState GetICState() V8_OVERRIDE {
return state_.GetICState();
}
virtual ExtraICState GetExtraICState() V8_OVERRIDE {
return state_.GetExtraICState();
}
virtual void VerifyPlatformFeatures(Isolate* isolate) V8_OVERRIDE {
ASSERT(CpuFeatures::VerifyCrossCompiling(SSE2));
}
virtual ExtraICState GetExtraICState() {
bool sse_field = Max(result_state_, Max(left_state_, right_state_)) > SMI &&
CpuFeatures::IsSafeForSnapshot(SSE2);
virtual Handle<Code> GenerateCode(Isolate* isolate) V8_OVERRIDE;
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_);
}
const BinaryOpIC::State& state() const { return state_; }
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 void PrintState(StringStream* stream) V8_OVERRIDE;
bool HasSideEffects(Isolate* isolate) const {
Handle<Type> left = GetLeftType(isolate);
Handle<Type> right = GetRightType(isolate);
return left->Maybe(Type::Receiver()) || right->Maybe(Type::Receiver());
}
virtual Handle<Code> GenerateCode(Isolate* isolate);
Maybe<Handle<Object> > Result(Handle<Object> left,
Handle<Object> right,
Isolate* isolate);
Token::Value operation() const { return op_; }
OverwriteMode mode() const { return mode_; }
Maybe<int> fixed_right_arg() const { return fixed_right_arg_; }
Handle<Type> GetLeftType(Isolate* isolate) const;
Handle<Type> GetRightType(Isolate* isolate) const;
Handle<Type> GetResultType(Isolate* isolate) const;
void UpdateStatus(Handle<Object> left,
Handle<Object> right,
Maybe<Handle<Object> > result);
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();
static void GenerateAheadOfTime(Isolate* isolate,
const BinaryOpIC::State& state);
enum State { NONE, SMI, INT32, NUMBER, STRING, GENERIC };
virtual Major MajorKey() V8_OVERRIDE { return BinaryOpIC; }
virtual int NotMissMinorKey() V8_OVERRIDE { return GetExtraICState(); }
// We truncate the last bit of the token.
STATIC_ASSERT(LAST_TOKEN - FIRST_TOKEN < (1 << 5));
class LeftStateField: public BitField<State, 0, 3> {};
// 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<bool, 4, 1> {};
class FixedRightArgValueBits: public BitField<int, 5, 4> {};
class RightStateField: public BitField<State, 5, 3> {};
class ResultStateField: public BitField<State, 9, 3> {};
class SSE2Field: public BitField<bool, 12, 1> {};
class OverwriteModeField: public BitField<OverwriteMode, 13, 2> {};
class OpBits: public BitField<int, 15, 5> {};
BinaryOpIC::State state_;
virtual CodeStub::Major MajorKey() { return BinaryOp; }
virtual int NotMissMinorKey() { return GetExtraICState(); }
static Handle<Type> 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);
void UpdateStatus(Handle<Object> object,
State* state);
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_;
Maybe<int> fixed_right_arg_;
State left_state_;
State right_state_;
State result_state_;
DISALLOW_COPY_AND_ASSIGN(BinaryOpICStub);
};

View File

@ -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);
}
}

View File

@ -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());

View File

@ -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.
}

478
src/ic.cc
View File

@ -2317,76 +2317,464 @@ RUNTIME_FUNCTION(MaybeObject*, ElementsTransitionAndStoreIC_Miss) {
}
const char* BinaryOpIC::GetName(TypeInfo type_info) {
switch (type_info) {
case UNINITIALIZED: return "Uninitialized";
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";
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<Token::Value>(
FIRST_TOKEN + OpField::decode(extra_ic_state));
mode_ = OverwriteModeField::decode(extra_ic_state);
fixed_right_arg_ = Maybe<int>(
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<Type> 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<Object> left,
Handle<Object> right,
Handle<Object> 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<int32_t>(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> 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<HeapNumber>::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 STRING: return "String";
case GENERIC: return "Generic";
}
UNREACHABLE();
return NULL;
}
// static
Handle<Type> 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<Object> left, Handle<Object> right) {
ExtraICState extra_ic_state = target()->extended_extra_ic_state();
BinaryOpStub stub(extra_ic_state);
State state(target()->extended_extra_ic_state());
Handle<Type> left_type = stub.GetLeftType(isolate());
Handle<Type> 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<JSFunction> function = handle(JSFunction::cast(builtin), isolate());
bool caught_exception;
Handle<Object> result = Execution::Call(
isolate(), function, left, 1, &right, &caught_exception);
if (caught_exception) return Failure::Exception();
Maybe<Handle<Object> > 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<unsigned>(sizeof(buffer)));
char buffer[150];
NoAllocationStringAllocator allocator(
buffer, static_cast<unsigned>(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<void*>(*target()));
stream.OutputToStdOut();
PrintF(" @ %p <- ", static_cast<void*>(*stub.GetCode(isolate())));
JavaScriptFrame::PrintTop(isolate(), stdout, false, true);
PrintF("]\n");
} else {
stub.UpdateStatus(left, right, result);
}
Handle<Code> 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<MaybeObject*>(*result.value);
return *result;
}
RUNTIME_FUNCTION(MaybeObject*, BinaryOpIC_Miss) {
HandleScope scope(isolate);
Handle<Object> left = args.at<Object>(0);
Handle<Object> right = args.at<Object>(1);
Handle<Object> left = args.at<Object>(BinaryOpICStub::kLeft);
Handle<Object> right = args.at<Object>(BinaryOpICStub::kRight);
BinaryOpIC ic(isolate);
return ic.Transition(left, right);
}

109
src/ic.h
View File

@ -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<int> fixed_right_arg() const { return fixed_right_arg_; }
Handle<Type> GetLeftType(Isolate* isolate) const {
return KindToType(left_kind_, isolate);
}
Handle<Type> GetRightType(Isolate* isolate) const {
return KindToType(right_kind_, isolate);
}
Handle<Type> GetResultType(Isolate* isolate) const;
void Print(StringStream* stream) const;
void Update(Handle<Object> left,
Handle<Object> right,
Handle<Object> result);
private:
enum Kind { NONE, SMI, INT32, NUMBER, STRING, GENERIC };
Kind UpdateKind(Handle<Object> object, Kind kind) const;
static const char* KindToString(Kind kind);
static Handle<Type> 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<int, 0, 4> {};
class OverwriteModeField: public BitField<OverwriteMode, 4, 2> {};
class SSE2Field: public BitField<bool, 6, 1> {};
class ResultKindField: public BitField<Kind, 7, 3> {};
class LeftKindField: public BitField<Kind, 10, 3> {};
// 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<bool, 13, 1> {};
class FixedRightArgValueField: public BitField<int, 14, 4> {};
class RightKindField: public BitField<Kind, 14, 3> {};
Token::Value op_;
OverwriteMode mode_;
Kind left_kind_;
Kind right_kind_;
Kind result_kind_;
Maybe<int> 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<Object> left,
Handle<Object> right);
};

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -410,28 +410,26 @@ void TypeFeedbackOracle::BinaryType(TypeFeedbackId id,
Handle<Type>* right,
Handle<Type>* result,
Maybe<int>* fixed_right_arg,
Token::Value operation) {
Token::Value op) {
Handle<Object> 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<int>();
return;
}
Handle<Code> code = Handle<Code>::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<Type> TypeFeedbackOracle::ClauseType(TypeFeedbackId id) {
Handle<Type> TypeFeedbackOracle::CountType(TypeFeedbackId id) {
Handle<Object> object = GetInfo(id);
Handle<Type> unknown(Type::None(), isolate_);
if (!object->IsCode()) return unknown;
if (!object->IsCode()) return handle(Type::None(), isolate_);
Handle<Code> code = Handle<Code>::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());
}

View File

@ -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);
}

View File

@ -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());

View File

@ -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.
}