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:
parent
aa83f2900a
commit
9b892b86b1
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
184
src/code-stubs.h
184
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<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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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
478
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";
|
||||
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
109
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<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);
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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.
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user